Styles
Components in this section are headless — they ship with no CSS. The elements-kit/ui/styles layer is an optional theme package on top. Skip it entirely and style with your own system, or import only the pieces you want.
Install
The token layer is color-agnostic: styles.css bundles every structural token (radius, space, scaling, typography, shadow, material, cursor) plus the unset reset in one import, and you add a neutral and an accent on top. Nothing is grayscale-by-default — you pick the palette.
// Structural tokens + reset (one import):import "elements-kit/ui/styles.css";
// A neutral scale (pick one):import "elements-kit/ui/styles/palette/slate.css";import "elements-kit/ui/styles/neutral/slate.css";
// An accent scale (any color):import "elements-kit/ui/styles/accent/neutral.css";Then activate them once on your root — accent/neutral only apply inside data-accent / data-neutral, so set them high up and every component below is styled with no per-element attributes:
<html data-accent="neutral" data-neutral="slate">// Or hand-pick individual token files instead of the barrel:import "elements-kit/ui/styles/typography.css";import "elements-kit/ui/styles/radius.css";import "elements-kit/ui/styles/unset.css";import "elements-kit/ui/styles/palette/blue.css";import "elements-kit/ui/styles/accent/blue.css";Theming via data attributes
Switches are data-* attributes on a root container — not classes you toggle, not a JS provider.
<body class="dark" data-radius="medium" data-scaling="md" data-material-background="translucent" data-accent="iris" data-neutral="slate"> ...</body>| Attribute | Values |
|---|---|
data-radius | none · small · medium · large · pill |
data-scaling | xs · sm · md · lg · xl |
data-material-background | solid · translucent |
data-accent | accent name — see accent list |
data-neutral | gray · mauve · olive · sage · sand · slate |
Light & dark
Color tokens flip when the container has .dark or .dark-theme. .light / .light-theme opt back in.
<html class="dark">...</html>Pair with createMediaQuery to follow the OS preference:
import { createMediaQuery } from "elements-kit/utilities/media-query";import { effect } from "elements-kit/signals";
const prefersDark = createMediaQuery("(prefers-color-scheme: dark)");effect(() => { document.documentElement.classList.toggle("dark", prefersDark());});Token reference
Override any token by re-declaring on :root or any container.
Color
Semantic — read these in your component CSS:
--color-background--color-overlay--color-material-solid,--color-material-translucent,--color-material,--backdrop-filter-material--color-surface--color-transparent--focus-1..--focus-12,--focus-a1..--focus-a12(alias of the active color)
Scales:
--neutral-1..--neutral-12plus--neutral-a1..--neutral-a12— the active neutral (set byneutral/<scale>.cssordata-neutral).--accent-1..--accent-12plus--accent-a1..--accent-a12— the active accent (set byaccent/<color>.cssordata-accent).--<color>-1..--<color>-12plus alpha — raw palette scales frompalette/<color>.css, e.g.--blue-9.--black-a1..--black-a12and--white-a1..--white-a12— alpha-on-color scales frompalette/black-alpha.css/palette/white-alpha.css(auto-imported bytheme.css).
Typography
--font-size-1..--font-size-9, --line-height-1..9, --letter-spacing-1..9, plus weights --font-weight-{light,regular,medium,bold}. Per-element families and overrides (--heading-*, --code-*, --strong-*, --em-*, --quote-*, --tab-*) live in typography.css.
Radius
--radius-1..--radius-6, --radius-pill, --radius-thumb. All scale with --radius-factor (driven by data-radius) and --scaling. radius.css sets the medium values at :root, so radius resolves even without a data-radius ancestor; set data-radius to override.
Spacing
--space-1 (4px) · --space-2 (8px) · --space-3 (12px) · --space-4 (16px) · --space-5 (24px) · --space-6 (32px) · --space-7 (40px) · --space-8 (48px) · --space-9 (64px). Each multiplied by --scaling.
Shadow
--shadow-1..--shadow-6. Light/dark variants ship out of the box, with a color-mix(in oklab, ...) upgrade for browsers that support it.
Cursor
--cursor-button, --cursor-checkbox, --cursor-link, --cursor-disabled, --cursor-menu-item, --cursor-radio, --cursor-slider-thumb, --cursor-slider-thumb-active, --cursor-switch. Defaults are platform-native; override per-element if you want a pointer button.
Unset native styles
Native form and text elements — <button>, <input>, <a>, <h1>–<h6>, lists, tables, <svg>, <img> — ship with browser defaults that bleed through component styles (gray button background, default font, list bullets, etc.). Apply class="unset" from unset.css to neutralise them in one line:
<button class="unset x-button" data-variant="solid" data-size="2"> Click me</button>unset uses :where(...) everywhere so it has zero specificity — your component classes (.x-button, .x-checkbox, …) and your own overrides always win.
Composite components in this kit (Button, future Checkbox, Switch, Listbox…) all rely on it. If you skip the import, native button rendering will leak through .x-button styling.
Customization
Override any token at any level. Container-scoped overrides win over :root:
.brand-card { --color-material-solid: var(--accent-2); --radius-3: var(--radius-5); background: var(--color-material); border-radius: var(--radius-3); padding: var(--space-4);}To swap the active accent, import a different accent/<color>.css:
// Replace this …import "elements-kit/ui/styles/accent/neutral.css";// … with this.import "elements-kit/ui/styles/accent/iris.css";Available accents: amber, blue, bronze, brown, crimson, cyan, gold, grass, green, indigo, iris, jade, lime, mint, orange, pink, plum, purple, red, ruby, sky, teal, tomato, violet, yellow. Plus semantic aliases that point at the active neutral or a fixed hue: neutral, success, error, warning, info.
Available neutral scales (pick one): gray, mauve, olive, sage, sand, slate.