Tokens ​

Design tokens in Sometic are plain nested maps: categories (color, space, …) → keys → string or number values. @sometic/theme/tokens gives you typed helpers to define, merge, and look up those maps. The theme controller turns the active theme’s tokens into CSS variables.

Overview ​

Tokens are data, not CSS frameworks. You own the scale names; Sometic does not force a Material or Bootstrap palette. Presets ship a small color / space / radius set for demos and tests only.

When to use ​

  • Defining brand and semantic scales for runtime themes
  • Merging base + brand + environment layers
  • Looking up a single token by "category.key" path

When not to use ​

  • Class name merging → @sometic/styling
  • Full compile-time token pipelines → Style Dictionary (or similar), then defineTokens on the output
  • Switching themes at runtime → Themes and Runtime switching

Installation ​

See Installation. Import tokens from the subpath:

ts
import {
    defineTokens,
    mergeTokens,
    resolveToken,
    type ThemeTokens,
    type TokenScale,
    type TokenValue,
} from "@sometic/theme/tokens";

Usage ​

Define a token map ​

ts
import { defineTokens } from "@sometic/theme/tokens";

const brandTokens = defineTokens({
    color: {
        bg: "#ffffff",
        fg: "#111827",
        primary: "#2563eb",
        danger: "#dc2626",
    },
    space: {
        1: "0.25rem",
        2: "0.5rem",
        3: "0.75rem",
        4: "1rem",
    },
    radius: {
        sm: "0.25rem",
        md: "0.5rem",
        lg: "0.75rem",
    },
});

defineTokens returns the same object with a preserved generic type. It does not mutate or validate color formats.

Merge layers ​

Later layers win per category key. null / undefined layers are skipped.

ts
import { defineTokens, mergeTokens } from "@sometic/theme/tokens";

const base = defineTokens({
    color: { bg: "#ffffff", fg: "#111827", primary: "#2563eb" },
});

const darkOverrides = defineTokens({
    color: { bg: "#0b1220", fg: "#f9fafb" },
});

const darkTokens = mergeTokens(base, darkOverrides);
// color.bg → "#0b1220", color.primary still "#2563eb"

Resolve a path ​

Paths use a single . between category and key ("color.primary"). Nested paths beyond one level are not supported in this helper.

ts
import { resolveToken } from "@sometic/theme/tokens";

resolveToken(brandTokens, "color.primary"); // "#2563eb"
resolveToken(brandTokens, "space.2"); // "0.5rem"
resolveToken(brandTokens, "missing.key"); // undefined
resolveToken(brandTokens, ""); // undefined

Feed tokens into a theme definition ​

ts
import { defineTokens } from "@sometic/theme/tokens";
import type { ThemeDefinition } from "@sometic/theme";

const tokens = defineTokens({
    color: { bg: "#fff", fg: "#111", primary: "#2563eb" },
});

const brandLight: ThemeDefinition = {
    id: "brand-light",
    colorScheme: "light",
    tokens,
};

How it works ​

ConceptShape
TokenValuestring | number
TokenScaleReadonly<Record<string, TokenValue>>
ThemeTokensReadonly<Record<string, TokenScale>>

Categories are one level deep. Keys may be numeric-looking strings ("1", "2") as with space scales. Numbers are allowed as values (for example raw font weights); CSS variable serialization stringifies them through styling’s normalizers.

Token → CSS variable flattening (default prefix sometic):

Token pathCSS variable
color.primary--sometic-color-primary
space.2--sometic-space-2
radius.md--sometic-radius-md

Non-alphanumeric characters in category/key are replaced with - during flattening. See CSS variables.

API ​

defineTokens(tokens) ​

ParameterTypeDescription
tokensT extends ThemeTokensToken map

Returns: T (identity helper for inference).

mergeTokens(...layers) ​

ParameterTypeDescription
layersArray<ThemeTokens | null | undefined>Layers to merge left → right

Returns: ThemeTokens (new object; shallow-per-category merge).

resolveToken(tokens, path) ​

ParameterTypeDescription
tokensThemeTokensSource map
pathstring"category.key"

Returns: TokenValue \| undefined.

Edge cases ​

CaseBehavior
Empty path / no . / trailing .resolveToken → undefined
Missing category or keyundefined (no throw)
mergeTokens() with no layersEmpty object
null / undefined layerSkipped
Overlapping keysLast layer wins
Invalid hex in a color tokenStill stored; contrast helpers may reject later
Deep nesting (color.brand.primary)Not a supported resolveToken path; use flat category keys

FAQ ​

Are tokens CSS variables already? ​

No. Tokens are JS data. The controller (or tokensToCssVariables) produces the variable map.

Can I reference another token inside a value? ​

Not as a built-in alias language. Put the final CSS value (or var(--…)) string yourself if you need references.

Do presets use defineTokens? ​

Yes. Import lightTokens / darkTokens from @sometic/theme/presets or copy the pattern for your brand.

Where do preferences persist? ​

Token maps are not persisted. Preferences (mode, theme id, …) can persist via the controller; see Theme store and Store.

Why not put tokens in @sometic/styling? ​

Architecture: styling resolves classes and styles; theme owns tokens and generation.