# Design Tokens: a Small Design System

Design tokens are a short list of custom properties for a site's colors, type sizes, spacing, corners and shadows, named by their job, so every component is built from the same decisions and one change restyles them all.

Journeyman · CSS · Updated November 1, 2026

## After this lesson you can

- Name design tokens by their job, such as --color-text or --space-3, instead of by their value
- Build components from tokens for color, type, spacing, corners and shadows, so one change restyles them all
- Register a token with @property to give it a type and a starting value

## Key takeaways

- A design token is one named design decision, such as the text color or the medium gap, kept as a custom property on :root.
- Name a token by its job (--color-text, --space-3), not its value (--dark-brown, --12px), so a new value never makes the name wrong.
- A spacing scale and a type scale are a few fixed steps, so every gap and font size on the page comes from one short list.
- When components read only tokens, changing one token restyles every component that uses it, on every page.
- currentColor reuses an element's own text color, and @property gives a token a type, so a value of the wrong kind can't break it.

The Daily Grind is opening a second shop, with a menu board, an online ordering page and a loyalty card, and the owner wants all of them to look like one brand. A designer hands over a brand kit: the exact coffee brown, the cream, a teal accent, the font sizes, how much space goes around things, how round the corners are. Your job is to turn that kit into CSS that every page uses, so no one ever eyeballs a color again.

That kit, written as custom properties, is a set of **design tokens**. You'll meet them on almost any team: a designer's file names colors like *Text / Primary* and *Spacing / 16*, Bootstrap's stylesheet starts with `--bs-primary` and `--bs-body-bg`, and the site you're reading this on runs on dozens of them. This lesson builds a small set for The Daily Grind, then looks at the real ones behind ask-codey.com.

## What a design token is

A sign shop that makes banners and truck wraps for a business keeps a sheet for each client: the logo color as an exact mix, the lettering font, how big the margins are. Every banner made from the sheet matches, whoever makes it. A **design token** is one line of that sheet: one design decision with a name. A set of them is the start of a **design system**, the rules a whole site is built from.

In CSS, tokens are custom properties, kept together on `:root`. Here are The Daily Grind's colors:

```css
:root {
  --color-bg: #fbf6ee;
  --color-surface: #fffdf9;
  --color-text: #2b1d14;
  --color-text-muted: #6b5848;
  --color-brand: #6f4e37;
  --color-on-brand: #fffaf2;
  --color-accent: #1f7a72;
  --color-border: #d9c7b3;
}
```

Eight colors, and that's the whole palette. A component never picks a color of its own; it picks one of these.

## Name them by their job

A coffee shop's schedule says who's the *opener* and who's the *closer*, not "Tom" and "Maya". When Maya takes the morning shift, the schedule still reads right. Tokens work the same way: name them by the **job** the value does, not by the value itself.

| Named by value | Named by job | Why the job name wins |
|---|---|---|
| `--dark-brown` | `--color-text` | In a dark theme the text turns cream, and `--dark-brown: #f3e9dc;` would be a lie. |
| `--teal` | `--color-accent` | When the accent becomes orange next spring, nothing needs renaming. |
| `--white` | `--color-surface` | The card background is the job; whether it's white or cream is the value. |
| `--16px` | `--space-4` | A step on a scale can change size; a name with a number of pixels can't. |

Two more habits help. Start every name with what kind of token it is (`--color-`, `--space-`, `--text-`, `--radius-`, `--shadow-`), so they sort together and you can guess them. And pair colors that sit on top of each other: `--color-on-brand` is the color of text that sits *on* the brand color, such as a button's label.

## Scales for spacing and type

A coffee shop sells three cup sizes, not thirty-seven. That's what a **scale** is: a few fixed steps, chosen once, that everything uses. A spacing scale and a **type scale** (the font sizes) keep a page calm, because every gap and every size comes from the same short list.

```css
:root {
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  --text-sm: 0.8rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.563rem;
  --text-2xl: 1.953rem;
}
```

