Guide
Themes & color schemes
Every visual value in Kimen resolves from a design token — zero hardcoded values, enforced by lint. A theme is nothing but a set of token assignments, so re-theming is a stylesheet plus one attribute, with no component changes and no runtime code.
Tokens are layered (the Design tokens guide covers each layer in depth):
- Primitive — raw ramps (
--ki-color-brand-500,--ki-space-md). Never consumed by components directly. - Semantic — meaning, not appearance (
--ki-surface-s0…s5,--ki-text-high-em,--ki-outline-low-em). This is the layer a brand reassigns to re-theme everything. - Component — the public styling contract of each element
(
--ki-button-primary-neutral-rest-bg,--ki-button-md-height). Each component page lists its own under CSS custom properties.
Themes
Section titled “Themes”Onmars is the default theme — it is tokens.css itself, no attribute
needed. Material 3 ships as the reference second theme and proves in CI,
on every commit, that reassigning the token layers alone restyles every
component:
<link rel="stylesheet" href="@kimen/tokens/css" /><!-- opt-in second theme --><link rel="stylesheet" href="@kimen/tokens/css/material3" /><html data-ki-theme="material3">An unknown data-ki-theme value simply matches no selector: the document
stays on the default theme.
Color scheme
Section titled “Color scheme”Both themes follow the OS by default and can be forced either way:
<html data-ki-color-scheme="dark"> <!-- force dark --><html data-ki-color-scheme="light"> <!-- force light --><!-- no attribute: follows prefers-color-scheme -->This site’s theme toggle drives exactly this attribute, so every live demo on these pages follows it.
The page contract
Section titled “The page contract”A token stylesheet can only carry custom properties, so it cannot declare
color-scheme. Without that declaration the tokens flip to their dark
values for a visitor who prefers dark while the browser keeps painting a
light canvas, light scrollbars and light autofill — dark Kimen surfaces on a
white page.
Load the opt-in page contract alongside a theme to close that gap. It
declares color-scheme, paints the page from --ki-surface-s0 /
--ki-text-high-em, and sets the body family:
<link rel="stylesheet" href="@kimen/tokens/css" /><link rel="stylesheet" href="@kimen/tokens/css/base" />It publishes no tokens of its own — it only consumes them, so it never
widens the token surface. If your page is deliberately single-scheme, set
data-ki-color-scheme and both the tokens and the browser follow that
choice.
Skipping it is supported in the light scheme. In the dark scheme the number
is measured rather than estimated: on a page loading only the token
stylesheet, with the visitor’s OS set to dark, 14 of 29 components render
text that falls below 4.5:1 — most of it at 1.00:1, white on white.
Nothing is broken in the components; the user agent simply keeps a light
canvas because color-scheme is still normal. So: load the contract, or
pin the page with data-ki-color-scheme="light". Doing neither is the one
combination the library cannot render legibly.
Surfaces
Section titled “Surfaces”The semantic surface scale in the current scheme — each swatch reads its color from the live token, so flipping this site’s theme toggle restyles them:
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;"><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s0); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s0</div><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s1); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s1</div><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s2); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s2</div><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s3); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s3</div><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s4); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s4</div><div style="inline-size: 5.5rem; block-size: 3.5rem; display: grid; place-items: center; border-radius: 0.5rem; border: 1px solid var(--ki-outline-low-em); background: var(--ki-surface-s5); color: var(--ki-text-med-em); font-family: var(--ki-typography-family-mono); font-size: 0.75rem;">s5</div></div>Customization order
Section titled “Customization order”When you need to customize a component, reach for surfaces in this order —
if a consumer would ever need !important, that is an API bug to report:
- Tokens — reassign at
:root(theme-wide) or on a subtree. ::part()— style exposed parts for one-off structural tweaks.- Slots — replace content entirely.