Get started
How LUZ is organised
Packages that depend in one direction, CSS in cascade layers inside one called luz, and one optional script.
Package boundaries
Each package depends on the ones above it in this list, never on the ones below.
The name stands for Light, Usable & Zero-dependency: light in weight (plain CSS and ES modules, nothing to build for the people who use it), usable by everyone (native HTML, three color modes, tested for accessibility), and with zero runtime dependencies.
@luz-ds/tokens -> primitive and semantic tokens (standalone)
↓ required by
@luz-ds/core -> components, objects, bare-element styles, optional JS
↓ required by
@luz-ds/catalog -> search, cookie-consent, theme-time, lang-switch, submenu, announcement, and the patterns of a landing page, a blog or newspaper, a web application and a documentation site
- The catalog needs core and tokens; core needs tokens; tokens needs nothing.
- Tokens, core and catalog share one version. There are no packages for one kind of site: the patterns of a landing page, a blog, an application or a documentation site are components of the catalog, one file each.
- The packages say so as peer dependencies. The core and catalog scripts warn in the console when a level is missing.
The documentation follows the packages, with one area more: Kinds of site puts together, for each kind, the prompt for an LLM and the recipes of its parts, made of the packages above and of the patterns of that kind in the catalog.
What each package needs
Find the part you want, and install the package of its row and the ones below it.
| Piece | Classes or attributes | Package | Notes |
|---|---|---|---|
Color modes, semantic tokens, ET Book | --luz-*, data-theme | tokens | Nothing else is needed |
Reset and styles of bare HTML elements | h1, p, a, ul… | core | In base.css and luz.css |
Components | .luz-btn, .luz-field, .luz-card, .luz-table, .luz-tabs… | core | Also one file each, in components/ |
Modal | .luz-modal on a <dialog> | core | The script is a fallback for browsers without command and commandfor |
Layout objects | .o-container, .o-grid, .o-stack, .o-media | core | Spacing comes from the space tokens |
An app layout with a sidebar column | .o-shell, .o-main | core | A plain <footer> as a direct child of .o-shell goes across the bottom; its look is yours, or a kind of site's footer |
A page with no sidebar, a sticky bar, an action group | .o-page, .o-sticky, .o-actions | core | Instead of .o-shell, for a page with no sidebar: the page and its bars |
Color mode: Auto, light, dark and high contrast | <luz-theme-mode> | core | luz.js loads its script, theme-mode.js, when the page has one; without it the radios still work and Auto follows the system: Color mode |
Skip link, band, image frame | .luz-skip-link, .luz-band, .luz-image | core | The image frame keeps a ratio and crops the picture to it; with no picture it is a flat surface |
The header, hero, sections and footer of a landing page | .luz-site-header, .luz-hero, .luz-main, .luz-section, .luz-stat, .luz-site-footer, .luz-glow | catalog | One file each, components/<block>.css. On core's objects (.o-page, .o-sticky, .o-container, .o-grid--split) and the catalog's navbar. A feature grid is ul.o-grid.o-grid--split, core's |
The header, page header, article header, table of contents and footer of a blog or a newspaper | .luz-site-header (--publication), .luz-masthead, .luz-page-header, .luz-article-header, .luz-article, .luz-toc, .luz-site-footer | catalog | One file each, components/<block>.css; the table of contents is in article.css. On core's objects and the catalog's navbar. Post cards, listings and the author box are core's card: see Blog or newspaper |
The header and the dashboard of a web application | .luz-app-bar (__product, __actions, __account), .luz-dashboard (__head, __title, __panel) | catalog | One file each, components/app-bar.css and components/dashboard.css. On core's shell, sticky bar, grid and card, and the catalog's sidebar. No script. See App header and Dashboard |
The shell, page head, rail, example, do and don't, pager and page foot of a documentation site | .luz-docs-layout, .luz-doc-header, .luz-page-rail, .luz-example, .luz-do-dont, .luz-pager, .luz-page-footer | catalog | One file each, components/<block>.css. On core's shell, code block and tables, and the catalog's sidebar. No script. See Shell |
The links of a header | .luz-navbar | catalog | One file, components/navbar.css. A popover panel at 768 px and below, opened by a .luz-nav-toggle, with no script. luz.css does not include it |
A sidebar, its brand and its menu button | .luz-sidebar (__brand, __logo, __brand-name, __version), .luz-nav-toggle | catalog | One file, components/sidebar.css. The drawer is a native popover and needs no script. catalog.js loads sidebar.js, the scroll spy, when the page has a sidebar; a page that loads only core has no scroll spy |
A command in a framed block | .luz-terminal | catalog | One file, components/terminal.css. No script of its own: the copy button is core's |
A timeline of dated events | <luz-timeline>, .luz-timeline | catalog | catalog.js loads its script, timeline.js, when the page has one; without it the timeline is a list |
Submenu, announcement | <luz-submenu>, <luz-announcement> | catalog | Tags with no script and no class: the submenu is a native <details> in a header's links, the announcement a <section> |
Cookie consent | <luz-cookie-consent>, .luz-cookie-consent | catalog | catalog.js loads its script, cookie-consent.js, when the page has one; a band fixed at the bottom of the window; without the script it stays hidden |
Color mode with the time of day, and the slider of the day | <luz-theme-time> | catalog | catalog.js loads its script, theme-time.js, when the page has one, and theme-time-bar.js for a variant="bar"; without them core's radios still work and Auto follows the system |
Language switch | <luz-lang-switch> | catalog | No script |
Site search | <luz-search>, .luz-search | catalog | Needs the script ( catalog.js, which loads search.js when the page has one) and an index file; without them it is a link |
Print styles | Nothing to add | core | In luz.css; catalog adds its own print rules |
A zone in a page that already has a design | data-luz | core | luz.conditional.css, instead of luz.css |
Checking what you or an AI agent wrote | npx @luz-ds/check@0.3.0, the luz-check command | check | A tool with no CSS and no layer, and no other package needed: it reads your HTML and CSS offline. See Working with AI |
- Two ways to lay out a page, both core's.
.o-shellwith.o-main: a sidebar column beside the main area..o-page: a header, the content and a footer in one column. Pick one for the grid of the page: the first when it has a sidebar, the second when it has none. A landing or CMS header and footer work with both; these docs use.o-shell. - The catalog needs core, and core needs tokens, so a row at a level also needs every package under it. The patterns of a landing page, a blog, an app and a documentation site are catalog components, so they need core and tokens too.
CSS layers
Core's CSS comes in native cascade layers (@layer), in ITCSS order, inside one container layer called luz. The six below are the ones that hold its rules; the layers page lists them all, with tokens, catalog and print.
A later layer wins over an earlier one, and any CSS of yours that is not in a layer wins over all of LUZ, with no !important. How to order and override them.
packages/core/src/
settings/_settings.css luz.settings z-index, layout widths, field text
generic/_reset.css luz.generic the reset
elements/*.css luz.elements bare HTML elements, zero specificity
objects/_objects.css luz.objects layout: .o-*
components/*.css luz.components the components: .luz-*
utilities/_utilities.css luz.utilities single-purpose helpers: .u-*
- The components layer holds 29 components.
dist/luz.cssholds these six layers andluz.print. Tokens are inluz.tokens, catalog, with the navbar, the sidebar, the terminal, the timeline and the patterns of a kind of site, inluz.catalog;luz.themesis empty, kept for a look of your own. For a page that needs a few components only, loaddist/base.css(settings, reset and elements), thendist/components/<name>.cssfor each one.- Rules for bare elements are wrapped in
:where(), so any class wins over them.
The script
One optional entry point, luz.js, starts every behaviour on the page: color mode, copy buttons, dialogs, highlighting, players and tabs.
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.js"></script>
To load less, import one module and call its init function. See JavaScript modules.
Minimal page
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sign up – Acme</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/tokens@0.3.0/dist/luz-tokens.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.css">
</head>
<body>
<main class="o-container">
<h1>Sign up</h1>
<form action="/signup/" method="post">
<div class="luz-field">
<label class="luz-field__label" for="email">Email</label>
<input class="luz-input" id="email" name="email" type="email" autocomplete="email" required>
</div>
<button class="luz-btn luz-btn--primary" type="submit">Create account</button>
</form>
</main>
</body>
</html>