# Introduction (/docs) Zazz is a variables-driven design framework built on native web standards: tokens, cascade layers, and browser APIs. It requires no build step, avoids framework lock-in, and eliminates selector specificity conflicts. ## Principles [#principles] * **Tool-agnostic:** Shared tokens and structures work across Figma, HTML, and web frameworks. * **Precision:** Spacing, color, radius, and typography resolve from CSS variables on `:root`. * **Defaults:** Ready-to-use component styling with built-in light and dark theme support. * **Standards-based:** Uses native HTML elements and CSS features (Popover, ``, Invoker Commands, `@layer`, and container queries). ## Architecture [#architecture] 1. **Tokens:** Semantic variables (`--primary`, `--muted-foreground`, `--gap-md`) control the theme. 2. **Cascade layers:** Later layers (`utilities`) cleanly override earlier layers (`components`) without `!important`. 3. **Data attributes:** Variants use attributes like `data-variant="primary"` and `data-size="sm"` instead of modifier classes. 4. **Fluid scales:** Fluid `clamp()` typography and spacing scale with the viewport without breakpoint jumps. # Accordion (/docs/components/accordion) A `` wraps native `
` and `` elements. The class form `
` is equivalent: the tag is CSS-only without JavaScript. Expand and collapse states, keyboard interactions, and animations (via `::details-content`) run natively in the browser. ## Default [#default] Structure each panel as a `
` containing a `` with an SVG chevron, followed by a body `
`. Multiple panels can open simultaneously. Give `
` items matching `name` attributes for mutual exclusion. Customize using tokens: `--ui-accordion-icon-size` and `--ui-accordion-icon-transform-open`. # Alert dialog (/docs/components/alert-dialog) An alert dialog is a [Dialog](/docs/components/dialog) with a stricter contract: ``. There is no close button and no light dismiss; Cancel or Confirm buttons are the only ways to dismiss it. It composes standard dialog slots (`dialog-content`, `dialog-header`, `dialog-body`, `dialog-footer`) and uses a native `` opened with Invoker Commands. Give the dialog `aria-labelledby` pointing to the heading and `aria-describedby` pointing to the body so the prompt and its consequences are announced together. ## Default [#default] ## API [#api] | Attribute | Target | Values | | ------------ | ------------------ | --------------------------------------------------- | | `command` | Trigger | `show-modal`; `close` on the Cancel/Confirm buttons | | `commandfor` | Trigger | Target `` id | | `role` | `.ui-alert-dialog` | `alertdialog` | | `closedby` | `.ui-alert-dialog` | `none`: the alert contract | # Autocomplete (/docs/components/autocomplete) A `` wraps a `.ui-input` and an anchored suggestion panel (`popover="manual"`). The input holds the form value (free text allowed) and typing filters the panel options using the cmdk ranking algorithm. Arrow keys move the highlight without moving focus out of the input (`aria-activedescendant`). Pressing Enter or clicking commits a suggestion into the input. Groups hide automatically when all their options filter out, and the empty slot appears when nothing matches. Without JavaScript, the markup renders as a regular text input. Omit the `popover` attribute on the panel for an inline, always-open list (no floating surface or dismissal; filtering unchanged). ## Default [#default] ## Groups and score ranking [#groups-and-score-ranking] `data-sort="score"` re-ranks visible options by match quality using CSS `order` without altering the DOM tree. `data-keywords` adds extra search keywords to an option. ## API [#api] | Attribute | Target | Values | | -------------------------- | ---------------------------------- | ----------------------------------------------- | | `data-sort` | Root | `score`: rank by match (default: DOM order) | | `data-min-length` | Root | Query length before the panel opens (default 0) | | `data-value` | `[data-slot="autocomplete-item"]` | Match/commit text (default: text content) | | `data-keywords` | `[data-slot="autocomplete-item"]` | Space-separated extra match targets | | `data-side` / `data-align` | `[data-slot="autocomplete-panel"]` | Placement (popover matrix) | # Avatar (/docs/components/avatar) Avatars are composed with utility classes. Stack an image over fallback text using `grid grid-area-pile`, clip with `rounded-full overflow-clip`, and set dimensions with `size-*`. Fallback text (`bg-muted`, `text-muted-foreground`) shows if the image fails to load. For text-only avatars, omit the `` element. ## Default [#default] # Badge (/docs/components/badge) Badges use the `.ui-badge` class. Set colors with `data-variant` (default when omitted). Set `data-size="icon"` for square icon badges. Badges can be static `` elements or interactive ` ``` | `data-slot` value | Purpose | | --------------------------------- | ------------------------------------------------ | | `carousel-viewport` | Visible window (required) | | `carousel-container` | Flex track for slides | | `carousel-slide` | Individual slide item | | `carousel-prev` / `carousel-next` | Navigation buttons (optional) | | `carousel-dots` / `carousel-dot` | Pagination container and template dot (optional) | | `carousel-thumbs` | Synchronized thumbnail container (optional) | `data-slot` is a space-separated token list, so an element can serve two primitives at once: a lightbox slide is also a carousel slide (`data-slot="lightbox-slide carousel-slide"`). Selectors match slots with `[data-slot~="carousel-slide"]`. ## Setup [#setup] Load `index.js` as an ES module: ```html ``` Third-party dependencies (Embla core and plugins) resolve through the ` ``` Reveal initializes automatically when the DOM loads. ## Single elements [#single-elements] ```html
Slides up into view
Fades in
Slides in from right
``` ### Animation types [#animation-types] | Value | Effect | | ------------- | ----------------------------- | | `slide-up` | Translate up from below | | `slide-down` | Translate down from above | | `slide-left` | Translate left from the right | | `slide-right` | Translate right from the left | | `fade` | Opacity fade | | `grow` | Scale up from smaller | | `shrink` | Scale down from larger | ## Stagger groups [#stagger-groups] Animate child elements sequentially using `data-reveal-each`: ```html
Item 1 (first)
Item 2 (100ms delay)
Item 3 (200ms delay)
``` Set `data-reveal-order="reversed"` to animate children in reverse order. ## Element transition isolation [#element-transition-isolation] Reveal controls `transition-*` properties to handle delays and durations. To apply hover transitions (such as borders or background changes) without conflicting with scroll reveals, place the reveal attribute on a parent container: ```html
``` ## Configuration attributes [#configuration-attributes] | Attribute | Default | Purpose | | ----------------------- | ----------------------------- | ------------------------------------------------- | | `data-reveal-duration` | `--ui-reveal-global-duration` | Animation duration | | `data-reveal-wait` | `0` | Base delay before starting (ms) | | `data-reveal-step` | `80` | Delay step between children (ms) | | `data-reveal-ease` | `--ui-reveal-global-ease` | Timing function | | `data-reveal-distance` | `1rem` | Slide translation distance | | `data-reveal-scale` | None | Scale value for grow/shrink | | `data-reveal-margin` | `0px` | Observer rootMargin | | `data-reveal-threshold` | `0.2` | Intersection threshold (0 to 1) | | `data-reveal-order` | None | Set to `"reversed"` for reversed stagger sequence | ## Global tokens [#global-tokens] ```css :root { --ui-reveal-global-duration: var(--default-transition-duration); --ui-reveal-global-ease: var(--default-transition-timing-function); --ui-reveal-global-wait: 0ms; --ui-reveal-global-distance: 1rem; --ui-reveal-global-grow: 0.97; --ui-reveal-global-shrink: 1.03; } ``` ## Dynamic updates [#dynamic-updates] To observe newly inserted DOM elements: ```js Reveal.getAutoInstance()?.refresh(); ``` # Select (/docs/components/select) `.ui-select` styles native `` to serve as the trigger and mirror selection. ## Default [#default] ## Placement [#placement] Use `data-side` and `data-align` on `.ui-select` to position the dropdown panel relative to the trigger button: ### Sides [#sides] ### Alignment [#alignment] ## Multiple [#multiple] `appearance: base-select` does not cover `` elements. Configure with standard `min`, `max`, `step`, and `value` attributes. ## Default [#default] # Switch (/docs/components/switch) The switch restyles `` into a sliding toggle. Bind with `checked`, `name`, and `value`. ## Default [#default] # Table (/docs/components/table) Apply `.ui-table` to a standard `` element (``, ``, ``, ``, `` for selected styling. Wrap wide tables in `
` for horizontal scrolling. ## Alternating rows [#alternating-rows] Add `data-variant="alternating"` for zebra striping: ## Grid format [#grid-format] Add `data-variant="grid"` for borders around all cells: ## Compact size [#compact-size] Add `data-size="sm"` to reduce cell padding: ## API [#api] | Attribute | Values | | -------------- | ----------------------------------------- | | `data-variant` | `alternating`, `grid` (omit = default) | | `data-size` | `sm` | | `data-layout` | `fixed` (omit = `table-layout: auto`) | | `data-state` | `selected` (on a `
`) | | `data-side` | `top` (on the `
`, `
`, ``). Rows include hover tints by default. Set `data-state="selected"` on a `
`; omit = bottom) | Override styling tokens globally on `:root` or on an instance (`--ui-table-row-background--hover`, `--ui-table-alternating-background`). # Tabs (/docs/components/tabs) Tabs use grouped `input[type="radio"][role="tab"]` inputs to toggle panels and sliding indicators using pure CSS. Wrap in `` for keyboard navigation (arrow keys, Home/End, wrap-around). Radio button order must match tab panel order. ## Default [#default] ## API [#api] | Attribute / Element | Placement | Values | | ------------------- | --------- | --------------------------------------------- | | `` | Root | Custom element root; adds keyboard navigation | | `data-orientation` | Root | `vertical` (horizontal by default) | # Textarea (/docs/components/textarea) `.ui-textarea` shares `--ui-field-*` tokens and `:user-invalid` validation states with the form input family. It applies `field-sizing: content` to auto-expand with text input, clamped between `--ui-textarea-min-block-size` (`5lh`) and `--ui-textarea-max-block-size` (`12lh`). ## Default [#default] # Toaster (/docs/components/toaster) Toasts render inside ``, a custom element placed on the top layer via the Popover API. Trigger toasts from buttons using `commandfor=""` and `command="--toast"`, or from JavaScript using `window.Toaster`. Toasts auto-dismiss after 4 seconds (pausing on hover or when the browser tab is hidden). Place `` once per page outside `
`. ## Default [#default] ## API [#api] | Attribute | Target | Values | | ------------------- | -------------- | --------------------------------------------------------------------------------------------- | | `command` | Trigger | `--toast`, `--toast-success`, `--toast-info`, `--toast-warning`, `--toast-destructive` | | `commandfor` | Trigger | ID of target `` | | `data-title` | Trigger | Toast title | | `data-description` | Trigger | Toast body copy | | `data-variant` | Trigger | `success`, `info`, `warning`, `destructive` | | `data-duration` | Trigger | Lifetime in ms (`Infinity` persists until dismissed) | | `data-close-button` | Trigger | Set to `false` to hide close button | | `data-position` | `` | `top-start`, `top-center`, `top-end`, `bottom-start`, `bottom-center`, `bottom-end` (default) | | `popover` | `` | `manual` (required) | ## JavaScript API [#javascript-api] ```js // Create toast const id = Toaster.toast({ title: "Event created", description: "Monday, July 13 at 9:00 AM", variant: "success", duration: 4000, action: { label: "Undo", onClick: (event) => {} }, closeButton: true, region: "my-toaster", }); // Shorthands Toaster.success("Changes saved"); Toaster.info("Update available"); Toaster.warning("Storage almost full"); Toaster.error("Something went wrong"); // Dismiss toast Toaster.dismiss(id); Toaster.dismiss(); // dismiss all ``` # Toggle group (/docs/components/toggle-group) Wrap `.ui-toggle` elements in a `` (or the equivalent `
`) to combine them into a single segmented unit. Use `radio` inputs for **single-select** and `checkbox` inputs for **multi-select**. Add `role="group"` and `aria-label` for accessibility. ## Single select [#single-select] ## Multi select [#multi-select] ## Vertical [#vertical] Add `data-orientation="vertical"` to stack items: ## API [#api] | Attribute | Values | | ------------------ | -------------------------------------- | | `data-orientation` | `vertical` (omit = horizontal default) | Outer corners adapt to child `--ui-toggle-radius` tokens automatically. # Toggle (/docs/components/toggle) A toggle is a `