---
title: "Color tokens"
description: "Primitives, the color system, composed colors and colors with opacity."
requested_language: nl
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/nl/design/color-tokens
version: unreleased (main)
docs_index: https://docs.systhema.app/nl/llms.txt
---
> This page isn't translated yet. Showing English.


Color tokens come in two tiers. Primitives are raw color scales; the color system maps them to semantic roles per color mode. Components reference the color system, so a change to a primitive or a mode reaches every component that uses it.

## Primitives

The `colorPrimitives` collection holds the raw scales: the `base` and `theme` ramps, including the endpoint stops `0` and `1000` as well as `50` through `950`. Your export can add brand scales. Generate a ramp from one brand color with [Color palettes](https://docs.systhema.app/nl/design/palettes.md).

## Color system

The `colorSystem` collection has one mode per theme (`default` and `dark` with the default tokens). Each mode maps semantic roles such as `foundations.bg` or `foundations.text` to primitives, and component tokens reference those roles. A region switches modes with the `theme` prop or `data-theme`; see [Themes and backgrounds](https://docs.systhema.app/nl/concepts/themes.md).

## References and overrides

A DTCG color leaf carries `$type: 'color'` and either a structured sRGB `$value` or an alias such as `{primitives.theme.600}`. The extractor preserves references for the CSS pipeline, so semantic tokens can point to foundations and component states can point to those semantic roles. With the default tokens, `layout.bg` has `main` and `alternative`, typography has `label`, `heading` and `body`, and buttons have `primary` and `secondary` roles.

Use the semantic role for UI styling rather than choosing a ramp stop in each component. For example, `bg-layout-main` and `color-body` follow the current color system. A raw palette utility such as `bg-theme-600` keeps the same primitive in both modes.

Override a runtime value in `systhema.config`, then run `pnpm systhema-core sync`:

```ts
import type { SysthemaConfig } from '@systhemaui/core'

const config: SysthemaConfig = {
  customTokens: {
    colorPrimitives: { theme: { 600: '#2563eb' } },
    colorSystem: { default: { foundations: { decorative: '{primitives.theme.600}' } } },
  },
}

export default config
```

The override is a plain runtime value, without `$type`, `$value` or the primitive file's `primitives` wrapper. See [Custom tokens](https://docs.systhema.app/nl/design/custom-tokens.md).

## Colors with opacity

Figma's "control opacity at scale" lets a color variable be _another_ variable at N% opacity. Its export writes the flattened result in `$value` and keeps the two parts in `$extensions["com.figma.composedColor"]`. Core reads the parts, so the token stays a live reference instead of a frozen hex:

```json
{
  "$type": "color",
  "$value": { "colorSpace": "srgb", "components": [0, 0, 0], "alpha": 0.25, "hex": "#000000" },
  "$extensions": {
    "com.figma.composedColor": {
      "colorArg": { "type": "alias", "alias": { "targetVariableName": "foundations/text" } },
      "opacityArg": { "type": "number", "value": 25 }
    }
  }
}
```

```css
--color-foundations-text-muted: color-mix(in srgb, var(--color-foundations-text) 25%, transparent);
```

Change `foundations.text`, or switch to the dark mode, and every color composed from it follows. A flattened `#00000040` would not.

The opacity can be a variable of its own, which is how one "muted" percentage drives a whole family. Point the composed color's opacity at a `number` variable (`foundations/mutedOpacity`, `$value: 25`) and core emits the reference, multiplied into a percentage:

```css
--color-foundations-muted-opacity: 25;
--color-foundations-text-muted: color-mix(
  in srgb,
  var(--color-foundations-text) calc(var(--color-foundations-muted-opacity) * 1%),
  transparent
);
```

The color side can point anywhere: a `colorPrimitives` stop becomes `var(--color-theme-600)`, a `tailwindcss` palette entry becomes a `theme('colors.black')` call.

Scope the opacity variable to Opacity in Figma, or use it as a same-collection composed-color opacity argument. A `number` variable is exported as a dimension (`25px`) unless its only Figma scope is `Opacity`, or a composed color in the same collection uses it as its opacity argument. A number that is neither reaches CSS as `25px` and the `calc(… * 1%)` around it is invalid.

Nothing changes for a project whose tokens carry no composed colors: a color with a literal alpha still emits the 8-digit hex.

If the composed-color extension has no color alias or usable opacity argument, extraction falls back to the resolved `$value`. Cross-collection opacity aliases need the target variable's correct Opacity scope; the automatic unitless detection applies to same-collection references.

## Generated tables

The [color reference](https://docs.systhema.app/nl/reference/tokens/colors.md) lists the shipped primitive and semantic values with their CSS variables. Use it to check exact paths and default values. The [token collections](https://docs.systhema.app/nl/design/token-collections.md) page explains how modes reach the runtime tree.
