Get started
Install LUZ
Pick a level, then copy one install command or the CDN tags. Each level needs the ones below it and nothing above.
LUZ is a place to start, not a finished site. The content, the brand and the final decisions are yours: check the result before you publish.
Know what you are building? Choose your snippet and copy the lines for it, CDN hashes included.
Loading LUZ from a CDN sends your visitors’ IP addresses to the CDN. If you do not want that, install from npm and serve the files from your own server.
Which level do I need?
- A new project: level 2, tokens and core.
luz.cssstyles the HTML you write. - A project that already has a design: level 1, tokens only, to start: it changes nothing you can see. Then level 2 for an existing project, with
luz.conditional.css, one region at a time. Migrate a project to LUZ has the steps. - Your own components, LUZ’s colours and spacing: level 1, tokens only.
- Ready-made buttons, forms, tables and dialogs: level 2, tokens and core.
- Whole pages: shell, page header, footer, search: level 3, the Catalog, all three packages.
- A landing page, with its header, hero and blocks: level 3 and the patterns for that kind of site, which are catalog components.
Level 1: tokens only
CSS custom properties: the colour, space and type scales, the font roles, the breakpoints and the motion durations, and the colours of light, dark and high contrast. No other LUZ package is needed.
pnpm add @luz-ds/tokens
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/tokens@0.3.0/dist/luz-tokens.min.css">
With a bundler such as Vite, import the files instead of adding tags. A href cannot name a package: these paths are for import only.
import "@luz-ds/tokens/css";
- Running text and headings use ET Book, an MIT-licensed font that ships with the tokens. Controls, labels and tables use the system sans, and code the system mono.
- Bringing your own fonts? Load
dist/tokens.min.cssinstead: the same tokens without any@font-face. - Check your own colours against the WCAG ratios with
npx @luz-ds/tokens@0.3.0 check-contrast, or in the browser.
Level 2: tokens and core
The 29 components, the styles of bare HTML elements, and optional JavaScript. Load tokens first.
pnpm add @luz-ds/tokens @luz-ds/core
<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">
<!-- Optional: copy buttons, tabs, players, the color mode -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.js"></script>
import "@luz-ds/tokens/css";
import "@luz-ds/core/css";
// Optional: copy buttons, tabs, players, the color mode
import "@luz-ds/core";
- Every page works without the script. See what each JavaScript module adds.
- Need a single component? Load
dist/base.min.cssplus the file you want, for exampledist/components/button.min.css. - Markup you cannot add classes to, such as Markdown or a CMS? A bare
button, field ortablealready has its look inluz.css. On a page that already has a design, loadluz.conditional.cssinstead and mark the zone withdata-luz.
Level 2 for a project that already has a design
Load luz.conditional.css instead of luz.css. It is the same file, with the reset and the bare elements limited to the region you mark with data-luz. Load the tokens first, as for any level. From a CDN, link luz.conditional.min.css of @luz-ds/core in place of luz.min.css; with a bundler, import it as below.
import "@luz-ds/tokens/css";
import "@luz-ds/core/conditional.css";
- The element with
data-luztakes the font, color and background of the page. The tokens stay on:root. - Layout objects, components and utilities are not limited to the region: they apply wherever you put their class.
- Your old CSS must not be unlayered, or it beats LUZ. Put it in a layer before
luz: see Cascade layers and Migrate a project to LUZ.
Level 3, the Catalog: tokens, core and catalog
The site's navigation and the pieces with a script or an identity of their own: navbar, sidebar, terminal, timeline, search, cookie consent, color mode by time of day, language switch, submenu, announcement and FAQ, and the patterns of a kind of site. The catalog needs core and tokens.
The page structure of each kind of site, from the header to the footer, is in the catalog too, one stylesheet per pattern: see Kinds of site.
The catalog has a stylesheet per component: link the ones the page uses, after core.
pnpm add @luz-ds/tokens @luz-ds/core @luz-ds/catalog
<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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/search.min.css">
<!-- Optional: core behaviours, and the site search and cookie banner -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/catalog.min.js"></script>
import "@luz-ds/tokens/css";
import "@luz-ds/core/css";
import "@luz-ds/catalog/components/search.css";
// Optional: core behaviours, and the site search
import "@luz-ds/core";
import "@luz-ds/catalog";
Kinds of site
To start a landing page, a blog, an app or a documentation site, go to Kinds of site: a prompt for any LLM, and the recipes of each part to copy.
There is no package for a kind of site. Its patterns are components of the catalog, one stylesheet each, named for what the piece is, <block>.css. Install level 3, then link the files your page uses, after core:
- Landing page:
site-header,hero,main,section,stat,site-footerandglow: the header, hero, sections and footer. - Blog or newspaper, with any CMS:
site-header(ormastheadfor a newspaper),page-header,article-header,article,post-listandsite-footer: the header, page header, article header, contents, post list and footer. - Web application:
app-bar,dashboardand, for the panels that show data,bar-list,column-chart,stacked-bar,ringandheat-map. - Documentation site:
docs-layout,doc-header,page-rail,example,do-dont,pagerandpage-footer.
The catalog has a page for each pattern, and says which kinds of site it fits best.
<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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/site-header.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/hero.min.css">
import "@luz-ds/tokens/css";
import "@luz-ds/core/css";
import "@luz-ds/catalog/components/site-header.css";
import "@luz-ds/catalog/components/hero.css";
- Link the files of a kind in the order of the list above.
- Link
herowithsection: the head of a section shares its rules with the hero. - None has a script. The catalog's script is optional: with the table of contents of a blog, it marks the section in view; with the sidebar of an app, it marks the current link and closes the drawer when a link is followed. Core's script does not do this.
Before you ship
- npm and pnpm install the lower levels for you. Yarn only warns, so list every package, as above.
- On a CDN, pin an exact version and add an
integrityhash withcrossorigin="anonymous". Compute the hash from the file you load, as below. - The core and catalog scripts warn in the console when the level below is missing.
- Set
langon<html>: core's scripts write their text in that language (English and Spanish ship).
curl -s https://cdn.jsdelivr.net/npm/@luz-ds/core@0.3.0/dist/luz.min.css | openssl dgst -sha384 -binary | openssl base64 -A
Write the result as integrity="sha384-…" on the tag that loads that file, and repeat it for each file.
A hash on a script covers that file only, not the files it loads later. catalog.min.js loads search.min.js on a page with a search and cookie-consent.min.js on a page with a cookie banner and theme-time.min.js on a page with a time of day for the color mode.
Give each one its own hash in an import map, before the first module script. It works in Chrome 127, Safari 18 and Firefox 138 and later; an older browser ignores it and loads the files unchecked.
A <link rel="modulepreload"> with a hash is not a guard in Safari or Firefox: use it as a performance hint only.
<script type="importmap">
{
"integrity": {
"https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/search.min.js": "sha384-…",
"https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/cookie-consent.min.js": "sha384-…",
"https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/theme-time.min.js": "sha384-…"
}
}
</script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/catalog.min.js" integrity="sha384-…" crossorigin="anonymous"></script>
Next steps
- Choose your snippet for your kind of site and the way you load it.
- Browse the components and copy their markup.
- Set up the color modes: light, dark and high contrast.
- Learn how the tokens work before you write your own components.
- See how LUZ is organised: packages, CSS layers and the page skeleton.
Frequently asked questions
The short answers. Each links to the page that explains it.
What is LUZ?
LUZ is a design system for the web, in three packages: tokens (colors, scales, fonts and three color modes), core (the reset, the style of HTML elements, layout objects, components and optional JavaScript) and the catalog (pieces that act for the whole site, such as search). Use the tokens alone, core with the tokens, or all three. Working with AI shows how an AI can write a site with it.
Are HTML tags styled without a class?
Yes, most of them. Text, links, lists, tables, buttons, form fields, details and dialog come out styled with no class. Tags that only structure the page, such as div, section, header and nav, have no style: the layout is yours. Which elements have a style with no class lists them all.
What are the classes for?
A class adds what HTML cannot say: a link that looks like a button, a role such as danger, a size, a component with markup of its own such as a card, and the layout with the o-* objects. The HTML says the rest: type="button" makes the secondary button, and aria-invalid="true" marks a field with an error. Browse the components to see what each class adds.
Do I need JavaScript?
No. Most components are CSS only, and the ones with a script work without it too. JavaScript modules says what each module adds.
Can I change the default styles?
Yes, without fighting them. Change the tokens to change the whole look, or write your own CSS: CSS with no layer beats every layer of LUZ, with no !important. What you leave alone stays. Changing the default styles shows the three ways.
Is LUZ only for new projects?
No. LUZ fits a new project best, but you can add it to one that already has a design, one region at a time: load luz.conditional.css instead of luz.css and put data-luz on the region. Level 2 for a project that already has a design has the lines to load.
How do I migrate a project to LUZ?
Migrate one page at a time, in five steps: the tokens with your brand, the old CSS in a layer before LUZ, a first region, the other pages one by one, and the old CSS removed. An AI does the mechanical work; you decide and check. Migrate a project to LUZ has the steps.