Hover, focus, and other states
Style hover, focus, validation, and disabled states straight from your markup, with prefixed utilities for hover and sensible defaults that handle the rest.
Interactive states in Zazz come from two places: hover: utilities you add yourself, and defaults the framework already handles: focus rings, validation feedback, and disabled styling. Most of the time you write less than you'd expect.
Hover
Prefix a utility with hover: and it applies while the pointer is over the element. The prefix works on color, opacity, and scale:
<a class="button hover:bg-primary hover:scale-105">Hover me</a>
<div class="card hover:opacity-75 transition">…</div>In your HTML you write hover:bg-primary, with no backslash. The backslash you'll see in the
source (.hover\:bg-primary) is only CSS escaping the colon in the selector; the class attribute
uses a plain colon.
What hover: covers:
- Text color:
hover:text-primaryand every other theme role. - Background:
hover:bg-primary, plushover:bg-transparentandhover:bg-none. - Border:
hover:border-primary,hover:border-destructive, and the other roles. - Opacity:
hover:opacity-{0,25,50,75,100}. - Scale:
hover:scale-{50,75,90,95,100,105,110,125,150}.
Pair them with transition so the change eases instead of snapping. Motion respects prefers-reduced-motion automatically, so you don't have to guard it.
Focus
You rarely write focus styles. The reset applies a :focus-visible ring to every focusable element, so keyboard users get a clear, consistent indicator everywhere. And :focus-visible means it shows for keyboard navigation without firing on every mouse click.
/* Already handled globally — shown for reference, not something you write */
:where(:focus-visible) {
outline: var(--outline-width) var(--outline-style) var(--ring);
}To recolor the ring in a given context, override --ring-color (not --ring, which recomputes from it):
<form style="--ring-color: var(--destructive)">…</form>Add the ring utility when you want a persistent ring on an element regardless of focus. The ring renders as a box-shadow from Tailwind/shadcn-compatible tokens, and a matching transparent outline is kept so focus stays visible in forced-colors mode.
Pressed and active
There are no active: utilities. Express pressed feedback in a component's own CSS with :active. That keeps tactile, per-component motion where it belongs rather than scattering it across markup. The built-in components already do this.
Validation states
Form fields validate on :user-invalid, which only matches after the user has interacted, on blur or submit, never while they're still typing. That timing is the difference between helpful and nagging, and you get it for free; no was-touched class to toggle.
<div class="field">
<label for="email">Email</label>
<input id="email" type="email" required />
<p class="field__error">Enter a valid email address.</p>
</div>The shared field family crossfades label, hint, and error for you. :user-invalid is Baseline and widely available. See Input for the full form markup.
Disabled
Components style their disabled state through :not(:disabled) guards rather than order-dependent overrides, so a disabled attribute is all you add:
<button class="button" data-variant="primary" disabled>Saving…</button>Component states via data attributes
Open/closed, selected, and side/alignment states are driven by data-* attributes that the framework and native APIs toggle: data-state, data-side, and friends. You set the variant; the component handles the transitions. See any component page for its attribute API.
Styling with utility classes
Build interfaces by composing small, single-purpose classes in your markup, each one wired to a design token, so what you build stays on-system.
Responsive design
Zazz is responsive by default. Fluid type and spacing scale on their own, and responsive prefixes read one centralized set of breakpoints that the body container publishes off the page width.