Design tokens

Design tokens are named design decisions (color, space, radius, typography, motion) expressed as data and usually published as CSS custom properties. In Sometic, @sometic/theme owns tokens, CSS variable generation, system preference handling, contrast helpers, and a theme controller.

Overview

Styling resolvers (@sometic/styling) merge class and style maps. They do not own a token graph. Theme sits beside styling:

text
@sometic/theme          → tokens, CSS variables, theme store / controller
@sometic/styling        → classes, styles, slots, state attributes
Adapters / your CSS       → apply variables and class recipes

Import surfaces (prefer subpaths for size):

NeedImport
Theme controller@sometic/theme
Token helpers@sometic/theme/tokens
CSS variable helpers@sometic/theme/css-variables
Contrast helpers@sometic/theme/contrast
System preference@sometic/theme/system
Optional presets@sometic/theme/presets

Presets stay on a subpath so the root entry respects size budgets. See Tree shaking.

Runtime theme controller

Create an explicit controller (no import-time browser access):

ts
import { createThemeController } from "@sometic/theme";

const theme = createThemeController({
    mode: "system", // or "light" | "dark" | custom scheme id
    persist: true,
    // storage adapter supplied in browser code
});

theme.subscribe((snapshot) => {
    // apply dataset / class on documentElement in an effect
});

SSR: construct and subscribe in client entry or effects. Do not touch localStorage or matchMedia at module top level.

Defaults: CSS variable prefix and storage keys default to sometic (see Beta maturity for identity notes).

CSS variables

Tokens typically publish as custom properties on a scope (document root or a subtree):

css
:root {
    --sometic-color-bg: #ffffff;
    --sometic-color-fg: #0a0a0a;
    --sometic-radius-md: 0.5rem;
}

[data-theme="dark"] {
    --sometic-color-bg: #0a0a0a;
    --sometic-color-fg: #f5f5f5;
}

Components and your CSS consume variables without knowing which theme file wrote them:

css
[data-slot="root"] {
    background: var(--sometic-color-bg);
    color: var(--sometic-color-fg);
    border-radius: var(--sometic-radius-md);
}

You can also pass instance overrides through cssVariables on styleable components. See Styling slots.

Contrast helpers

Theme exposes contrast helpers so accessible pairs can be checked when you author or switch tokens. Use them when generating themes or validating brand palettes; do not treat them as a substitute for real contrast QA on composed UI.

Modes of consumption

ModeHow tokens reach the page
Plain CSSHand-authored variables; optional controller for mode class
Runtime switchingController updates mode; CSS responds to data-theme / class
Tailwind / BootstrapMap utilities to CSS variables; frameworks stay consumer-owned

Guides: Tokens, CSS variables, Runtime switching, Plain CSS, Tailwind, Bootstrap.

Theme store relationship

Theme preferences often persist through store-backed persistence (same philosophy as @sometic/store/persistent): explicit adapters, disposable controllers, SSR-safe construction. See Theme store and Theming.

When to use / when not

Use when you need shared light/dark (or multi-brand) tokens across React, Vue, and elements, with optional persistence and system preference.

Do not use when a single static stylesheet is enough and you never switch themes. You can still use styling slots and state attributes without @sometic/theme.

FAQ

Is a default visual theme required? No. Components work unstyled. Tokens are optional.

Can I rename the CSS prefix? Beta defaults to sometic. Treat legacy names as unsupported; see release notes on Beta maturity.

Where do I put brand colors? In your token set or presets, then publish as CSS variables. Keep secrets and environment-specific URLs out of token packages.

Does theme replace component classes? No. Tokens supply values; slots still map structure to your recipes.