Zazz Design Framework
Getting Started

Overview

How Zazz controls the cascade with layers, tokens, and conventions: no specificity wars, no build step.

Zazz replaces specificity-based CSS architecture with a layered cascade. Once you see how the layers, tokens, and conventions fit together, you can customize and extend the system without fighting it. This page is the map; each section links to the page that covers it in full.

The cascade layers

Every stylesheet in Zazz slots into a cascade layer, declared once, in order, in _layers.css:

@layer variables, reset, legacy, zazz, migrations;

@layer zazz {
  @layer components, utilities;
}

Later layers override earlier ones regardless of selector specificity, so you never need !important, BEM modifiers, or naming hacks:

LayerPurposeExample
variablesToken declarations only (lowest):root { --button-radius: var(--radius-md); }
resetNative element baselineshtml, body, input, select defaults
legacyYour pre-Zazz CSS during a migration@import "old.css" layer(legacy)
zazz.componentsComponent rules.button, .dialog, .field
zazz.utilitiesAtomic override classes.text-primary, .gap-md, .rounded-full
migrationsClass-translation shims during a migration (highest).btn-primary { --button-background: … }

A plain .button rule in components is overridden by a utility in utilities, with no !important. The outer legacy and migrations layers bracket the stack for incremental adoption; day to day you work in the two Zazz sublayers. For the full model, see _layers.css.

base files

Four files form the foundation, and every other file depends on them:

  • _layers.css declares the cascade order and must load first.
  • _variables.css holds every global token: brand scales, semantic roles, and the metric systems for spacing, type, radius, and shadow.
  • _reset.css sets native-element defaults, all wrapped in :where() for zero specificity so component rules always win.
  • _utilities.css is the atomic override layer, also zero-specificity.

Tokens and theming

Nothing in Zazz is hardcoded. Components read var(--token), so you restyle by reassigning variables rather than rewriting rules. Tokens run from raw scales up to per-component hooks, and you override at the narrowest scope that fits:

:root {
  --primary: oklch(0.6 0.2 145); /* global: re-skin the brand */
  --button-radius: var(--radius-full); /* component: all buttons go pill-shaped */
}
<button class="button" style="--button-background: var(--secondary)">Instance: this button only</button>

The token tiers and the three override scopes are covered in full on Theme variables.

Typography

Zazz ships a fluid type system: every size is a clamp() that scales smoothly from phone to desktop with no breakpoints. A single text-* class bundles the right size, weight, line-height, and letter-spacing for its step, and .prose applies the whole rhythm to long-form content automatically.

<span class="text-display">Big launch headline</span>
<p class="text-sm text-muted-foreground">Caption text</p>

See Text & font for the type utilities and Prose for long-form content.

Variants and sizes

Components expose their styles through data-* attributes instead of BEM modifier classes. The default variant is always the absence of the attribute:

<button class="button" data-variant="primary" data-size="sm">Save</button>

Each component documents its own data-variant and data-size values; see Button for a worked example.

Dark mode

Dark mode is on from the first line. :root sets color-scheme: light dark, and every role token resolves through light-dark(), so a role-based design is correct in both themes with no parallel styles. See Dark mode for the system toggle, the .dark override, and inverted surfaces.

Next steps

On this page