Skip to content

Design Tokens

Karthik P edited this page Nov 3, 2026 · 2 revisions

Design tokens

This page explains what fiscalclock's design tokens are, how the files in design/tokens/ are organised and named, how to build and check them, how a website will use them, and lists every spacing, radius, border, breakpoint, layout and motion value.

Last reviewed: 1 Nov 2026

What this is and why it matters

A design token is a named design value, such as color.text.default or space.4, stored as data instead of being typed into each page. fiscalclock keeps every colour, size, spacing and timing value in a few JSON files, and a script turns them into CSS and checks them. The website, diagrams, social cards and these wiki pages then all use the same numbers, and a change made once reaches everything. The token files are the single source of truth: if a value appears anywhere else, it was copied from them (design/tokens/README.md).

Terms used on this page

Term Meaning
token A name plus a value plus a type, for example space.4 = 16 px, of type dimension.
alias A token whose value is another token, written in curly braces: {palette.teal.700}.
primitive A raw value with no meaning attached, such as a palette colour.
semantic token A token named for its job, such as color.bg.default, which aliases a primitive. Each theme points it at a different primitive.
composite token A token whose value bundles several parts; fiscalclock's composite tokens are text styles.
CSS custom property A variable in CSS, written --name and read with var(--name). The build writes one per token.
theme The light or dark set of semantic colours (Colour-System).
DTCG The W3C Design Tokens Community Group, which publishes the file format.

More terms are on the Design-Glossary.

The three tiers

Tier What it holds Where Changes with theme? Use it in pages?
Primitive palette.* colours, font.*, space.*, radius.*, border-width.*, breakpoint.*, layout.*, duration.*, easing.* primitives.tokens.json No Yes for sizes, spacing and motion; never for palette.*, which would bypass the theme switch
Semantic color.*: backgrounds, text, borders, accent, focus, status, code, diagram and logo roles semantic.light.tokens.json, semantic.dark.tokens.json Yes: same names, different values Yes, for every colour
Composite typography type.*: body, small, display, heading-1, heading-2, heading-3, code primitives.tokens.json No Yes, for every text style

Component tokens, such as a colour reserved for one button, do not exist. If a later phase needs them, they would alias semantic tokens, never primitives (token research, section 6.1).

Files

File What it holds
primitives.tokens.json The palette, font families, type scale, weights, line heights, spacing, radii, border widths, breakpoints, layout widths, durations, easing and the composite type.* styles. The same in both themes.
semantic.light.tokens.json Role-based colours for the light theme. Each is an alias of a palette colour and has a $description.
semantic.dark.tokens.json The same roles, with the same names, for the dark theme.
fiscalclock.resolver.json The resolver document: which files to load, in which order, for each theme.
contrast-checks.json The colour pairs checked against WCAG 2.2 in both themes on every build.
generated/ Generated files. Never edit them by hand.

The format

The files follow the DTCG Design Tokens Format Module 2025.10, with its Color and Resolver modules. It was published on 28 Oct 2025 as a "Final Community Group Report"; it is a stable community specification, not a W3C Standard (token research, section 1.1; format module).

A colour primitive, from primitives.tokens.json. The $type is set once on the palette group and inherited. A colour is an object: its colour space, its three components from 0 to 1, and a hex fallback; the build checks that hex matches the components.

{
  "palette": {
    "$type": "color",
    "teal": {
      "700": {
        "$value": {
          "colorSpace": "srgb",
          "components": [0.0431, 0.4196, 0.4118],
          "hex": "#0b6b69"
        }
      }
    }
  }
}

A semantic alias, from semantic.light.tokens.json:

{
  "color": {
    "$type": "color",
    "accent": {
      "default": {
        "$value": "{palette.teal.700}",
        "$description": "The one accent: links, the current item, the open end of an interval."
      }
    }
  }
}

A composite text style, from primitives.tokens.json:

{
  "type": {
    "$type": "typography",
    "body": {
      "$value": {
        "fontFamily": "{font.family.sans}",
        "fontSize": "{font.size.md}",
        "fontWeight": "{font.weight.regular}",
        "letterSpacing": { "value": 0, "unit": "px" },
        "lineHeight": "{font.line-height.body}"
      }
    }
  }
}

