The two-layer token model
A semantic color token system has two distinct layers. The primitive layer contains the full color vocabulary — every shade available, named by color and lightness step. Primitive names are descriptive but not meaningful: --color-teal-300 is a specific lightness of teal, nothing more. The semantic layer maps intent onto primitives — --color-action-default might reference --color-teal-300 in the light theme and --color-teal-400 in the dark theme. Components consume only semantic tokens. This separation is the key insight: components become indifferent to the underlying color values because they only care about semantic intent. A button that uses --color-action-default will look correct in any theme as long as the token is mapped correctly.
Common semantic categories
A minimal semantic layer needs five categories to cover most interface needs. Surface tokens describe backgrounds at various depths: surface-primary for page background, surface-secondary for card backgrounds, surface-elevated for floating layers. Border tokens describe edge treatments: border-default, border-subtle, border-focus. Text tokens describe content: text-primary, text-secondary, text-placeholder, text-inverse. Interactive tokens describe action states: action-default, action-hover, action-pressed, action-disabled. Status tokens describe system feedback: status-success, status-warning, status-error, status-info. The Nocturne Tech collection maps cleanly onto this framework — every color in the set has an obvious semantic role, which is why it exports so cleanly as a token set.
Starting with semantic tokens in a new project
The fastest way to adopt semantic tokens is to start from a well-structured collection and assign semantic names to the colors you already know you need. ColorArchive packs include pre-named CSS custom properties organized by semantic role — the Complete Archive ships with a full token set covering surface, border, text, interactive, and status categories for both light and dark themes. The hardest part of semantic naming is resisting the temptation to use color names — calling a token --color-blue-hover instead of --color-action-hover defeats the purpose. A useful rule: if you can replace a semantic token name with a color name and the name still makes sense, it is not semantic enough. Surface, action, status, border, and text are semantic. Blue, coral, and amber are not.