Colors
Semantic color tokens and OKLCH brand colors. Use tokens for surfaces, text, controls, and borders instead of raw color scales.
Semantic Tokens
Instead of using raw Radix color scales directly, use the semantic tokens from @delphi/ui. Each token encapsulates light/dark values so you never need to write dark: prefixes for color.
import { surface, text, control, border, divider } from "@delphi/ui";
Surface
Surfaces define the visual layering of your UI. The hierarchy goes from dimmest to brightest in both light and dark mode:
surface.basePage / app background — the dimmest layersurface.secondaryPanels, sidebars, grouped containerssurface.primaryCards, content areas — the brightest layer// Layering example — base → secondary → primary
<div className={cn(surface.base, "p-6")}>
<div className={cn(surface.secondary, "rounded-3xl p-4")}>
<div className={cn(surface.primary, "rounded-2xl p-4")}>
<h1 className={text.high}>Title</h1>
<p className={text.mid}>Body</p>
</div>
</div>
</div>
// Hierarchy: dimmest → brightest
surface.base // Page background
surface.secondary // Panels, sidebars
surface.primary // Cards, content areas
Text
text.high // Headings, primary labels
text.mid // Body text, descriptions
text.low // Captions, timestamps, placeholders
text.inverted // Text on dark surfaces (e.g. on control.solid)
Control
Control tokens map 1:1 with Button component variants and include hover/active states.
control.solid // Primary CTA. Pair with text.inverted.
control.subtle // Low-emphasis actions with backdrop blur.
control.ghost // Minimal weight, appears on hover.
control.outline // Bordered secondary actions.
control.danger // Destructive actions.
control.success // Positive/confirmation actions.
Border & Divider
Two intensities — subtle for card edges and dividers, loud for inputs and emphasized dividers. Each supports directional variants.
// Border — wraps an element
<div className={cn(border.subtle.all, "rounded-xl p-4")}>Subtle card</div>
<div className={cn(border.loud.all, "rounded-xl p-4")}>Loud card</div>
<div className={cn(border.subtle.b, "pb-4")}>Bottom border only</div>
// Divider — separates children
<div className={cn(divider.subtle.y, "flex flex-col")}>
<div className="py-3">Item 1</div>
<div className="py-3">Item 2</div>
</div>
// Directions: all, y, x, t, b, l, r
border.subtle.all // Card edges, skeleton outlines
border.loud.all // Input borders, emphasized dividers
divider.subtle.y // List separators
divider.loud.y // Emphasized list separators
Tangerine (Brand Orange)
Tangerine is our signature brand color. It's deliberately bolder and punchier than a standard color scale. Higher chroma values create the vibrant, energetic feel that signifies Delphi elements. Use it sparingly for primary actions, brand moments, and interactive elements that need to stand out.
Tangerine-9 (#FD5400) is our base brand color, used for primary buttons, solid backgrounds, and key interactive elements. Click any swatch above to copy its hex value.
/* Tangerine Scale - shared-global-styles.css is the source of truth */
--tangerine-1: oklch(0.99 0.0096 42); /* Lightest */
--tangerine-2: oklch(0.97 0.0192 42); /* App background */
--tangerine-3: oklch(0.94 0.036 42); /* Subtle background */
--tangerine-4: oklch(0.90 0.06 42); /* Hover states */
--tangerine-5: oklch(0.85 0.096 42); /* Borders (subtle) */
--tangerine-6: oklch(0.79 0.132 42); /* Borders (standard) */
--tangerine-7: oklch(0.72 0.18 42); /* Solid backgrounds */
--tangerine-8: oklch(0.68 0.21 42); /* Solid backgrounds (darker) */
--tangerine-9: oklch(0.67 0.22 42); /* #FD5400 - Base brand color */
--tangerine-10: oklch(0.60 0.21 42); /* Hover */
--tangerine-11: oklch(0.50 0.18 42); /* Active */
--tangerine-12: oklch(0.40 0.14 42); /* Text */
// Tailwind usage
<button className="bg-tangerine-9 hover:bg-tangerine-10">Primary action</button>
<span className="text-tangerine-11">Accent text</span>
<div className="bg-tangerine-9/10">Soft brand tint</div>
OKLCH Color Generator
Use this tool to generate harmonious color palettes for any hue. Adjust the hue slider to explore different colors while maintaining consistent lightness and chroma relationships.
| Step | Lightness | Chroma | OKLCH | Preview |
|---|---|---|---|---|
| 1 | oklch(0.9900 0.0096 255.00) | |||
| 2 | oklch(0.9700 0.0192 255.00) | |||
| 3 | oklch(0.9400 0.0360 255.00) | |||
| 4 | oklch(0.9000 0.0500 255.00) | |||
| 5 | oklch(0.8600 0.0800 255.00) | |||
| 6 | oklch(0.8200 0.1000 255.00) | |||
| 7 | oklch(0.7400 0.1400 255.00) | |||
| 8 | oklch(0.6500 0.1800 255.00) | |||
| 9 | oklch(0.5800 0.2000 255.00) | |||
| 10 | oklch(0.5200 0.1900 255.00) | |||
| 11 | oklch(0.4600 0.1700 255.00) | |||
| 12 | oklch(0.4000 0.1400 255.00) |
:root {
--color-1: oklch(0.9900 0.0096 255.00);
--color-2: oklch(0.9700 0.0192 255.00);
--color-3: oklch(0.9400 0.0360 255.00);
--color-4: oklch(0.9000 0.0500 255.00);
--color-5: oklch(0.8600 0.0800 255.00);
--color-6: oklch(0.8200 0.1000 255.00);
--color-7: oklch(0.7400 0.1400 255.00);
--color-8: oklch(0.6500 0.1800 255.00);
--color-9: oklch(0.5800 0.2000 255.00);
--color-10: oklch(0.5200 0.1900 255.00);
--color-11: oklch(0.4600 0.1700 255.00);
--color-12: oklch(0.4000 0.1400 255.00);
}Understanding OKLCH
OKLCH stands for:
- L - Lightness (0-1, where 0 is black and 1 is white)
- C - Chroma (0-0.4, color intensity/saturation)
- H - Hue (0-360, the color wheel angle)
OKLCH provides perceptually uniform colors, meaning equal steps in lightness appear equally different to human eyes. This makes it easier to create accessible color combinations.
Scale Convention (1-12)
We use a 1-12 scale inspired by Radix Colors:
- 1-2 - App backgrounds
- 3-4 - Subtle backgrounds, hover states
- 5-6 - Borders, separators
- 7-8 - Solid backgrounds, hover on solid
- 9 - Primary solid color
- 10-11 - Hover/active states, text on colored backgrounds
- 12 - High contrast text
Layering with Surface Tokens
Surface tokens create clear visual hierarchy without needing to think about individual color steps. Always nest in order: surface.base (outermost) > surface.secondary > surface.primary (innermost).
Don't skip levels or nest the same surface
Each surface level should wrap the next. Nesting the same surface or skipping levels creates indistinguishable boundaries.
Where does each layer end?
primary > primary > primaryClear stacking layers
base > secondary > primaryDon't use control tokens for layout
Control tokens (control.subtle, control.ghost, etc.) include hover/active states and are designed for interactive elements only. Use surface tokens for static containers.
A card using control.subtle
control.subtle for a static cardA card using surface.primary
surface.primary + border.subtleOrange for warnings
Orange is our brand color (Tangerine). Using it for warnings creates confusion between brand elements and system feedback. Use yellow for warnings instead.
⚠ Is this a warning or a feature?
⚠ Clearly a system warning
Accessibility
When using tokens, these are the contrast ratios you get out of the box:
| Combination | Contrast | WCAG |
|---|---|---|
text.high on surface.base | ~15:1 | AAA |
text.mid on surface.base | ~10:1 | AAA |
text.low on surface.base | ~5:1 | AA |
text.inverted on control.solid | ~15:1 | AAA |
Always test your color combinations with contrast checking tools.