The JSON above is reformatted onto fewer lines; the values are copied from the files.

The resolver document

A resolver document says how to assemble the token files for each variant of the design. fiscalclock's loads the primitives first, then one theme modifier whose light and dark contexts each add one semantic file. Light is the default. From fiscalclock.resolver.json:

"modifiers": {
  "theme": {
    "contexts": {
      "light": [{ "$ref": "semantic.light.tokens.json" }],
      "dark": [{ "$ref": "semantic.dark.tokens.json" }]
    },
    "default": "light"
  }
},
"resolutionOrder": [
  { "$ref": "#/sets/primitives" },
  { "$ref": "#/modifiers/theme" }
]

The files keep strictly to the 2025.10 format so that an independent tool, Terrazzo, can read them unchanged as a cross-check (DD-016). No Terrazzo check is set up yet.

Naming rules

Rule Example Source
Each part of a name is lowercase ASCII letters and digits, with single hyphens between words. The build rejects anything else. color.bg.accent-subtle, font.line-height.body NAME pattern in build_tokens.py
Name semantic tokens by role, not by colour. color.status.danger, not color.red token research, section 6.1
Use a default leaf for the main member of a group. color.accent.default and color.accent.hover token research, section 6.2
Aliases use curly braces and point at a whole token. "{palette.neutral.900}" token research, section 6.2
Colours use colorSpace: "srgb" and always include hex. see the excerpt above token research, section 6.2
Every semantic token has a $description saying where it is used. "Page background." token research, section 6.2
Both themes define exactly the same semantic names. The build fails otherwise. design/tokens/README.md

CSS names and the --fc- prefix

The CSS name is --fc- followed by the token name with dots replaced by hyphens:

Token CSS custom property
color.text.default --fc-color-text-default
space.4 --fc-space-4
font.line-height.body --fc-font-line-height-body
type.body (composite) five properties: --fc-type-body-font-family, --fc-type-body-font-size, --fc-type-body-font-weight, --fc-type-body-letter-spacing, --fc-type-body-line-height

The prefix stops fiscalclock's variables from colliding with a framework's own, such as Tailwind's colour variables (DD-017).

Building and checking

Run from the repository root. The script uses only the Python standard library (DD-016).

Command What it does
python -I design/scripts/build_tokens.py Regenerates everything in design/tokens/generated/.
python -I design/scripts/build_tokens.py --check Builds in memory and fails if the committed files in generated/ are out of date. Prints "design tokens are up to date" on success.
python -I design/scripts/build_tokens.py --self-test Feeds the builder five broken inputs and checks that each is rejected: a circular alias, an alias to a missing token, an alias of the wrong type, themes with different token names, and a hex that does not match its components.
python -I design/scripts/check_brand.py --wiki <path to a wiki clone> Fails if a logo SVG, a design document or a wiki page uses a hex colour that is not in the palette, or a --fc- name that is not a token.
python -I design/scripts/contrast.py --self-test Checks the contrast formula against nine published reference values.

On every build the script also rejects any part of the format the files do not use, resolves every alias, and fails if any pair in contrast-checks.json falls below its minimum (design/tokens/README.md).

As of 1 Nov 2026 these commands are run by hand: no CI workflow in .github/workflows/ calls them yet. Running --check in CI, as the repository already does for other generated files, is Proposed (token research, section 6.4).

Outputs

File Use
generated/tokens.css CSS custom properties: primitives once on :root, light colours on :root, dark colours under prefers-color-scheme: dark and again under [data-theme="dark"] (DD-018). Semantic colours are written as var() references to primitives, so aliases stay visible.
generated/tokens.light.json, generated/tokens.dark.json Every token resolved to a plain CSS value, keyed by its CSS name, for tools that take values in configuration rather than CSS. The brand-asset script reads its colours from here (DD-023).
generated/contrast.md The contrast report: every checked pair's ratio in both themes. Colour-System shows a copy.

How a website will use them

