Design tokens
Summary
Soma's design tokens are the --soma-* CSS custom
properties defined in src/css/_tokens.scss, the
single styling contract of the library: every component reads
them, themes override the color-derived
half, and your own CSS can consume them to stay consistent with
whatever theme is active. Static scales (type, spacing, radius,
density) sit on :root; everything color-derived
lives once per theme block.
Using tokens
Read a token anywhere with var(…):
.my-callout {
padding: var(--soma-space-3) var(--soma-space-4);
border-inline-start: 3px solid var(--soma-color-primary);
border-radius: var(--soma-radius-md);
background: var(--soma-color-primary-subtle);
color: var(--soma-color-primary-subtle-fg);
}
That exact rule, live (it follows the active theme):
You can override a token on any subtree. Custom properties cascade, and
var(…) resolves at the element that uses it, so
every Soma component (and any of your own CSS reading the token)
inside that subtree follows, while everything outside keeps the
theme value:
[data-my-area] {
--soma-color-link: #b3382c;
}
Themes are exactly this mechanism at page scale: a
[data-soma-theme='name'] block overriding tokens for
the subtree it is set on; see the
custom-theme recipe.
Reference
The complete token surface, grouped as in
src/css/_tokens.scss. The Value
column is resolved live with getComputedStyle from
the loaded stylesheet, so it always shows the active
theme and density. Switch either (corner buttons, or the
Themes page) and the tables re-resolve.
Type
| Token | Value (live) | Preview / notes |
|---|
Spacing
4 px base with a generous top end — Soma's look is whitespace-heavy.
| Token | Value (live) | Preview / notes |
|---|
Radius
| Token | Value (live) | Preview / notes |
|---|
Density
Vertical rhythm tokens switched by data-soma-density
on <html> (comfortable is the default). The
values below are live, so cycling density with the corner button
updates them. Full mode table on the
Density page.
| Token | Value (live) | Preview / notes |
|---|
Motion
One duration/easing vocabulary for every transition: fast for hovers and chevrons, base for overlay enters and toasts, slow for drawers and larger movements. Ambient loops (spinner, skeleton shimmer) sit deliberately outside the scale.
| Token | Value (live) | Preview / notes |
|---|
Everything below is color-derived and lives once per theme block: the theming surface.
Brand
| Token | Value (live) | Preview / notes |
|---|
Page & text
| Token | Value (live) | Preview / notes |
|---|
Surfaces
| Token | Value (live) | Preview / notes |
|---|
Shadows
Soft, blue-grey tinted elevation. In the dark theme the overlay
tiers (-2/-3) add a 1px border-strong edge
ring: black shadows barely read on a dark page, so elevation leans
on the surface ladder instead. In the high-contrast theme all three
become outline rings so white surfaces stay delineated on the white
page.
| Token | Value (live) | Preview / notes |
|---|
--soma-shadow-1resting card
--soma-shadow-2dropdown, popover
--soma-shadow-3dialog
Semantic sets
Five sets (neutral, success,
warning, danger, info),
each six slots: the solid fill, its -hover and
-active state shades (derived per theme via
color-mix), text on the fill (-fg),
the tinted variant (-subtle), and text on the tint
(-subtle-fg). Fills and their
paired foregrounds are contrast-audited together; always use the
matching -fg, never a hand-picked color.
| Token | Value (live) | Preview / notes |
|---|
The theming surface
The reference above splits in two. The static
scales (type, spacing, radius, density) are
theme-neutral: declared once on :root, identical in
every theme, so layout and rhythm never shift when the theme
changes. Everything color-derived (brand,
page & text, surfaces, shadows and the five semantic sets,
plus the color-scheme declaration and the optional
--soma-link-decoration) is the theming surface:
the dark and high-contrast blocks
restate exactly that set, and a custom theme overrides any
subset of it (anything omitted falls back to the light values on
:root).
| Tier | Groups |
|---|---|
| Theme-neutral (never overridden) | Type (--soma-font-*, --soma-line-height*), spacing (--soma-space-*), radius (--soma-radius-*), density (--soma-density-*, switched by data-soma-density, not by themes). |
| Theming surface (restated per theme) | Brand (--soma-color-primary*, -link, -focus, --soma-focus-ring, --soma-link-decoration), page & text (--soma-color-page, --soma-page-background, --soma-color-text*, --soma-color-border*), surfaces (--soma-surface*), shadows (--soma-shadow-*), the five six-slot semantic sets (fill, -hover, -active, -fg, -subtle, -subtle-fg), and color-scheme. |
The worked [data-soma-theme='acme'] recipe (which
tokens to supply and what falls back) is on the
Themes page. House law
for contributors: any new color-derived token must be
declared in all three theme blocks in _tokens.scss;
the per-theme axe sweep fails CI if one is missing.