Docs
Quick start
A page in five steps: one head snippet, a layout, a header, a hero, and a theme.
You need a text editor and a browser. You install nothing; the kit loads from a CDN. When you are ready to own the files, the installation page covers the CLI.
1. The head
Create index.html and paste this into <head>. It is the output of the kit's own buildHead() with the fonts block off: the stylesheet, the import map for the kit's bare dependencies (signal-polyfill and the Embla packages), the one polyfill, the script, and a theme script that pins the stored theme-dark / theme-light switch before first paint.
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Paint the right theme (background, form controls, scrollbars) before CSS loads -->
<meta name="color-scheme" content="light dark">
<!-- Zazz styles: the whole kit, one request -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@zazz-ui/core@0.7.0/dist/zazz.css">
<!-- Import map: bare specifiers in Zazz scripts resolve to pinned, SRI-checked files -->
<script type="importmap">
{
"imports": {
"signal-polyfill": "https://cdn.jsdelivr.net/npm/signal-polyfill@0.2.2/dist/index.js",
"embla-carousel": "https://cdn.jsdelivr.net/npm/embla-carousel@8.6.0/esm/embla-carousel.esm.js",
"embla-carousel-autoplay": "https://cdn.jsdelivr.net/npm/embla-carousel-autoplay@8.6.0/esm/embla-carousel-autoplay.esm.js",
"embla-carousel-auto-scroll": "https://cdn.jsdelivr.net/npm/embla-carousel-auto-scroll@8.6.0/esm/embla-carousel-auto-scroll.esm.js",
"embla-carousel-class-names": "https://cdn.jsdelivr.net/npm/embla-carousel-class-names@8.6.0/esm/embla-carousel-class-names.esm.js"
},
"integrity": {
"https://cdn.jsdelivr.net/npm/signal-polyfill@0.2.2/dist/index.js": "sha384-0OqM7OXGT6Oz1z98u/geiFn/FUyz4iWexkXHCvbIGXRRDXl6bp6IKc0j78AQ6DEK",
"https://cdn.jsdelivr.net/npm/embla-carousel@8.6.0/esm/embla-carousel.esm.js": "sha384-GXjfyU1fSHHeweoYd3rwo7OBKYtkq+JcsDoe8VDRc6znCty2ZTzyVvX1DwqCE+9t",
"https://cdn.jsdelivr.net/npm/embla-carousel-autoplay@8.6.0/esm/embla-carousel-autoplay.esm.js": "sha384-AX49yBc7zd/CT8ZqNMRiiqqVSV9xfxb27VwEGziPioT3wL4k4C5BW2qwefdWuN4J",
"https://cdn.jsdelivr.net/npm/embla-carousel-auto-scroll@8.6.0/esm/embla-carousel-auto-scroll.esm.js": "sha384-SPdnL1eic73WfM72zFEf0RJ5PdUC7TL3Yb5jOl1JC3/l4D7EsVxwO8wFnnezlMSu",
"https://cdn.jsdelivr.net/npm/embla-carousel-class-names@8.6.0/esm/embla-carousel-class-names.esm.js": "sha384-ZWno/FqzYeWE5Ils19GIhep1AZ5bMl0mp9wmm6baTJE4+pjMjOdTnE55SjUQAdVh"
}
}
</script>
<link rel="modulepreload" href="https://cdn.jsdelivr.net/npm/@zazz-ui/core@0.7.0/dist/zazz.js">
<!-- Polyfill: Interest Invokers (interestfor) — native in Chromium only -->
<script type="module" src="https://cdn.jsdelivr.net/npm/invokers@2.2.2/dist/esm/production/interest.js" integrity="sha384-hR2BVNtsS7fIIMwOm+f7MY0JZfuByGhQQfevCBB6evFATKzBsTw0JzH/RxD94z2T" crossorigin="anonymous"></script>
<!-- Zazz behavior: the whole kit, one module -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@zazz-ui/core@0.7.0/dist/zazz.js"></script>
<!-- Theme: apply the persisted (or preferred) scheme before first paint -->
<script>
(() => {
try {
// A stored choice pins the scheme with the theme-dark / theme-light
// switch; with no choice the page follows the system scheme.
const storedTheme = localStorage.getItem("theme");
if (storedTheme === "dark" || storedTheme === "light") {
const root = document.documentElement;
root.dataset.ui = ((root.dataset.ui || "") + " theme-" + storedTheme).trim();
}
} catch {
/* no storage access: follow the system scheme */
}
})();
</script>2. A layout
Add a header and a main region. data-ui="layout" turns an element into the band grid for its children: everything inside is capped and centered at the xl band unless placed elsewhere.
<body>
<header data-ui="layout" style="--py: 4; --border-b: 1">
<div style="--display: flex; --items: center; --justify: space-between">
<a href="/" data-ui="text-lg" style="--font-weight: strong">Northwind</a>
<nav style="--display: flex; --gap: 1">
<a href="#menu" data-ui="button" data-button-variant="ghost">Menu</a>
<a href="#signup" data-ui="button" data-button-variant="primary">Join</a>
</nav>
</div>
</header>
<main data-ui="layout"></main>
</body>Three things happened in that header. --py: 4 is four steps on the spacing scale. --border-b: 1 is a one-pixel border in --color-border. And the links became buttons through an identity and a preset, not a class.
3. A hero
Inside <main>:
<section style="--py: 24; --text-align: center">
<p data-ui="text-eyebrow">Small-batch roasting since 2019</p>
<h1 data-ui="text-h4" style="--mt: 2; --text-wrap: balance">Coffee worth slowing down for</h1>
<p data-ui="text-lg" style="--mt: 4; --mx: auto; --max-w: var(--article-sm); --text: var(--color-muted-foreground)">
Beans roasted every morning, shipped the same day.
</p>
</section>text-eyebrow, text-h4, and text-lg are typography roles: each bundles family, size, weight, leading, tracking, and case, and every size is fluid. Write the text in sentence case; the eyebrow's capitals come from the role.
4. Cards in a grid
<section id="menu" style="--py: 8">
<div style="--display: grid; --grid-cols: 1; --grid-cols--md: 3; --gap: 4">
<article data-ui="card" style="--p: 6; --display: flex-col; --gap: 2">
<span data-ui="badge" data-badge-variant="muted">Espresso</span>
<h2 data-ui="text-lg">The classic</h2>
<p data-ui="text-sm" style="--text: var(--color-muted-foreground)">
A Honduran single origin with notes of dark chocolate and orange peel.
</p>
</article>
<!-- two more cards -->
</div>
</section>--grid-cols--md: 3 is a breakpoint tier: one column until the container is 65 characters wide, three from there. Breakpoints read the nearest inline-size container, not the window, so the same card grid works in a sidebar.
5. A theme
Open the page. The text uses the system fonts, and the colors and form controls already follow the system color scheme, because every color role is a light-dark() pair. Pin a scheme with data-ui="theme-dark" on <html> (the head's script does that from a stored choice), or change the look everywhere with a few tokens:
<style>
@layer overrides {
:root {
--color-primary: light-dark(oklch(0.45 0.15 30), oklch(0.8 0.12 30));
--radius-multiplier: 0.5;
--ui-button-font-weight: 600;
}
}
</style>A token recolors every primary surface; a hook retunes every button. Neither touches your markup. The theming page has the whole model.
Next
Add <ui-debug data-debug-domains="localhost"></ui-debug> to the body while you work: it warns in the console about a misspelled utility, a tier without a base, or a preset on the wrong element. Then read the core concepts, or jump to the API reference.