# Turn a Dark Mode Color Palette Into Reusable Design Tokens

[Open the live Fudge conversation](https://design.withfudge.com/share/turn-dark-mode-color-palette-into-reusable-design-tokens)

Last updated: 2026-08-25

A dark mode palette becomes reusable design tokens when each color has a job, a contrast role, and a consistent name. Start by sorting the colors into surfaces, text, actions, borders, and states, then test those roles across the components where they will appear.

## Use roles, not color names

Avoid token names such as `dark-green-700` or `gray-900` when the goal is a maintainable interface. Those names describe a color value, but not what the color does. Prefer names that remain useful if the palette changes:

- `--color-surface-page`: the main page background
- `--color-surface-raised`: cards, panels, and menus above the page
- `--color-surface-inset`: inputs, code areas, and recessed sections
- `--color-text-primary`: headings and essential copy
- `--color-text-secondary`: supporting copy and metadata
- `--color-text-muted`: low-priority labels, used carefully
- `--color-action-primary`: main buttons and links
- `--color-action-primary-hover`: the interaction state for that action
- `--color-border-subtle`: quiet separation between nearby surfaces
- `--color-border-strong`: stronger outlines and focus treatment

The examples below include a green-heavy palette with values such as `#14532d`, `#233f2a`, `#2c7a4a`, and `#799c92`. Treat those as observed starting points, not as a finished token system. Compare the examples first, then decide which value belongs to each role in your own interface.

A practical first pass might look like this:

```css
:root {
  --color-surface-page: #102016;
  --color-surface-raised: #233f2a;
  --color-surface-inset: #1b2d20;
  --color-text-primary: #f2f7ef;
  --color-text-secondary: #b7c8b7;
  --color-text-muted: #799c92;
  --color-action-primary: #2c7a4a;
  --color-border-subtle: #35513b;
}
```

## Captured pages

[![Pangram AI Detector](https://pin.fontofweb.com/9687?format=jpg)](https://design.withfudge.com/share/pin-9687)

[Pangram AI Detector](https://design.withfudge.com/share/pin-9687)

## Colors

- `#496c10`
- `#15502e`
- `#14532d`
- `#233f2a`
- `#254f1a`
- `#1e6f30`
- `#295631`
- `#556659`
- `#4a5a4a`
- `#2c7a4a`
- `#2a966f`
- `#799c92`

## Build a dark mode token map

Give every extracted color a decision before adding it to code. A simple worksheet helps:

| Observed value | Proposed role | Where it appears | Keep, adjust, or replace |
| --- | --- | --- | --- |
| `#233f2a` | raised surface | card or panel | keep if it separates from the page |
| `#14532d` | dark accent surface | badge or selected state | check text placed on top |
| `#2c7a4a` | primary action | button or active control | test hover and disabled states |
| `#799c92` | muted text | metadata or helper copy | verify it is not too faint |

This prevents palette drift. Two colors may look different but serve the same role, while a single color may be reused incorrectly on both a background and a text label. A token should describe a repeated design decision, not merely preserve every sampled pixel.

## Check the system across components

Test the token map against a small component checklist:

1. Page background with primary text.
2. Raised card with primary and secondary text.
3. Primary button with its label, hover, focus, and disabled states.
4. Text input with placeholder, entered text, border, and focus ring.
5. Link on both the page surface and a raised surface.
6. Error, warning, success, and selected states.
7. Divider and border combinations beside adjacent surfaces.

If a color only works in one component, keep it as a component token rather than forcing it into a global role. For example, `--button-primary-background` can refer to `--color-action-primary` while allowing a button-specific hover or pressed value.

## Export a usable token file

Keep the source palette and semantic mapping separate. The source list records what you observed; the semantic layer records how your product uses it. That makes future updates easier and preserves a clear audit trail.

Use a compact structure such as:

```json
{
  "color": {
    "surface": { "page": "#102016", "raised": "#233f2a" },
    "text": { "primary": "#f2f7ef", "secondary": "#b7c8b7" },
    "action": { "primary": "#2c7a4a" }
  }
}
```

Before shipping, compare each token where it is actually used, not only as isolated swatches. Fudge's color inspection can help identify roles and usage across a captured page, while the observed green examples below can serve as a visual reference for the direction. Then adjust the values and keep the final names in your codebase's token file.

## Use this in your AI agent

> Turn this captured dark mode palette into semantic design tokens. Group colors into page, raised, and inset surfaces; primary, secondary, and muted text; actions; borders; focus; and status states. Show the observed hex values, assign each a proposed role, flag weak combinations for contrast testing, and return CSS custom properties plus a JSON token map. Keep source colors separate from semantic tokens and do not invent website facts.
>
> [Install Fudge for your AI agent](/mcp)

---

Use the same semantic names in both themes and change the values underneath them. For example, keep `--color-surface-page`, `--color-surface-raised`, `--color-text-primary`, and `--color-action-primary` stable across themes. Do not create separate names such as `--dark-green-900` and `--light-gray-100` unless they are internal palette steps that never reach components.

A useful structure has three layers:

1. Primitive values, such as `--green-900` or `--neutral-50`.
2. Semantic roles, such as `--color-surface-page` and `--color-text-primary`.
3. Component roles, such as `--button-primary-background` or `--input-border-focus`.

The semantic layer is the contract used by most components. The component layer handles exceptions without weakening the global system. Keep the names based on purpose, because a dark theme may use a pale green for a button label while a light theme uses a dark green for the same role.

---

Create a small token test page before applying the map throughout the product. Show the main surfaces, headings, body copy, links, buttons, inputs, cards, alerts, selected controls, focus rings, and disabled states together. This exposes collisions that isolated swatches hide.

For each combination, record the foreground role, background role, intended use, and whether the pairing passes your chosen contrast requirement. Check normal text separately from large text and icons. Also test hover, pressed, and focus states, since a color that works at rest may become too faint or too bright during interaction.

Finally, remove one-off hex values from components and replace them with semantic references. Keep the primitive palette in one file, the semantic mapping in another, and document any deliberate exceptions next to the component that owns them.

## Related questions

- [Turn Text and Background Contrast Into Reusable Design Tokens](/share/turn-text-and-background-contrast-into-reusable-design-tokens)
- [Turn Accessible CTA Colors into Reusable Design Tokens](/share/turn-accessible-cta-colors-into-reusable-design-tokens)
- [Turn a Website Color Palette Into Reusable Design Tokens](/share/turn-website-color-palette-into-reusable-design-tokens)
- [Inspect a Website's Gradient Palette and Color Use](/share/tool-for-inspecting-website-gradient-palette-on-a-website)
