Color
DraftA two-tier system of primitive palettes and semantic tokens that keeps every surface, text, and border themeable without touching component code.
- Usage
- Tokens
- Code
- Status & changelog
Common alternative names
Color palette, colour, theme colors, color tokens
Principles
Semantic over literal
Every color in the system has a role, not just a value. Instead of reaching for "blue-600", you use --color-action-primary — and the system handles what that means in light mode, dark mode, or any future theme. This keeps intent readable and theme switching painless.
Accessible by default
Our palette was designed so that semantic pairings meet WCAG 2.1 contrast requirements out of the box. Text tokens paired with their corresponding background tokens achieve at least 4.5:1 contrast for normal text. Feedback colors — danger, success, warning, info — are tested in both themes to ensure legibility without relying on color alone.
Two-tier architecture
Primitive tokens define the raw color ramps (e.g. --color-blue-600). Semantic tokens reference primitives and assign them to interface roles (e.g. --color-action-primary maps to --color-blue-600 in light mode). Components only use semantic tokens — this indirection is what makes theming, dark mode, and brand customisation possible without touching component code.
Anatomy
The color system has two layers. Primitives are the full palette of available values. Semantic tokens map those values to roles within the interface.
--color-blue-600--color-red-600--color-gray-900--color-action-primary--color-action-destructive--color-text-defaultSemantic roles
Semantic tokens are grouped into seven categories. Each category addresses a different part of the interface.
| Category | Purpose | Example token |
|---|---|---|
| Background | Page and container fills | --color-background-default |
| Text | Body copy, labels, links | --color-text-default |
| Action | Buttons and interactive controls | --color-action-primary |
| Border | Dividers, outlines, focus rings | --color-border-default |
| Feedback | Alerts, banners, validation | --color-feedback-danger-bg |
| Surface | Layered containers (base, raised, overlay) | --color-surface-raised |
| Icon | Iconography in all states | --color-icon-default |
Usage guidelines
Use semantic tokens, never primitives
Components must always reference semantic tokens. Primitives exist to feed the semantic layer — they should not appear in component CSS. This rule ensures that every component automatically adapts to theme changes.
color: var(--color-text-default);
background: var(--color-background-subtle);
Reference semantic tokens so components adapt to theme changes automatically.
color: #282828;
background: var(--color-gray-50);
Hardcoded hex values or primitive tokens break theming.
Match token role to element purpose
Choose tokens that match the semantic role of the element. A background should use a background token, not a surface token repurposed.
| Element | Correct token | Wrong token |
|---|---|---|
| Page fill | --color-background-default | --color-surface-base |
| Error message text | --color-feedback-danger-text | --color-text-danger on a plain background |
| Disabled input border | --color-border-disabled | --color-gray-200 |
| Primary button fill | --color-action-primary | --color-blue-600 |
Action token patterns
Interactive elements follow a consistent three-state pattern: rest, hover, and active. Each variant (primary, secondary, ghost, destructive) provides all three plus a text color. See the Code tab for implementation examples.
Feedback tokens come in sets
Each feedback category (danger, success, warning, info) provides a background, text, and border token designed to work together. Always use the complete set. See the Code tab for implementation examples.
Dark mode
Arch UI supports light and dark themes through the same semantic token names. When [data-theme="dark"] is applied, the token values are remapped to dark-appropriate primitives. No component code changes are needed.
The general pattern: light mode uses darker primitives for foreground and lighter ones for background, while dark mode inverts this relationship. Accent colors shift toward lighter shades in dark mode to maintain contrast against dark surfaces.
Accessibility
Contrast requirements
All semantic text-on-background pairings meet WCAG 2.1 AA requirements:
- Normal text (below 18px or below 14px bold): 4.5:1 minimum
- Large text (18px+ or 14px+ bold): 3:1 minimum
- UI components and graphical objects: 3:1 minimum against adjacent colors
Do not rely on color alone
Color should reinforce meaning, not be the sole indicator. Combine color with text labels, icons, or patterns to ensure information is perceivable by users with color vision deficiencies.
Red border and an error icon and descriptive text.
Combine color with icons and descriptive text for validation errors.
A red border as the only indicator that a field is invalid.
Don't rely on color alone to signal state.
Focus visibility
All interactive elements must show a visible focus indicator using --color-border-focus. This token maps to --color-blue-500 (#068BEE) in light mode and --color-blue-400 (#6DAAFB) in dark mode, both providing strong contrast against their respective background colors. See the Code tab for the recommended focus style.
Semantic color tokens
All semantic tokens used by Arch UI components. These tokens change values between light and dark themes while keeping their names constant. Always use these in component CSS — never reference primitives directly.
Background
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-background-default | #FFFFFF | #282828 | default |
--color-background-subtle | #F3F3F3 | #4B4B4B | subtle |
--color-background-muted | #E8E8E8 | #5E5E5E | muted |
--color-background-inverse | #282828 | #F3F3F3 | inverse |
--color-background-disabled | #E8E8E8 | #4B4B4B | disabled |
--color-background-overlay | rgba(0, 0, 0, 0.5) | rgba(0, 0, 0, 0.7) | overlay |
Text
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-text-default | #282828 | #F3F3F3 | default |
--color-text-subtle | #727272 | #A6A6A6 | subtle |
--color-text-placeholder | #A6A6A6 | #868686 | placeholder |
--color-text-disabled | #BBBBBB | #727272 | disabled |
--color-text-inverse | #FFFFFF | #282828 | inverse |
--color-text-link | #266EF1 | #6DAAFB | link |
--color-text-link-visited | #944DE7 | #C490F9 | link visited |
--color-text-link-hover | #175BCC | #A9C9FF | link hover |
--color-text-danger | #DE1135 | #FC7F79 | danger |
--color-text-success | #0E8345 | #06C167 | success |
--color-text-warning | #845201 | #D79900 | warning |
--color-text-info | #266EF1 | #6DAAFB | info |
Action
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-action-primary | #266EF1 | #068BEE | primary |
--color-action-primary-hover | #175BCC | #6DAAFB | primary hover |
--color-action-primary-active | #1948A3 | #A9C9FF | primary active |
--color-action-primary-text | #FFFFFF | #FFFFFF | primary text |
--color-action-secondary | #E8E8E8 | #5E5E5E | secondary |
--color-action-secondary-hover | #DDDDDD | #727272 | secondary hover |
--color-action-secondary-active | #BBBBBB | #868686 | secondary active |
--color-action-secondary-text | #4B4B4B | #E8E8E8 | secondary text |
--color-action-ghost | transparent | transparent | ghost |
--color-action-ghost-hover | #E8E8E8 | #4B4B4B | ghost hover |
--color-action-ghost-active | #DDDDDD | #5E5E5E | ghost active |
--color-action-ghost-text | #4B4B4B | #E8E8E8 | ghost text |
--color-action-destructive | #DE1135 | #F83446 | destructive |
--color-action-destructive-hover | #BB032A | #FC7F79 | destructive hover |
--color-action-destructive-active | #950F22 | #FFB2AB | destructive active |
--color-action-destructive-text | #FFFFFF | #FFFFFF | destructive text |
Border
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-border-default | #DDDDDD | #5E5E5E | default |
--color-border-subtle | #E8E8E8 | #4B4B4B | subtle |
--color-border-strong | #A6A6A6 | #868686 | strong |
--color-border-focus | #068BEE | #6DAAFB | focus |
--color-border-danger | #F83446 | #FC7F79 | danger |
--color-border-success | #009A51 | #06C167 | success |
--color-border-warning | #B97502 | #D79900 | warning |
--color-border-disabled | #DDDDDD | #5E5E5E | disabled |
--color-border-selected | #000000 | selected |
Feedback
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-feedback-danger-bg | #FFF0EE | #950F22 | danger bg |
--color-feedback-danger-text | #BB032A | #FFB2AB | danger text |
--color-feedback-danger-border | #FFD2CD | #950F22 | danger border |
--color-feedback-success-bg | #EAF6ED | #166C3B | success bg |
--color-feedback-success-text | #166C3B | #7FD99A | success text |
--color-feedback-success-border | #B1EAC2 | #166C3B | success border |
--color-feedback-warning-bg | #FDF2DC | #845201 | warning bg |
--color-feedback-warning-text | #845201 | #F6BC2F | warning text |
--color-feedback-warning-border | #FFD688 | #845201 | warning border |
--color-feedback-info-bg | #EFF4FE | #175BCC | info bg |
--color-feedback-info-text | #175BCC | #A9C9FF | info text |
--color-feedback-info-border | #CDDEFF | #175BCC | info border |
Surface
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-surface-base | #FFFFFF | #282828 | base |
--color-surface-raised | #FFFFFF | #4B4B4B | raised |
--color-surface-overlay | #FFFFFF | #4B4B4B | overlay |
--color-surface-sunken | #F3F3F3 | #4B4B4B | sunken |
Icon
| Token | Light | Dark | Use case |
|---|---|---|---|
--color-icon-default | #5E5E5E | #BBBBBB | default |
--color-icon-subtle | #A6A6A6 | #868686 | subtle |
--color-icon-disabled | #BBBBBB | #727272 | disabled |
--color-icon-inverse | #FFFFFF | #282828 | inverse |
--color-icon-danger | #F83446 | #FC7F79 | danger |
--color-icon-success | #009A51 | #06C167 | success |
--color-icon-warning | #B97502 | #D79900 | warning |
--color-icon-info | #068BEE | #6DAAFB | info |
Primitive color palette
The primitive palette defines every available color value in the system. These tokens are the raw material that semantic tokens reference. Primitives should not be used directly in component CSS — they exist to feed the semantic layer.
Each hue provides a ramp from 50 (lightest) through 900 (darkest). The neutral gray ramp is used most heavily in both themes.
Black & White
Gray
Blue
Red
Green
Yellow
Orange
Purple
Teal
Magenta
Lime
Amber
Action token patterns
Interactive elements follow a consistent three-state pattern: rest, hover, and active. Each variant (primary, secondary, ghost, destructive) provides all three plus a text color.
.button--primary {
background: var(--color-action-primary);
color: var(--color-action-primary-text);
}
.button--primary:hover {
background: var(--color-action-primary-hover);
}
.button--primary:active {
background: var(--color-action-primary-active);
}
Feedback token sets
Each feedback category (danger, success, warning, info) provides a background, text, and border token designed to work together. Always use the complete set.
.alert--danger {
background: var(--color-feedback-danger-bg);
color: var(--color-feedback-danger-text);
border-color: var(--color-feedback-danger-border);
}
Focus visibility
All interactive elements must show a visible focus indicator using --color-border-focus.
:focus-visible {
outline: 2px solid var(--color-border-focus);
outline-offset: 2px;
}
Status & changelog coming soon.