Skip to content

Philosophy

The commitments that underpin this library: accessibility first, headless structure with documented hooks and opt-in theming tiers, all in plain language.

Accessibility first

Each component ships the ARIA roles, keyboard interactions, and focus management of its WAI-ARIA pattern by default. A Modal traps focus and restores it on close. A Combobox implements the full editable-combobox pattern with aria-activedescendant. Tabs handle arrow keys and roving tabindex. You cannot accidentally opt out by restyling.

The same applies to color. The token engine grades every text-on-surface and text-on-intent pairing against WCAG AA each time it generates a sheet. It warns by default; pass --strict and a failure stops the build. Contrast & Accessibility shows the live ratios.

Headless structure: override with documented hooks or snippets

Two routes get you past a decision the library made.

Documented hooks hand you the styling, and they cover most of what you will want to change. Every component ships a stable hz-* root class plus data-* and aria-* attributes for every variant and state, listed on the component's own page. They are API, so styling against them is supported. Every component also takes a class prop, merged onto its root, so restyling one instance never means wrapping it in a div.

Snippets are the escape hatch when styling is not enough and you need different markup. Pass a children or per-item snippet and render your own structure, without forking the component. The component keeps the parts that are easy to get wrong: roles, keyboard behavior, focus order.

Theming is opt-in, one tier at a time

On their own, components render with native element defaults. From there you add exactly as much as you want: the token sheet, then the reference theme, then token overrides of your own, then a generated sheet from your own config. Each tier is a superset of the one before. You can stop at any of them.

The example themes are the proof. Ocean redefines only tokens. Terminal imports no reference theme and styles the raw hooks from scratch. Both drive the same components, unchanged.

This site sits in the middle of that range. It runs the reference theme as shipped, plus a small sheet of class overrides that adds no palette and redefines no token. That sheet ships as the Docs example theme, so the styling you are reading is a tier you can import.

No bloat

Components carry structural CSS and nothing else. The reset, token sheet, reference theme and utility classes are separate imports, so you take only the ones you want. Icons come one glyph at a time rather than as a barrel.

The package has exactly one dependency: esm-env, a build-time helper that keeps the dev-only warnings working in every bundler instead of only Vite. It resolves to constants your bundler eliminates, so none of it reaches a production build. Svelte is the only peer dependency.

Shipping less is what makes the headless API and the theming tiers possible: no visual opinion for you to undo, and no layer you are forced to carry. These are the numbers for each layer, measured from the published package rather than estimated:

What a visitor downloads, piece by piece
What you importDownload (gzipped)UncompressedWhat that includes
Every component, minified52.4 kB241 kBA real build of the whole library, carrying only the few icons the components draw. Import three components and you ship a fraction of this.
Component structural CSS5.9 kB40.1 kBLayout and behavior only. No colors or other visual styling.
Design tokens3.2 kB12.0 kBThe --hz-* custom properties, also called CSS variables.
Reference theme50.2 kB146 kBOne import for the shared base sheet plus a sheet per component. Import single component sheets instead and you pay less.
Reset1.3 kB2.4 kBOptional. Structural only.
Utilities1.7 kB6.8 kBHelper classes you import only if you want them.
Everything, together115 kB448 kB

Gzipped is what crosses the network. Uncompressed is what the browser holds once it unzips the file.

The first row is the ceiling for JavaScript, not for the total: you will always add at least the token sheet on top of it. Your bundler keeps only the components you import, so three components cost a fraction of that row. The reference theme works the same way. You can import one component sheet at a time instead of the whole thing. The token, reset and utility figures are exact, because you import each of those sheets whole.

1 dependency (build-time only), with svelte as the only peer dependency.

Why the library is written in Svelte

Markup, script and styles sit in one file, so there is one place to look and one file to change. That suits a person reading the source and an agent editing it. Svelte also reports accessibility problems at compile time, so mistakes surface before review does.

Plain language, for you and your agents

The words a component ships by default (labels, error messages, empty states) are as much a part of its design as the markup. We write short sentences and skip jargon.

Form's error summary is the worked example. Its default title counts the errors: "There is a problem" for one, "There are 3 problems" for three. A screen-reader user gets the count before they start navigating the list.

These docs are indexed in llms.txt, generated from the same manifest that builds the navigation. The Agents page spells out the conventions so a coding agent can follow them literally. Prose that a person can read without decoding is prose an agent can act on without guessing.

The practice-level version of these commitments

The House rules turn these commitments into working conventions: one set, for you and your agents alike.

Where to go next

Getting Started

Install the package, import a component, and theme it in three steps.

Components

Every component, with its props, theme hooks and accessibility notes.

Theming

The layer model, and how far you can push it without forking the library.

Patterns

Full pages built from the library: homepage, product listing, checkout form, and more.

Agents

Point a coding agent at this library and keep its output correct.