Soft UI’s color system is built on Tailwind palettes with semantic tokens that adapt to theme, mode, and scheme.
Color Architecture
Colors are organized in three layers:
- Palette colors: Raw RGB values from Tailwind (
--blue-500, --neutral-200, etc.)
- Theme/base mappings:
--theme-* and --base-* that reference palette colors
- Semantic tokens: Purpose-driven colors (
--color-surface-page, --color-actions-primary-default, etc.)
Always use semantic tokens in components. They adapt automatically to theme changes.
Theme Colors (--theme-*)
Theme colors provide the accent palette, controlled by data-theme-color attribute.
Available Theme Colors
Chromatic:
- red, orange, amber, yellow
- lime, green, emerald, teal
- cyan, sky, blue (default), indigo
- violet, purple, fuchsia, pink, rose
Achromatic:
- mauve (purple-tinted gray)
- mist (blue-tinted gray)
- olive (yellow-tinted gray)
- taupe (brown-tinted gray)
Theme Color Scale
Each theme color maps to a 50-950 scale:
--theme-50 /* Lightest */
--theme-100
--theme-200
--theme-300
--theme-400
--theme-500 /* Base */
--theme-600
--theme-700
--theme-800
--theme-900
--theme-950 /* Darkest */
Example: Blue (Default)
:root {
--theme-50: var(--blue-50); /* 239 246 255 */
--theme-100: var(--blue-100); /* 219 234 254 */
--theme-200: var(--blue-200); /* 191 219 254 */
--theme-300: var(--blue-300); /* 147 197 253 */
--theme-400: var(--blue-400); /* 96 165 250 */
--theme-500: var(--blue-500); /* 59 130 246 */
--theme-600: var(--blue-600); /* 37 99 235 */
--theme-700: var(--blue-700); /* 29 78 216 */
--theme-800: var(--blue-800); /* 30 64 175 */
--theme-900: var(--blue-900); /* 30 58 138 */
--theme-950: var(--blue-950); /* 23 37 84 */
}
Switching Theme Colors
document.documentElement.dataset.themeColor = 'violet';
This remaps all --theme-* variables:
html[data-theme-color="violet"] {
--theme-50: var(--violet-50); /* 245 243 255 */
--theme-500: var(--violet-500); /* 139 92 246 */
--theme-950: var(--violet-950); /* 46 16 101 */
/* ... */
}
Base Colors (--base-*)
Base colors provide the neutral/grayscale palette, controlled by data-base-color attribute.
Available Base Colors
Standard grays:
- neutral (default) - Pure neutral gray
- slate - Cool blue-gray
- gray - Balanced gray
- zinc - Cool neutral
- stone - Warm gray
Custom grays:
- mauve - Purple-tinted gray
- mist - Blue-tinted gray
- olive - Yellow-tinted gray
- taupe - Brown-tinted gray
Base Color Scale
--base-50 /* Lightest */
--base-100
--base-200
--base-300
--base-400
--base-500 /* Base */
--base-600
--base-700
--base-800
--base-900
--base-950 /* Darkest */
Example: Neutral (Default)
:root {
--base-50: var(--neutral-50); /* 250 250 250 */
--base-100: var(--neutral-100); /* 245 245 245 */
--base-200: var(--neutral-200); /* 229 229 229 */
--base-500: var(--neutral-500); /* 115 115 115 */
--base-800: var(--neutral-800); /* 38 38 38 */
--base-950: var(--neutral-950); /* 10 10 10 */
}
Custom grays (mauve, mist, olive, taupe) have subtle color casts that create warmer or cooler interfaces.
Semantic Color Tokens
Semantic tokens map to theme/base colors and adapt to mode and scheme.
Surface Colors
Backgrounds and layers:
--color-surface-page /* Main page background */
--color-surface-canvas /* Secondary background (panels, sidebars) */
--color-surface-overlay /* Modals, popovers, tooltips */
--color-surface-inverse /* Inverse backgrounds */
--color-surface-interactive-default /* Hover/interactive surfaces (default) */
--color-surface-interactive-hover /* Hover state */
--color-surface-interactive-selected /* Selected state */
Light mode values:
--surface-page: var(--pure-white); /* 255 255 255 */
--surface-canvas: var(--base-50); /* 250 250 250 */
--surface-overlay: var(--pure-white); /* 255 255 255 */
--surface-interactive-default: var(--darken-3); /* 0 0 0 / 0.03 */
Dark mode values:
--surface-page: var(--base-900); /* 23 23 23 */
--surface-canvas: var(--darken-16); /* 0 0 0 / 0.16 */
--surface-overlay: var(--base-800); /* 38 38 38 */
--surface-interactive-default: var(--lighten-6); /* 255 255 255 / 0.06 */
Content Colors
Text and icon colors:
--color-content-strong /* Primary text (headings, important content) */
--color-content-subtle /* Body text */
--color-content-muted /* Secondary text (captions, metadata) */
--color-content-disabled /* Disabled text */
--color-content-link-default /* Link color */
--color-content-link-hover /* Link hover color */
Light mode values:
--content-strong: var(--base-800); /* 38 38 38 */
--content-subtle: var(--base-500); /* 115 115 115 */
--content-muted: var(--base-400); /* 163 163 163 */
--content-link-default: var(--theme-600); /* 37 99 235 (blue) */
Dark mode values:
--content-strong: var(--base-200); /* 229 229 229 */
--content-subtle: var(--base-400); /* 163 163 163 */
--content-muted: var(--base-500); /* 115 115 115 */
--content-link-default: var(--theme-500); /* 59 130 246 (blue) */
Action Colors
Button and interactive element colors:
--color-actions-primary-default
--color-actions-primary-hover
--color-actions-primary-disabled
--color-actions-secondary-default
--color-actions-secondary-hover
--color-actions-secondary-disabled
--color-actions-tertiary-default
--color-actions-tertiary-hover
--color-actions-tertiary-disabled
--color-actions-danger-default
--color-actions-danger-hover
--color-actions-danger-disabled
Mono scheme (grayscale primary):
/* Light mode */
--actions-primary-default: var(--base-800); /* 38 38 38 */
--actions-primary-hover: var(--base-900); /* 23 23 23 */
/* Dark mode */
--actions-primary-default: var(--base-200); /* 229 229 229 */
--actions-primary-hover: var(--base-100); /* 245 245 245 */
Color scheme (theme-colored primary):
/* Light mode */
--actions-primary-default: var(--theme-600); /* 37 99 235 (blue) */
--actions-primary-hover: var(--theme-700); /* 29 78 216 */
/* Dark mode */
--actions-primary-default: var(--theme-500); /* 59 130 246 */
--actions-primary-hover: var(--theme-400); /* 96 165 250 */
Use mono scheme for enterprise/professional UIs, color scheme for consumer/creative UIs.
Border Colors
--color-border-subtle /* Default borders */
--color-border-muted /* Subtle dividers */
--color-border-inverse /* Inverse borders (on dark backgrounds) */
--color-border-interactive-default /* Input/select borders */
--color-border-interactive-hover /* Hover state */
--color-border-interactive-active /* Focus/active state */
Light mode values:
--border-subtle: var(--darken-6); /* 0 0 0 / 0.06 */
--border-muted: var(--darken-4); /* 0 0 0 / 0.04 */
--border-interactive-default: var(--darken-8); /* 0 0 0 / 0.08 */
--border-interactive-active: var(--base-800); /* 38 38 38 */
Feedback Colors
Status and alert colors:
Success (Lime):
--color-surface-feedback-success-subtle /* Light: lime-200, Dark: lime-900 */
--color-surface-feedback-success-muted /* Light: lime-100, Dark: lime-950 */
--color-content-feedback-success-strong /* Light: lime-600, Dark: lime-400 */
--color-content-feedback-success-subtle /* Light: lime-700, Dark: lime-100 */
--color-border-feedback-success-subtle /* Light: lime-300, Dark: lime-800 */
Warning (Amber):
--color-surface-feedback-warning-subtle /* Light: amber-200, Dark: amber-900 */
--color-surface-feedback-warning-muted /* Light: amber-100, Dark: amber-950 */
--color-content-feedback-warning-strong /* Light: amber-600, Dark: amber-400 */
--color-content-feedback-warning-subtle /* Light: amber-700, Dark: amber-100 */
--color-border-feedback-warning-subtle /* Light: amber-200, Dark: amber-800 */
Danger (Rose):
--color-surface-feedback-danger-subtle /* Light: rose-200, Dark: rose-900 */
--color-surface-feedback-danger-muted /* Light: rose-100, Dark: rose-950 */
--color-content-feedback-danger-strong /* Light: rose-600, Dark: rose-500 */
--color-content-feedback-danger-subtle /* Light: rose-700, Dark: rose-100 */
--color-border-feedback-danger-subtle /* Light: rose-200, Dark: rose-800 */
Info (Sky):
--color-surface-feedback-info-subtle /* Light: sky-200, Dark: sky-900 */
--color-surface-feedback-info-muted /* Light: sky-100, Dark: sky-950 */
--color-content-feedback-info-strong /* Light: sky-600, Dark: sky-400 */
--color-content-feedback-info-subtle /* Light: sky-700, Dark: sky-100 */
--color-border-feedback-info-subtle /* Light: sky-200, Dark: sky-800 */
Decorative Colors
For badges, tags, and color-coded elements. Available for all 18 chromatic colors:
--color-surface-decorative-{color}-strong /* Vibrant background */
--color-surface-decorative-{color}-subtle /* Light background */
--color-surface-decorative-{color}-muted /* Very light background */
--color-content-decorative-{color}-strong /* Strong text */
--color-content-decorative-{color}-subtle /* Subtle text */
--color-border-decorative-{color}-subtle /* Border color */
Example (violet):
/* Light mode */
--surface-decorative-violet-strong: var(--violet-500); /* 139 92 246 */
--surface-decorative-violet-subtle: var(--violet-200); /* 221 214 254 */
--surface-decorative-violet-muted: var(--violet-100); /* 237 233 254 */
--content-decorative-violet-strong: var(--violet-600); /* 124 58 237 */
--content-decorative-violet-subtle: var(--violet-700); /* 109 40 217 */
--border-decorative-violet-subtle: var(--violet-200); /* 221 214 254 */
/* Dark mode */
--surface-decorative-violet-strong: var(--violet-500); /* 139 92 246 */
--surface-decorative-violet-subtle: var(--violet-900); /* 76 29 149 */
--surface-decorative-violet-muted: var(--violet-950); /* 46 16 101 */
--content-decorative-violet-strong: var(--violet-400); /* 167 139 250 */
--content-decorative-violet-subtle: var(--violet-100); /* 237 233 254 */
--border-decorative-violet-subtle: var(--violet-800); /* 91 33 182 */
Utility Colors
Specialized UI colors:
--color-utility-kbd /* Keyboard shortcut background */
--color-utility-kbd-border /* Keyboard shortcut border */
--color-utility-focus-outer /* Focus ring outer (3px) */
--color-utility-focus-inner /* Focus ring inner (1px) */
--color-utility-shadow-l1 /* Shadow layer 1 (lightest) */
--color-utility-shadow-l2 /* Shadow layer 2 */
--color-utility-shadow-l3 /* Shadow layer 3 */
--color-utility-shadow-l4 /* Shadow layer 4 (darkest) */
--color-utility-backdrop /* Modal/overlay backdrop */
--color-utility-avatar /* Avatar placeholder background */
Lighten & Darken Overlays
Transparent overlays for creating interactive states:
--lighten-2: 255 255 255 / 0.02
--lighten-4: 255 255 255 / 0.04
--lighten-8: 255 255 255 / 0.08
--lighten-16: 255 255 255 / 0.16
/* ... through --lighten-96 */
--darken-2: 0 0 0 / 0.02
--darken-4: 0 0 0 / 0.04
--darken-8: 0 0 0 / 0.08
--darken-16: 0 0 0 / 0.16
/* ... through --darken-96 */
Usage:
/* Hover state in light mode */
.button:hover {
background: rgb(var(--darken-5));
}
/* Hover state in dark mode */
html[data-mode="dark"] .button:hover {
background: rgb(var(--lighten-8));
}
Lighten/darken values are in RGB format (no rgb() wrapper), so you must wrap them: rgb(var(--darken-8))
Pure Colors
Absolute white and black:
--pure-white: 255 255 255
--pure-black: 0 0 0
Use sparingly for maximum contrast or absolute values.
Color Usage Patterns
/* Primary button */
.button-primary {
background: var(--color-actions-primary-default);
color: var(--color-content-on-accent-strong);
}
.button-primary:hover {
background: var(--color-actions-primary-hover);
}
/* Secondary button */
.button-secondary {
background: var(--color-actions-secondary-default);
color: var(--color-content-strong);
}
Cards & Panels
.card {
background: var(--color-surface-overlay);
border: 1px solid var(--color-border-subtle);
color: var(--color-content-subtle);
}
.input {
background: var(--color-surface-page);
border: 1px solid var(--color-border-interactive-default);
color: var(--color-content-strong);
}
.input:hover {
border-color: var(--color-border-interactive-hover);
}
.input:focus {
border-color: var(--color-border-interactive-active);
box-shadow: 0 0 0 1px var(--color-utility-focus-inner),
0 0 0 4px var(--color-utility-focus-outer);
}
Alerts
.alert-success {
background: var(--color-surface-feedback-success-muted);
border: 1px solid var(--color-border-feedback-success-subtle);
color: var(--color-content-feedback-success-strong);
}
Best Practices
Do:
- Use semantic tokens (
--color-surface-page) instead of raw palette tokens (--blue-500)
- Wrap lighten/darken values with
rgb(): rgb(var(--darken-8))
- Test colors in both light and dark modes
- Use decorative colors for badges and tags
- Use feedback colors consistently (lime=success, amber=warning, rose=danger, sky=info)
Don’t:
- Hardcode color values
- Mix theme colors with base colors for text/background (contrast issues)
- Use pure-white/pure-black unless absolutely necessary
- Forget to test color scheme switching (mono vs color)
Accessibility
- All semantic tokens meet WCAG 2.1 Level AA contrast ratios (4.5:1 for text, 3:1 for UI)
- Use
--color-content-strong for important text to ensure readability
- Decorative colors have sufficient contrast when paired with appropriate content tokens
- Focus rings use high-contrast colors with double-ring design (inner + outer)
When creating custom color combinations, verify contrast using browser DevTools or a contrast checker.