Colour
Colour tokens give every surface, text, border, icon and status in Nuton a named role. Use the semantic tokens in product code; the palette underneath is what they point to.
Beta. Semantic colours come from the Figma Mapped collection and the palette from Foundation. The three action-primary tokens (D13) and the focus-ring colour (C5) are interim, waiting for Figma variables. Stable needs the code-only dark values (C69) approved and a Dark mode added in Figma (D8, TD-11, FC-38), a design decision on the input-border contrast (C7), and the remote-library links re-pointed to local Foundation (TD-02).
Overview
Colour in Nuton has two tiers. Semantic tokens name a job (secondary text, a soft border, an error fill); the palette holds the raw steps they point to. Components and product code use semantic tokens only (D3), so a change of value, or a future dark theme, happens in one place.
| Role | Prefix | Use for | Tokens |
|---|---|---|---|
| Background | --nu-bg-* | Page, card and control fills. | |
| Text | --nu-text-* | Text, by emphasis (D7). | |
| Stroke | --nu-stroke-* | Borders and dividers. | |
| Icon | --nu-icon-* | Icon fills, matched to the text beside them. | |
| Action | --nu-action-* | The neutral main action (D13). | |
| State | --nu-state-* | Status, feedback and the interactive blue. | |
| Overlay | --nu-overlay-* | Scrims behind dialogs and sheets. | |
| Illustration | --nu-illustration-* | Greys for empty-state artwork (TD-08). |
Swatches render the live CSS variables. The State row shows each family's Base step; every family also has Dark, Light and Lighter.
Roles on a real surface
- Photosynthesis basicsCompleted
- Cell divisionIn progress
- Quiz: the cell
<ul className="lesson-list"> {/* bg-white, stroke-soft-200 border */}
<li>
<div>
<span style={{ color: 'var(--nu-text-strong-950)' }}>Photosynthesis basics</span>
<span style={{ color: 'var(--nu-text-sub-600)' }}>Biology 101 · 12 min</span>
</div>
<span style={{ background: 'var(--nu-state-success-lighter)', color: 'var(--nu-state-success-dark)' }}>
Completed
</span>
</li>
<li>
…
<Button size="sm">Continue</Button> {/* action-primary-bg / -fg */}
</li>
</ul>Dark mode
Nuton has three themes: Light, Dark and System (follows the device). Pick one by setting data-theme on <html>, or on any element to theme just that part. With no attribute you get Light. The Theme toggle sets it for the whole page and remembers the choice.
<html data-theme="system"> <!-- follows the device -->
<html data-theme="dark">
<section data-theme="dark"> <!-- one always-dark area -->The dark values are code-only (D8, C69): Figma has one colour mode, so each palette ramp is reversed (Gray-50 swaps with Gray-950, and so on for every hue) and the semantic tokens keep pointing at the same palette names. Components need no dark rules. Shadows get stronger, and translucent black and white tints swap. Every required text and non-text pair is re-checked in dark by npm run tokens:check. Dark needs design approval, and a Dark mode on the Mapped collection (FC-38), before Stable.
| Token | Light | Dark |
|---|---|---|
--nu-bg-white | #ffffff | #171717 |
--nu-bg-weak-50 | #f7f7f7 | #1c1c1c |
--nu-bg-soft-100 | #f5f5f5 | #262626 |
--nu-bg-strong-950 | #171717 | #ffffff |
--nu-text-strong-950 | #171717 | #ffffff |
--nu-text-sub-600 | #5c5c5c | #d1d1d1 |
--nu-stroke-soft-200 | #ebebeb | #333333 |
--nu-state-primary-base | #3559e9 | #97baff |
--nu-state-error-base | #e93544 | #ff6875 |
--nu-state-success-base | #1daf61 | #3ee089 |
Raw colours (hex, or palette steps used for fixed looks) do not change with the theme. For something that must stay dark, put data-theme="dark" on it and use ordinary semantic tokens inside.
Tokens
Every table is generated from @nuton/tokens. Value shows the alias the token points to and the resolved colour; Source shows where it's authored. Figma names are kept verbatim, numeric suffix included: Text/Sub-600 becomes --nu-text-sub-600 (D3).
Background
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-bg-strong-950 | → gray-950#171717 | FigmaMapped/Bg/Strong-950 | |
--nu-bg-surface-800 | → gray-800#262626 | FigmaMapped/Bg/Surface-800 | |
--nu-bg-sub-300 | → gray-300#d1d1d1 | FigmaMapped/Bg/Sub-300 | |
--nu-bg-soft-200 | → gray-200#ebebeb | FigmaMapped/Bg/Soft-200 | |
--nu-bg-soft-100 | → gray-100#f5f5f5 | FigmaMapped/Bg/Soft-100 | |
--nu-bg-weak-50 | → gray-50#f7f7f7 | FigmaMapped/Bg/Weak-50 | |
--nu-bg-white | → gray-0#ffffff | FigmaMapped/Bg/White |
White is the default page and card fill; Weak-50 and Soft-100/200 are quieter surfaces and hover fills. Strong-950 and Surface-800 are the dark fills behind the main action.
Text
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-text-strong-950 | → gray-950#171717 | FigmaMapped/Text/Strong-950 | |
--nu-text-sub-600 | → gray-600#5c5c5c | FigmaMapped/Text/Sub-600 | |
--nu-text-soft-400 | → gray-400#a4a4a4 | FigmaMapped/Text/Soft-400 | |
--nu-text-disabled-300 | → gray-300#d1d1d1 | FigmaMapped/Text/Disabled-300 | |
--nu-text-white | → gray-0#ffffff | FigmaMapped/Text/White |
D7: Strong-950 for primary text, Sub-600 for secondary text, hints and placeholders. Soft-400 is decorative only: it measures 2.49:1 on white (C1). Disabled text uses Disabled-500 (gray-500, 4.23:1 on white), darkened from Figma's Disabled-300 so it stays readable (C8).
Stroke
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-stroke-strong-950 | → gray-950#171717 | FigmaMapped/Stroke/Strong-950 | |
--nu-stroke-sub-300 | → gray-300#d1d1d1 | FigmaMapped/Stroke/Sub-300 | |
--nu-stroke-soft-200 | → gray-200#ebebeb | FigmaMapped/Stroke/Soft-200 | |
--nu-stroke-white | → gray-0#ffffff | FigmaMapped/Stroke/White |
Soft-200 for dividers and card borders. Sub-300 is the default input border, which measures 1.53:1 on white (C7). Strong-950 marks hover and focus.
Icon
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-icon-strong-950 | → gray-950#171717 | FigmaMapped/Icon/Strong-950 | |
--nu-icon-sub-600 | → gray-600#5c5c5c | FigmaMapped/Icon/Sub-600 | |
--nu-icon-soft-400 | → gray-400#a4a4a4 | FigmaMapped/Icon/Soft-400 | |
--nu-icon-disabled-300 | → gray-300#d1d1d1 | FigmaMapped/Icon/Disabled-300 | |
--nu-icon-white | → gray-0#ffffff | FigmaMapped/Icon/White |
Match the icon to the text it sits with: Strong-950 beside primary text, Sub-600 beside secondary text. Soft-400 suits decorative icons that are hidden from assistive tech.
Action
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-action-primary-bg | → bg-strong-950#171717 | Interim | |
--nu-action-primary-bg-hover | → bg-surface-800#262626 | Interim | |
--nu-action-primary-fg | → text-white#ffffff | Interim |
Action is not state-primary (D13)
Nuton's main action is the neutral black pill: --nu-action-primary-*. The blue --nu-state-primary-* family is a state colour for interactive emphasis and links, not “the most important button”. The bare primary name is retired. The action tokens are interim until Figma has an Action group; their values copy Figma's neutral filled button.
State
10 families (faded, primary, purple, success, error, warning, pending, feature, teal, verified), each with four steps. In @nuton/ui today, Lighter is a tinted background, Light is its hover, and Base is the solid colour for fills, icons, dots and labels. No component uses Dark yet; it reads as text on Lighter (Success Dark on Success Lighter is 6.53:1).
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-state-faded-dark | → gray-800#262626 | FigmaMapped/State/Faded/Dark | |
--nu-state-faded-base | → gray-500#7b7b7b | FigmaMapped/State/Faded/Base | |
--nu-state-faded-light | → gray-200#ebebeb | FigmaMapped/State/Faded/Light | |
--nu-state-faded-lighter | → gray-100#f5f5f5 | FigmaMapped/State/Faded/Lighter | |
--nu-state-primary-dark | → blue-900#182f8b | FigmaMapped/State/Primary/Dark | |
--nu-state-primary-base | → blue-600#3559e9 | FigmaMapped/State/Primary/Base | |
--nu-state-primary-light | → blue-200#c0d5ff | FigmaMapped/State/Primary/Light | |
--nu-state-primary-lighter | → blue-50#ebf1ff | FigmaMapped/State/Primary/Lighter | |
--nu-state-purple-dark | → purple-950#2b1164 | FigmaMapped/State/Purple/Dark | |
--nu-state-purple-base | → purple-500#7d52f4 | FigmaMapped/State/Purple/Base | |
--nu-state-purple-light | → purple-200#dbd6fe | FigmaMapped/State/Purple/Light | |
--nu-state-purple-lighter | → purple-50#f5f3ff | FigmaMapped/State/Purple/Lighter | |
--nu-state-success-dark | → green-900#16643b | FigmaMapped/State/Success/Dark | |
--nu-state-success-base | → green-600#1daf61 | FigmaMapped/State/Success/Base | |
--nu-state-success-light | → green-200#c2f5da | FigmaMapped/State/Success/Light | |
--nu-state-success-lighter | → green-50#e0faec | FigmaMapped/State/Success/Lighter | |
--nu-state-error-dark | → red-900#8b1822 | FigmaMapped/State/Error/Dark | |
--nu-state-error-base | → red-600#e93544 | FigmaMapped/State/Error/Base | |
--nu-state-error-light | → red-200#ffc0c5 | FigmaMapped/State/Error/Light | |
--nu-state-error-lighter | → red-50#ffebec | FigmaMapped/State/Error/Lighter | |
--nu-state-warning-dark | → orange-950#71330a | FigmaMapped/State/Warning/Dark | |
--nu-state-warning-base | → orange-600#e16614 | FigmaMapped/State/Warning/Base | |
--nu-state-warning-light | → orange-200#ffd9c0 | FigmaMapped/State/Warning/Light | |
--nu-state-warning-lighter | → orange-50#fff3eb | FigmaMapped/State/Warning/Lighter | |
--nu-state-pending-dark | → yellow-950#624c18 | FigmaMapped/State/Pending/Dark | |
--nu-state-pending-base | → yellow-600#e6a819 | FigmaMapped/State/Pending/Base | |
--nu-state-pending-light | → yellow-200#ffecc0 | FigmaMapped/State/Pending/Light | |
--nu-state-pending-lighter | → yellow-50#fffaeb | FigmaMapped/State/Pending/Lighter | |
--nu-state-feature-dark | → pink-950#68123d | FigmaMapped/State/Feature/Dark | |
--nu-state-feature-base | → pink-600#e9358f | FigmaMapped/State/Feature/Base | |
--nu-state-feature-light | → pink-200#ffc0df | FigmaMapped/State/Feature/Light | |
--nu-state-feature-lighter | → pink-50#ffebf4 | FigmaMapped/State/Feature/Lighter | |
--nu-state-teal-dark | → teal-950#0b463e | FigmaMapped/State/Teal/Dark | |
--nu-state-teal-base | → teal-700#178c7d | FigmaMapped/State/Teal/Base | |
--nu-state-teal-light | → teal-200#c2f5ee | FigmaMapped/State/Teal/Light | |
--nu-state-teal-lighter | → teal-50#e4fbf8 | FigmaMapped/State/Teal/Lighter | |
--nu-state-verified-dark | → sky-950#124b68 | FigmaMapped/State/Verified/Dark | |
--nu-state-verified-base | → sky-700#2597d0 | FigmaMapped/State/Verified/Base | |
--nu-state-verified-light | → sky-200#c0eaff | FigmaMapped/State/Verified/Light | |
--nu-state-verified-lighter | → sky-100#d5f1ff | FigmaMapped/State/Verified/Lighter |
The palette step behind each step isn't uniform across families: Dark is 900 or 950, Base is 500, 600 or 700, and Verified Lighter is 100 (TD-07, design to confirm). Teal and Verified skip the Alias layer in Figma (TD-06). Purple and Feature may be renamed in Figma (TD-09).
Overlay
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-overlay-gray | #3333333d | FigmaMapped/overlay/overlay-gray | |
--nu-overlay-slate | #2b303b3d | FigmaMapped/overlay/overlay-slate |
Literal values with alpha in Figma, not aliases. Figma's repeated group name (overlay/overlay-gray) is collapsed in code; the Figma rename is queued as TD-09.
Illustration
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-illustration-strong-400 | → gray-400#a4a4a4 | FigmaMapped/illustration/strong-400 | |
--nu-illustration-sub-300 | → gray-300#d1d1d1 | FigmaMapped/illustration/sub-300 | |
--nu-illustration-soft-200 | → gray-200#ebebeb | FigmaMapped/illustration/soft-200 | |
--nu-illustration-weak-100 | → gray-100#f5f5f5 | FigmaMapped/illustration/weak-100 | |
--nu-illustration-white-0 | → gray-0#ffffff | FigmaMapped/illustration/white-0 |
Lower-case in Figma, skips the Alias layer and has no bindings in the DS file. Proposed for deprecation unless another Nuton file uses it (TD-08, Q-04). Prefer Icon or Stroke tokens.
Global palette
Components use semantic tokens, not the palette (D3, D13)
The palette is a copy of the Figma Foundation collection. It isn't exposed as Tailwind utilities, so product code can't bypass the semantic layer. A few component tokens read it where Figma binds a palette step directly: the primary and error button hovers (Blue 700, Red 700) and Avatar persona colours (hue 200 and 950). If no semantic token fits, ask for one rather than reaching for a hue.
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-gray-950 | #171717 | FigmaFoundation/neutral/gray/950 | |
--nu-gray-900 | #1c1c1c | FigmaFoundation/neutral/gray/900 | |
--nu-gray-800 | #262626 | FigmaFoundation/neutral/gray/800 | |
--nu-gray-700 | #333333 | FigmaFoundation/neutral/gray/700 | |
--nu-gray-600 | #5c5c5c | FigmaFoundation/neutral/gray/600 | |
--nu-gray-500 | #7b7b7b | FigmaFoundation/neutral/gray/500 | |
--nu-gray-400 | #a4a4a4 | FigmaFoundation/neutral/gray/400 | |
--nu-gray-300 | #d1d1d1 | FigmaFoundation/neutral/gray/300 | |
--nu-gray-200 | #ebebeb | FigmaFoundation/neutral/gray/200 | |
--nu-gray-100 | #f5f5f5 | FigmaFoundation/neutral/gray/100 | |
--nu-gray-50 | #f7f7f7 | FigmaFoundation/neutral/gray/50 | |
--nu-gray-0 | #ffffff | FigmaFoundation/neutral/gray/0 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-slate-950 | #0e121b | FigmaFoundation/neutral/slate/950 | |
--nu-slate-900 | #181b25 | FigmaFoundation/neutral/slate/900 | |
--nu-slate-800 | #222530 | FigmaFoundation/neutral/slate/800 | |
--nu-slate-700 | #2b303b | FigmaFoundation/neutral/slate/700 | |
--nu-slate-600 | #525866 | FigmaFoundation/neutral/slate/600 | |
--nu-slate-500 | #717784 | FigmaFoundation/neutral/slate/500 | |
--nu-slate-400 | #99a0ae | FigmaFoundation/neutral/slate/400 | |
--nu-slate-300 | #cacfd8 | FigmaFoundation/neutral/slate/300 | |
--nu-slate-200 | #e1e4ea | FigmaFoundation/neutral/slate/200 | |
--nu-slate-100 | #f2f5f8 | FigmaFoundation/neutral/slate/100 | |
--nu-slate-50 | #f5f7fa | FigmaFoundation/neutral/slate/50 | |
--nu-slate-0 | #ffffff | FigmaFoundation/neutral/slate/0 |
Slate has no Mapped references yet (TD-10); keep it until the remote links are re-pointed.
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-blue-950 | #122368 | FigmaFoundation/blue/950 | |
--nu-blue-900 | #182f8b | FigmaFoundation/blue/900 | |
--nu-blue-800 | #1f3bad | FigmaFoundation/blue/800 | |
--nu-blue-700 | #2547d0 | FigmaFoundation/blue/700 | |
--nu-blue-600 | #3559e9 | FigmaFoundation/blue/600 | |
--nu-blue-500 | #335cff | FigmaFoundation/blue/500 | |
--nu-blue-400 | #6895ff | FigmaFoundation/blue/400 | |
--nu-blue-300 | #97baff | FigmaFoundation/blue/300 | |
--nu-blue-200 | #c0d5ff | FigmaFoundation/blue/200 | |
--nu-blue-100 | #d5e2ff | FigmaFoundation/blue/100 | |
--nu-blue-50 | #ebf1ff | FigmaFoundation/blue/50 | |
--nu-blue-0 | #f9fbff | FigmaFoundation/blue/0 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-orange-950 | #71330a | FigmaFoundation/orange/950 | |
--nu-orange-900 | #96440d | FigmaFoundation/orange/900 | |
--nu-orange-800 | #b75310 | FigmaFoundation/orange/800 | |
--nu-orange-700 | #ce5e12 | FigmaFoundation/orange/700 | |
--nu-orange-600 | #e16614 | FigmaFoundation/orange/600 | |
--nu-orange-500 | #fa7319 | FigmaFoundation/orange/500 | |
--nu-orange-400 | #ffa468 | FigmaFoundation/orange/400 | |
--nu-orange-300 | #ffc197 | FigmaFoundation/orange/300 | |
--nu-orange-200 | #ffd9c0 | FigmaFoundation/orange/200 | |
--nu-orange-100 | #ffe6d5 | FigmaFoundation/orange/100 | |
--nu-orange-50 | #fff3eb | FigmaFoundation/orange/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-red-950 | #681219 | FigmaFoundation/red/950 | |
--nu-red-900 | #8b1822 | FigmaFoundation/red/900 | |
--nu-red-800 | #ad1f2b | FigmaFoundation/red/800 | |
--nu-red-700 | #d02533 | FigmaFoundation/red/700 | |
--nu-red-600 | #e93544 | FigmaFoundation/red/600 | |
--nu-red-500 | #fb3748 | FigmaFoundation/red/500 | |
--nu-red-400 | #ff6875 | FigmaFoundation/red/400 | |
--nu-red-300 | #ff97a0 | FigmaFoundation/red/300 | |
--nu-red-200 | #ffc0c5 | FigmaFoundation/red/200 | |
--nu-red-100 | #ffd5d8 | FigmaFoundation/red/100 | |
--nu-red-50 | #ffebec | FigmaFoundation/red/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-green-950 | #0b4627 | FigmaFoundation/green/950 | |
--nu-green-900 | #16643b | FigmaFoundation/green/900 | |
--nu-green-800 | #1a7544 | FigmaFoundation/green/800 | |
--nu-green-700 | #178c4e | FigmaFoundation/green/700 | |
--nu-green-600 | #1daf61 | FigmaFoundation/green/600 | |
--nu-green-500 | #1fc16b | FigmaFoundation/green/500 | |
--nu-green-400 | #3ee089 | FigmaFoundation/green/400 | |
--nu-green-300 | #84ebb4 | FigmaFoundation/green/300 | |
--nu-green-200 | #c2f5da | FigmaFoundation/green/200 | |
--nu-green-100 | #d0fbe9 | FigmaFoundation/green/100 | |
--nu-green-50 | #e0faec | FigmaFoundation/green/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-yellow-950 | #624c18 | FigmaFoundation/yellow/950 | |
--nu-yellow-900 | #86661d | FigmaFoundation/yellow/900 | |
--nu-yellow-800 | #a78025 | FigmaFoundation/yellow/800 | |
--nu-yellow-700 | #c99a2c | FigmaFoundation/yellow/700 | |
--nu-yellow-600 | #e6a819 | FigmaFoundation/yellow/600 | |
--nu-yellow-500 | #f6b51e | FigmaFoundation/yellow/500 | |
--nu-yellow-400 | #ffd268 | FigmaFoundation/yellow/400 | |
--nu-yellow-300 | #ffe097 | FigmaFoundation/yellow/300 | |
--nu-yellow-200 | #ffecc0 | FigmaFoundation/yellow/200 | |
--nu-yellow-100 | #ffefcc | FigmaFoundation/yellow/100 | |
--nu-yellow-50 | #fffaeb | FigmaFoundation/yellow/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-purple-950 | #2b1164 | FigmaFoundation/purple/950 | |
--nu-purple-900 | #471e94 | FigmaFoundation/purple/900 | |
--nu-purple-800 | #5522b5 | FigmaFoundation/purple/800 | |
--nu-purple-700 | #6629d8 | FigmaFoundation/purple/700 | |
--nu-purple-600 | #753bec | FigmaFoundation/purple/600 | |
--nu-purple-500 | #7d52f4 | FigmaFoundation/purple/500 | |
--nu-purple-400 | #a38cf9 | FigmaFoundation/purple/400 | |
--nu-purple-300 | #c1b6fc | FigmaFoundation/purple/300 | |
--nu-purple-200 | #dbd6fe | FigmaFoundation/purple/200 | |
--nu-purple-100 | #ece9fe | FigmaFoundation/purple/100 | |
--nu-purple-50 | #f5f3ff | FigmaFoundation/purple/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-sky-950 | #124b68 | FigmaFoundation/sky/950 | |
--nu-sky-900 | #18658b | FigmaFoundation/sky/900 | |
--nu-sky-800 | #1f7ead | FigmaFoundation/sky/800 | |
--nu-sky-700 | #2597d0 | FigmaFoundation/sky/700 | |
--nu-sky-600 | #35ade9 | FigmaFoundation/sky/600 | |
--nu-sky-500 | #47c2ff | FigmaFoundation/sky/500 | |
--nu-sky-400 | #68cdff | FigmaFoundation/sky/400 | |
--nu-sky-300 | #97dcff | FigmaFoundation/sky/300 | |
--nu-sky-200 | #c0eaff | FigmaFoundation/sky/200 | |
--nu-sky-100 | #d5f1ff | FigmaFoundation/sky/100 | |
--nu-sky-50 | #ebf8ff | FigmaFoundation/sky/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-pink-950 | #68123d | FigmaFoundation/pink/950 | |
--nu-pink-900 | #8b1852 | FigmaFoundation/pink/900 | |
--nu-pink-800 | #ad1f66 | FigmaFoundation/pink/800 | |
--nu-pink-700 | #d0257a | FigmaFoundation/pink/700 | |
--nu-pink-600 | #e9358f | FigmaFoundation/pink/600 | |
--nu-pink-500 | #fb4ba3 | FigmaFoundation/pink/500 | |
--nu-pink-400 | #ff68b3 | FigmaFoundation/pink/400 | |
--nu-pink-300 | #ff97cb | FigmaFoundation/pink/300 | |
--nu-pink-200 | #ffc0df | FigmaFoundation/pink/200 | |
--nu-pink-100 | #ffd5ea | FigmaFoundation/pink/100 | |
--nu-pink-50 | #ffebf4 | FigmaFoundation/pink/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-teal-950 | #0b463e | FigmaFoundation/teal/950 | |
--nu-teal-900 | #16645a | FigmaFoundation/teal/900 | |
--nu-teal-800 | #1a7569 | FigmaFoundation/teal/800 | |
--nu-teal-700 | #178c7d | FigmaFoundation/teal/700 | |
--nu-teal-600 | #1daf9c | FigmaFoundation/teal/600 | |
--nu-teal-500 | #22d3bb | FigmaFoundation/teal/500 | |
--nu-teal-400 | #3fdec9 | FigmaFoundation/teal/400 | |
--nu-teal-300 | #84ebdd | FigmaFoundation/teal/300 | |
--nu-teal-200 | #c2f5ee | FigmaFoundation/teal/200 | |
--nu-teal-100 | #d0fbf5 | FigmaFoundation/teal/100 | |
--nu-teal-50 | #e4fbf8 | FigmaFoundation/teal/50 |
| Preview | Token | Value | Source |
|---|---|---|---|
--nu-alpha-slate-24 | #99a0ae3d | FigmaFoundation/alpha/slate/alpha-24 | |
--nu-alpha-slate-16 | #99a0ae29 | FigmaFoundation/alpha/slate/alpha-16 | |
--nu-alpha-slate-10 | #99a0ae1a | FigmaFoundation/alpha/slate/alpha-10 | |
--nu-alpha-gray-24 | #a3a3a33d | FigmaFoundation/alpha/gray/alpha-24 | |
--nu-alpha-gray-16 | #a3a3a329 | FigmaFoundation/alpha/gray/alpha-16 | |
--nu-alpha-gray-10 | #a3a3a31a | FigmaFoundation/alpha/gray/alpha-10 | |
--nu-alpha-blue-24 | #476cff3d | FigmaFoundation/alpha/blue/alpha-24 | |
--nu-alpha-blue-16 | #476cff29 | FigmaFoundation/alpha/blue/alpha-16 | |
--nu-alpha-blue-10 | #476cff1a | FigmaFoundation/alpha/blue/alpha-10 | |
--nu-alpha-orange-24 | #fa73193d | FigmaFoundation/alpha/orange/alpha-24 | |
--nu-alpha-orange-16 | #fa731929 | FigmaFoundation/alpha/orange/alpha-16 | |
--nu-alpha-orange-10 | #fa73191a | FigmaFoundation/alpha/orange/alpha-10 | |
--nu-alpha-red-24 | #fb37483d | FigmaFoundation/alpha/red/alpha-24 | |
--nu-alpha-red-16 | #fb374829 | FigmaFoundation/alpha/red/alpha-16 | |
--nu-alpha-red-10 | #fb37481a | FigmaFoundation/alpha/red/alpha-10 | |
--nu-alpha-green-24 | #1fc16b3d | FigmaFoundation/alpha/green/alpha-24 | |
--nu-alpha-green-16 | #1fc16b29 | FigmaFoundation/alpha/green/alpha-16 | |
--nu-alpha-green-10 | #1fc16b1a | FigmaFoundation/alpha/green/alpha-10 | |
--nu-alpha-yellow-24 | #fbc64b3d | FigmaFoundation/alpha/yellow/alpha-24 | |
--nu-alpha-yellow-16 | #fbc64b29 | FigmaFoundation/alpha/yellow/alpha-16 | |
--nu-alpha-yellow-10 | #fbc64b1a | FigmaFoundation/alpha/yellow/alpha-10 | |
--nu-alpha-purple-24 | #784def3d | FigmaFoundation/alpha/purple/alpha-24 | |
--nu-alpha-purple-16 | #784def29 | FigmaFoundation/alpha/purple/alpha-16 | |
--nu-alpha-purple-10 | #784def1a | FigmaFoundation/alpha/purple/alpha-10 | |
--nu-alpha-sky-24 | #47c2ff3d | FigmaFoundation/alpha/sky/alpha-24 | |
--nu-alpha-sky-16 | #47c2ff29 | FigmaFoundation/alpha/sky/alpha-16 | |
--nu-alpha-sky-10 | #47c2ff1a | FigmaFoundation/alpha/sky/alpha-10 | |
--nu-alpha-pink-24 | #fb4ba33d | FigmaFoundation/alpha/pink/alpha-24 | |
--nu-alpha-pink-16 | #fb4ba329 | FigmaFoundation/alpha/pink/alpha-16 | |
--nu-alpha-pink-10 | #fb4ba31a | FigmaFoundation/alpha/pink/alpha-10 | |
--nu-alpha-teal-24 | #22d3bb3d | FigmaFoundation/alpha/teal/alpha-24 | |
--nu-alpha-teal-16 | #22d3bb29 | FigmaFoundation/alpha/teal/alpha-16 | |
--nu-alpha-teal-10 | #22d3bb1a | FigmaFoundation/alpha/teal/alpha-10 | |
--nu-alpha-white-24 | #ffffff3d | FigmaFoundation/alpha/white/alpha-24 | |
--nu-alpha-white-16 | #ffffff29 | FigmaFoundation/alpha/white/alpha-16 | |
--nu-alpha-white-10 | #ffffff1a | FigmaFoundation/alpha/white/alpha-10 | |
--nu-alpha-black-24 | #1717173d | FigmaFoundation/alpha/black/alpha-24 | |
--nu-alpha-black-16 | #17171729 | FigmaFoundation/alpha/black/alpha-16 | |
--nu-alpha-black-10 | #1717171a | FigmaFoundation/alpha/black/alpha-10 |
Translucent tints of each hue at three strengths, plus white and black. Previews sit on the page background.
Usage
Load tokens.css once, then reference CSS variables directly or through the Tailwind theme. Tailwind exposes semantic colours only, named after the Figma path, so the role appears twice: bg-bg-white, text-text-sub-600.
@import '@nuton/tokens/tokens.css';
.lesson-row {
background: var(--nu-bg-white);
border: 1px solid var(--nu-stroke-soft-200);
color: var(--nu-text-strong-950);
}
.lesson-row:hover { background: var(--nu-bg-weak-50); }
.lesson-row__meta { color: var(--nu-text-sub-600); }
.lesson-row__status {
background: var(--nu-state-success-lighter);
color: var(--nu-state-success-dark);
}/* app.css */
@import 'tailwindcss';
@import '@nuton/tokens/tokens.css';
@import '@nuton/tokens/tailwind-v4.css';
// LessonRow.tsx
<li className="bg-bg-white hover:bg-bg-weak-50 border border-stroke-soft-200">
<span className="text-text-strong-950">Photosynthesis basics</span>
<span className="text-text-sub-600">Biology 101 · 12 min</span>
<span className="bg-state-success-lighter text-state-success-dark">Completed</span>
</li>// tailwind.config.js (load @nuton/tokens/tokens.css on the page too)
module.exports = {
presets: [require('@nuton/tokens/tailwind-v3-preset')],
}
// Same class names as v4. Opacity modifiers work through color-mix:
<div className="bg-state-primary-base/10 text-state-primary-dark">…</div>
<button className="bg-action-primary-bg hover:bg-action-primary-bg-hover text-action-primary-fg">…</button>Choosing a token
- Pick the role first, then the emphasis. Fills use
bg-, text usestext-, borders usestroke-, icons useicon-. Don't borrow across roles because two values match today; dark mode may split them (D8). - Main action:
action-primary-*. Usestate-primary-*only where the interactive blue is meant, such as links and selected controls (D13). - Status: a Lighter background with Dark text, or Base for icons and dots. White text passes on
state-primary-base(5.59:1); Success, Pending, Warning, Error, Feature, Verified and Teal Base fail with white text (A2). - Secondary text:
text-sub-600, including placeholders and hints.text-soft-400is decorative only (D7, C1). - No raw values. Lint blocks hex colours and arbitrary Tailwind colours in
@nuton/ui; the palette is CSS-only.
In use
Real @nuton/ui components, showing which semantic tokens they read.
Action and state colours on buttons
<Button appearance="stroke">Save draft</Button> {/* stroke-sub-300, text-strong-950 */}
<Button>Publish lesson</Button> {/* action-primary-bg / -fg */}
<Button intent="primary">Join space</Button> {/* state-primary-base, text-white */}The neutral filled button is the main action; blue is the state colour (D13). Error buttons and error text use red-700, because Figma's state-error-base is 4.15:1 (C6, resolved 2026-10-14); see the Button page.
Text roles in a form
For example, Biology 101.
<Field label="Space name" hint="For example, Biology 101."> {/* label: text-strong-950, hint: text-sub-600 */}
<Input placeholder="Biology 101" /> {/* placeholder: text-sub-600 (D7, C1) */}
</Field>Figma draws placeholders in Soft-400; code uses Sub-600 so they pass 4.5:1 (D7, C1). The default border is Stroke/Sub-300 (C7). Error and disabled states are on the Inputs and fields page.
State colours as data series
- Lessons completed
- Quizzes taken
- Flashcards reviewed
<ul className="legend">
<li><ChartLegend color="blue">Lessons completed</ChartLegend></li> {/* state-primary-base */}
<li><ChartLegend color="orange">Quizzes taken</ChartLegend></li> {/* state-warning-base */}
<li><ChartLegend color="teal">Flashcards reviewed</ChartLegend></li> {/* state-teal-base */}
</ul>Chart colours map to State Base tokens, and every series keeps a text label so colour is never the only cue (WCAG 1.4.1).
Accessibility
Ratios below are computed at build time from the resolved token values, with the same WCAG formula as npm run tokens:check. Text needs 4.5:1; borders, focus rings and other UI parts need 3:1.
| Foreground on background | Used for | Ratio | Needs | Result |
|---|---|---|---|---|
text-strong-950 on bg-white | Body text and headings | 17.93:1 | 4.5:1 | Pass |
text-sub-600 on bg-white | Secondary text, placeholders (D7, C1) | 6.69:1 | 4.5:1 | Pass |
text-sub-600 on bg-weak-50 | Secondary text on weak surfaces | 6.24:1 | 4.5:1 | Pass |
text-strong-950 on bg-soft-100 | Neutral lighter button label | 16.44:1 | 4.5:1 | Pass |
action-primary-fg on action-primary-bg | Main action label | 17.93:1 | 4.5:1 | Pass |
action-primary-fg on action-primary-bg-hover | Main action label, hover | 15.13:1 | 4.5:1 | Pass |
text-white on state-primary-base | Primary filled button label | 5.59:1 | 4.5:1 | Pass |
state-primary-base on bg-white | Primary stroke, ghost and link label | 5.59:1 | 4.5:1 | Pass |
state-primary-base on state-primary-lighter | Primary lighter label | 4.94:1 | 4.5:1 | Pass |
stroke-strong-950 on bg-white | Input border on hover and focus | 17.93:1 | 3:1 | Pass |
focus-ring-color on bg-white | Focus ring colour (interim, C5) | 5.59:1 | 3:1 | Pass |
text-soft-400 on bg-white | Soft-400 used as text | 2.49:1 | 4.5:1 | Below, C1: decorative only |
red-700 on bg-white | Error hint text; error stroke and ghost label (C6, red-700) | 5.26:1 | 4.5:1 | Pass |
text-white on red-700 | Error filled button label (C6, red-700) | 5.26:1 | 4.5:1 | Pass |
red-700 on state-error-lighter | Error lighter label (C6, red-700) | 4.59:1 | 4.5:1 | Pass |
state-error-base on bg-white | Error borders and icons (non-text) | 4.15:1 | 3:1 | Pass |
stroke-sub-300 on bg-white | Default input border, the only boundary cue | 1.53:1 | 3:1 | Below, C7 |
text-disabled-500 on bg-white | Disabled text, labels and hints (C8, gray-500) | 4.23:1 | 3:1 | Pass |
text-disabled-500 on bg-weak-50 | Disabled label on a weak fill (C8) | 3.95:1 | 3:1 | Pass |
text-white on state-success-base | White text on a success fill | 2.85:1 | 4.5:1 | Below, A2: don’t use |
Browser axe on the component pages reports the same failures; it measures the rendered disabled hint at 1.52:1 (docs/quality-strategy.md). Every failing pair is a Figma value implemented as specified, waiting on a design decision.
Text contrast
Strong-950 and Sub-600 pass on every Nuton surface. Never set meaningful text in Soft-400 or Disabled-300; disabled text uses Disabled-500 (C8). Error text uses Red 700 (5.26:1), not State/Error/Base (4.15:1); the base red stays for borders and icons (C6).
Non-text contrast
When a border is a control's only boundary, it needs 3:1. Stroke/Sub-300 doesn't reach it (C7). Hover and focus borders in Strong-950 pass. The interim focus-ring colour is 5.59:1 on white; Figma's focus effects are below 3:1 (C5). See Layout and responsive for focus rings.
Colour alone
Never use colour as the only signal (WCAG 1.4.1). Pair state colours with text or an icon: chart series have labels, errors have a message, Avatar status needs a label (C22).
Themes and forced colours
Only the light theme exists (D8). In Windows forced-colours mode the system palette replaces these tokens, so keep borders and text, not fills alone, carrying meaning.
Guidelines and related
Use semantic colour tokens for
- Every fill, text, border and icon colour in product code and components.
- Status and feedback, with State Lighter backgrounds and Dark text.
- The main action, through
action-primary-*.
Don't
- Use palette tokens or hex values in product code.
- Use
state-primaryblue to mean “most important action” (D13). - Put meaningful text in Soft-400, or white text on Success, Error or the other Base fills listed in A2.
- Invent dark-mode values in code (D8).