The spacing numbers are steps, not sizes: `--space-4` is the fourth step, which happens to be `1rem` (16 pixels at the browser's normal text size). The skipped 5 and 7 leave room to add a step later without renaming anything. The type sizes go up by the same amount each time: each one is 1.25 times the one below it, rounded, so `1rem` times 1.25 is `1.25rem`, and times 1.25 again is about `1.563rem`. Going down, `1rem` divided by 1.25 is `0.8rem`.

## Corners and shadows

Corners and shadows get tokens too, and a token can hold a whole value, not only one number:

```css
:root {
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgb(43 29 20 / 12%);
  --shadow-md: 0 4px 12px rgb(43 29 20 / 16%);
}
```

`--radius-pill` is a radius so big that a button's ends become fully round, like a pill. `--shadow-md` holds a whole `box-shadow` value: how far down it falls, how soft it is, and its color, a see-through version of the text brown so the shadow looks warm, not gray.

## Components built only from tokens

Now the components use nothing but tokens. Here are a button and The Daily Grind's menu card:

```css
.btn {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-base);
}
.btn-primary {
  background-color: var(--color-brand);
  color: var(--color-on-brand);
}
.menu-card {
  background-color: var(--color-surface);
  padding: var(--space-6);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
```

Here's the payoff. When the owner asks for the darker roast, you change one line, `--color-brand: #5a3e2b;`, and every primary button, card heading and link on every page follows. When the designer decides the cards feel cramped, `--space-6: 2rem;` gives every card more room.

![The Daily Grind's menu card and buttons twice, before and after one token change: on the left in the original coffee brown, on the right in a darker roast, with every button and heading changed together](https://fsdtrainingmedia.blob.core.windows.net/public/images/2026/10/848e689e715f45168dd77593be986dd0.webp)

CSS has one token built in. The keyword `currentColor`, read "current color", means "this element's own text color". A badge that's outlined in its own color needs one token, not two:

```css
.badge {
  color: var(--color-accent);
  border: 1px solid currentColor;
}
```

The border follows the text. Change `--color-accent`, or give the badge another color on hover, and the border comes along.

**Try it.** In your `daily-grind` folder from *Custom Properties: Variables in CSS* (or a new HTML file with a button and a card), add the color, spacing and radius tokens above to `:root`, and rewrite the button and card rules with `var()` only. Then change `--radius-md` to `0` and `--radius-pill` to `0`: the whole page goes square-cornered from two lines.

## A real design system: ask-codey.com

The site you're reading works this way. Its design system's stylesheet starts with a `:root` block of tokens, and the site's buttons, cards and form fields are built from them. Here are some of its real lines:

```css
:root {
  --color-bg: #f6f5f3;
  --color-surface: #edebe7;
  --color-text: #2a2f38;
  --color-accent: #c0562c;

  --font-heading: "Barlow Condensed", system-ui, sans-serif;
  --font-body: "Barlow", system-ui, sans-serif;

  --space-2: 6.8px;
  --space-3: 10.2px;
  --space-4: 13.6px;
  --radius-md: 4px;
}
```

The design system first had a blue accent, `#5980a6`. To give the site Ask Codey's own look, a second `:root` block lower in the same file sets `--color-accent` to a rust orange, `#c0562c`. Everything that reads `--color-accent`, from buttons to links, turned rust orange, and not one component rule was touched. That one stylesheet reads `var()` more than a hundred times, and none of those lines had to change.

**Try it.** Open ask-codey.com, press F12, and select the `<html>` element in the Elements panel. In the Styles pane, find `--color-accent` and change it to `#1f7a72`. The whole site turns teal until you reload.

## A token that checks itself: @property

A parking garage has spots marked *Compact cars only*: the spot knows what kind of car belongs in it. An ordinary custom property knows nothing about its value. Set `--color-brand: 12px;` by mistake and CSS accepts it, then every button that reads it loses its background.

`@property`, read "at property", **registers** a custom property with a type. A registered one is also called a **typed custom property**.

```css
@property --color-brand {
  syntax: "<color>";
  inherits: true;
  initial-value: #6f4e37;
}
```

- **`syntax`** is the type, in quotes: `"<color>"`, `"<length>"` (such as `1.5rem`), `"<number>"` or `"<percentage>"`.
- **`inherits`** says whether children get the value, as an ordinary custom property's do (`true`), or not (`false`).
- **`initial-value`** is the starting value, used when nothing sets the token or when something sets it to a value of the wrong type.

Now `--color-brand: 12px;` can't break the buttons: `12px` isn't a color, so the token keeps a color it can use (the value from the element around it, or the starting value). A typed token can also glide smoothly from one value to another in a transition, where an ordinary custom property jumps straight to the new value. `@property` works in every current browser; it reached Firefox last, in July 2024, so a browser older than that ignores the registration and treats the token as an ordinary custom property.

## Check yourself

2 questions to try on the lesson page.

## So what

A design system isn't a big framework: it's a short list of named decisions that every component reads. Build your pages from tokens, and a rebrand, a new client's colors or a dark theme is a change to the list, not a hunt through every rule.

## Key terms

- Design token
- [Current color](https://www.ask-codey.com/glossary/current-color)
- [Typed custom property](https://www.ask-codey.com/glossary/typed-custom-property)
- [@property](https://www.ask-codey.com/glossary/property-css)

## Cheat sheets

- [Design Tokens: a Small Design System](https://www.ask-codey.com/cheat-sheets/css-design-tokens-cheat-sheet)

## Related

- [Custom Properties: Variables in CSS](https://www.ask-codey.com/lessons/css-custom-properties)
- [Light and Dark Themes with prefers-color-scheme](https://www.ask-codey.com/lessons/css-dark-mode)
- [Inheritance: What Children Get from Their Parents](https://www.ask-codey.com/lessons/css-inheritance)

Source: https://www.ask-codey.com/lessons/css-design-tokens