No website stack has been chosen (DD-009), so the tokens assume none. The plan, summarised from design/tokens/README.md:

  1. Load tokens.css first, before any framework style sheet. It defines variables only and styles nothing.
  2. Map the framework's variables onto the tokens in one small style sheet, for example --vp-c-bg: var(--fc-color-bg-default); for VitePress. The token research, section 5 lists the variables and dark-mode selector of twelve docs frameworks.
  3. Set the theme on <html>. With no attribute the page follows the operating system; data-theme="light" or "dark" overrides it. Set it before the first paint and add <meta name="color-scheme" content="light dark">.
  4. For frameworks configured with values instead of CSS (Mintlify, Nextra, Furo), read tokens.light.json and tokens.dark.json at build time instead of copying hex values by hand.
  5. Copy breakpoints into media queries. CSS cannot read a custom property inside a media query, so the breakpoint.* values must be written into the site's media queries and kept in step.
@import "tokens.css";

body {
  background: var(--fc-color-bg-default);
  color: var(--fc-color-text-default);
  font-family: var(--fc-type-body-font-family);
  font-size: var(--fc-type-body-font-size);
  line-height: var(--fc-type-body-line-height);
}

Changing a token

  1. Edit the token file, then run python -I design/scripts/build_tokens.py.
  2. If the change touches a colour, the build reruns every contrast check. Fix a failure by changing the colour, never by removing the check.
  3. Run python -I design/scripts/build_tokens.py --check and python -I design/scripts/check_brand.py --wiki <path to a wiki clone>.
  4. Record the change and its reason in design/DECISIONS.md, and update the wiki pages that show the value (Colour-System, Typography, Layout-and-Spacing and this page).
  5. If it is a brand colour, regenerate the logo and card exports in assets/brand/ and update the Canva designs listed on Asset-Inventory.
  6. Commit the token files and the regenerated generated/ files together.

Reference: dimension and motion tokens

Every value below is copied from generated/tokens.css. Colour tokens are listed on Colour-System and type tokens on Typography. How to use the spacing and layout values is on Layout-and-Spacing.

Spacing

CSS custom property Token Value
--fc-space-0 space.0 0
--fc-space-1 space.1 4px
--fc-space-2 space.2 8px
--fc-space-3 space.3 12px
--fc-space-4 space.4 16px
--fc-space-5 space.5 24px
--fc-space-6 space.6 32px
--fc-space-7 space.7 48px
--fc-space-8 space.8 64px
--fc-space-9 space.9 96px

Radius

CSS custom property Token Value
--fc-radius-none radius.none 0
--fc-radius-sm radius.sm 3px
--fc-radius-md radius.md 6px
--fc-radius-lg radius.lg 8px
--fc-radius-xl radius.xl 12px
--fc-radius-full radius.full 9999px

Border width

CSS custom property Token Value
--fc-border-width-thin border-width.thin 1px
--fc-border-width-thick border-width.thick 2px

Breakpoints

A breakpoint is a minimum screen width at which the layout changes.

CSS custom property Token Value
--fc-breakpoint-sm breakpoint.sm 640px
--fc-breakpoint-md breakpoint.md 768px
--fc-breakpoint-lg breakpoint.lg 1024px
--fc-breakpoint-xl breakpoint.xl 1280px

Layout

CSS custom property Token Value
--fc-layout-prose layout.prose 720px
--fc-layout-sidebar layout.sidebar 264px
--fc-layout-toc layout.toc 224px
--fc-layout-page layout.page 1440px
--fc-layout-header layout.header 56px
--fc-layout-gutter-sm layout.gutter-sm 16px
--fc-layout-gutter-lg layout.gutter-lg 32px

Duration

CSS custom property Token Value
--fc-duration-fast duration.fast 120ms
--fc-duration-base duration.base 200ms

Easing

An easing curve sets how an animation speeds up and slows down.

CSS custom property Token Value
--fc-easing-standard easing.standard cubic-bezier(0.2, 0, 0, 1)

Where durations and easing are used is not decided yet. Any motion must stop when the reader asks for reduced motion (Accessibility).

Links

Clone this wiki locally