10 added, 10 removed. Audit A to A.
---
name: useclassy
description: >-
Write and refactor Vue, React, Svelte, Blade, and HTML with vite-plugin-useclassy
(UseClassy) modifier attributes: class:hover, class:focus, class:sm, class:md,
class:dark, className:hover, className:focus, class:sm:hover, and similar.
Use when authoring UI, writing Tailwind or UnoCSS hover/focus/responsive/dark
variants, converting hover: focus: sm: md: prefixes into UseClassy attributes,
refactoring className variant tokens, or reviewing and fixing UseClassy markup.
---
# Authoring with UseClassy
Use UseClassy to separate Tailwind variants from base utilities:
```html
<!-- Before -->
<button class="rounded px-4 hover:bg-blue-500 hover:text-white focus:ring-2">
<!-- After -->
<button
class="rounded px-4"
class:hover="bg-blue-500 text-white"
class:focus="ring-2"
>
```
## Syntax
- | Language | Base | Modifiers |
- |----------|------|-----------|
- | Vue / Blade | `class="…"` | `class:hover="…"`, `class:sm:hover="…"` |
- | React | `className="…"` | `className:hover="…"` (also accepts `class:…`); JSX expressions allowed |
- | Svelte | `class="…"` | Quoted only: `class:hover="…"` |
+ | Language | Base | Modifiers |
+ | ----------- | --------------- | ----------------------------------------------------------------------- |
+ | Vue / Blade | `class="…"` | `class:hover="…"`, `class:sm:hover="…"` |
+ | React | `className="…"` | `className:hover="…"`, `className:@md="…"`, `className:group-hover/item="…"`, `className:[&>*]="…"`, `className:data-[state=open]="…"`; JSX expressions allowed |
+ | Svelte | `class="…"` | Quoted only: `class:hover="…"` |
- Modifier names may contain letters, numbers, `_`, `-`, `:`, `/` (named groups such as `group-hover/item`), and `@` (container queries such as `@md`). Arbitrary variants (`[&>*]`, `data-[state=open]`) cannot be attribute names — leave those tokens on the base class. In React JSX, `/` in an attribute name is invalid, so named groups must stay on `className`.
+ Modifier names may contain letters, numbers, `_`, `-`, `:`, `/` (named groups such as `group-hover/item`), `@` (container queries such as `@md`), and arbitrary variants with `[…]` (`[&>*]`, `data-[state=open]`). UseClassy parses modifier names with bracket depth so `=` inside `[…]` is not the attribute separator. React uses the same modifier attributes as Vue; UseClassy rewrites them before JSX/HTML parse.
- - **Vue / Blade / Svelte / HTML:** modifier values must be double-quoted static class strings.
- - **React:** prefer double-quoted static strings. JSX expressions are also supported when string literals inside the expression should receive the variant prefix, e.g. `className:hover={on ? 'bg-blue-500' : 'bg-gray-200'}`.
+ - **Vue / Blade / Svelte / HTML:** modifier values must be quoted static class strings (`"` or `'`).
+ - **React:** prefer quoted static strings (`"` or `'`). JSX expressions are also supported when string literals inside the expression should receive the variant prefix, e.g. `className:hover={on ? 'bg-blue-500' : 'bg-gray-200'}`.
## Refactor existing code
When asked to convert markup to UseClassy:
1. Identify the framework from the file/config; use `className:` for React and `class:` elsewhere.
2. Inspect each **static** `class` / `className` string.
3. Keep unprefixed utilities in the base attribute.
4. Group utilities that share the same modifier prefix and remove that prefix:
- `hover:bg-blue-500 hover:text-white` → `class:hover="bg-blue-500 text-white"`
- `focus:ring-2 focus:ring-blue-400` → `class:focus="ring-2 ring-blue-400"`
- `md:px-6 md:py-3` → `class:md="px-6 py-3"`
- `sm:hover:underline` → `class:sm:hover="underline"`
5. Preserve utility order within each group and preserve unrelated attributes/expressions.
Convert only static tokens that can be represented safely. Do not rewrite:
- Dynamic expressions, template interpolations, conditional class helpers, Vue `:class`, or Svelte directives — unless you are intentionally using React's `className:mod={…}` expression form with string literals.
- - Arbitrary variant prefixes such as `[&>*]:mt-2` or `data-[state=open]:block`; their characters are not valid in a UseClassy modifier name.
- Variant tokens embedded in variables or function calls (leave those variables unchanged, or store already-prefixed class names).
## Chained modifiers
`class:sm:hover="underline"` produces `sm:hover:underline` only — the same composition as Tailwind and UnoCSS. Converting `sm:hover:underline` on a base class to `class:sm:hover="underline"` is behavior-preserving.
```html
class:sm:hover="underline"
<!-- produces sm:hover:underline -->
```
## Framework rules
- Put base utilities on `class` / `className`.
- Vue / Blade / HTML: use `class:modifier="…"`.
- - React: prefer `className:modifier="…"` for static variants. For runtime conditions that still use string literals, `className:modifier={cond ? 'a' : 'b'}` is valid and will prefix those literals. Leave `className={…}` base expressions unchanged when they are unrelated.
+ - React: prefer `className:modifier="…"` for static variants, including `className:@md`, `className:group-hover/item`, `className:[&>*]`, and `className:data-[state=open]` (UseClassy rewrites before JSX parse). For runtime conditions that still use string literals, `className:modifier={cond ? 'a' : 'b'}` is valid and will prefix those literals. Leave `className={…}` base expressions unchanged when they are unrelated.
+ - React editor setup: add `compilerOptions.plugins: [{ "name": "vite-plugin-useclassy/typescript-plugin" }]`, extend `tsconfig.app.json` from `tsconfig.json`, set `js/ts.experimental.useTsgo` to `false`, `js/ts.tsserver.useSyntaxServer` to `never`, and select the workspace TypeScript version in VS Code/Cursor. Oxlint/ESLint still parse source text and may need file ignores on smoke demos.
- Vue: leave `:class` and other dynamic bindings unchanged.
- **Svelte**: only transform quoted UseClassy modifiers. Native `class:active={cond}` and `class:active` stay untouched — do not rewrite those.
- Do not move conditional base utilities into modifier attributes on Vue/Svelte/Blade; UseClassy modifiers represent Tailwind variants. React is the exception for `className:mod={…}` expression values.
## Verification
After a refactor:
1. Confirm every moved utility lost exactly the modifier encoded in its attribute name.
2. Confirm base and dynamic classes remain intact.
3. Confirm Svelte native directives remain expressions/directives.
4. Run the project’s formatter, typecheck, and relevant tests/build.
## More examples
See [examples.md](examples.md).