Design Tokens
Last updated on 2026-08-27
The Email Marketing Kit uses an Operator design profile -- dense, information-rich layouts with flat surfaces, hairline borders, and a restrained color palette. All tokens are defined in app/globals.css using the oklch color space for perceptually uniform color manipulation.
Design Profile: Operator
The Operator profile is optimized for data-heavy dashboards and tool interfaces:
- Flat surfaces with hairline borders (no drop shadows)
- Dense information layouts with compact spacing
- Neutral-dominant palette with a single accent hue
- Monospace data for numerical values
- High contrast for readability at small font sizes
oklch Color System
All colors use the oklch color space, which provides perceptually uniform lightness and chroma values:
oklch(lightness chroma hue)
- Lightness (0-1): 0 is black, 1 is white
- Chroma (0-0.4): 0 is gray, higher values are more saturated
- Hue (0-360): color wheel angle
Primary Accent: Hue 115 (Chartreuse-Green)
The primary accent color uses hue 115, a chartreuse-green that provides strong visibility against neutral backgrounds.
| Token | Light Mode | Dark Mode |
|---|---|---|
--primary |
oklch(0.48 0.14 115) |
oklch(0.72 0.14 115) |
--primary-foreground |
oklch(0.98 0.005 115) |
oklch(0.13 0.02 115) |
Neutral Palette
Neutrals are pinned to hue 250-255 with near-zero chroma:
| Token | Light Mode | Dark Mode | Purpose |
|---|---|---|---|
--background |
oklch(0.985 0.002 250) |
oklch(0.13 0.012 255) |
Page background |
--foreground |
oklch(0.145 0.015 255) |
oklch(0.96 0.005 250) |
Primary text |
--card |
oklch(1 0 0) |
oklch(0.17 0.014 255) |
Card surfaces |
--muted |
oklch(0.96 0.004 250) |
oklch(0.22 0.015 255) |
Muted backgrounds |
--muted-foreground |
oklch(0.4 0.02 250) |
oklch(0.7 0.02 250) |
Secondary text |
--accent |
oklch(0.94 0.008 250) |
oklch(0.25 0.02 255) |
Hover/active states |
--border |
oklch(0.92 0.004 250) |
oklch(1 0 0 / 12%) |
Borders |
--input |
oklch(0.92 0.004 250) |
oklch(1 0 0 / 15%) |
Input borders |
Secondary Accent
A purple secondary accent at hue 286:
| Token | Light Mode | Dark Mode |
|---|---|---|
--secondary |
oklch(0.78 0.08 286) |
oklch(0.72 0.08 286) |
--secondary-foreground |
oklch(0.25 0.04 286) |
oklch(0.18 0.04 286) |
Status Tokens
Status colors encode meaning beyond color alone -- each status is paired with text labels and icons throughout the UI.
| Token | Light Mode | Dark Mode | Usage |
|---|---|---|---|
--success |
oklch(0.55 0.15 150) |
oklch(0.7 0.15 150) |
Active, sent, verified |
--success-foreground |
oklch(0.98 0.01 150) |
oklch(0.16 0.03 150) |
Text on success |
--warning |
oklch(0.6 0.14 75) |
oklch(0.75 0.14 80) |
Scheduled, sending, pending |
--warning-foreground |
oklch(0.2 0.05 75) |
oklch(0.2 0.05 80) |
Text on warning |
--destructive |
oklch(0.58 0.22 27) |
oklch(0.704 0.191 22.216) |
Bounced, failed, error |
--info |
oklch(0.55 0.16 240) |
oklch(0.65 0.15 240) |
Draft, informational |
--info-foreground |
oklch(0.98 0.01 240) |
oklch(0.15 0.03 240) |
Text on info |
Chart Colors
Five chart colors for Recharts visualizations, anchored to distinct hues:
| Token | Light Mode | Dark Mode | Hue |
|---|---|---|---|
--chart-1 |
oklch(0.48 0.14 115) |
oklch(0.72 0.14 115) |
115 (green, matches primary) |
--chart-2 |
oklch(0.65 0.08 286) |
oklch(0.65 0.08 286) |
286 (purple) |
--chart-3 |
oklch(0.6 0.08 1) |
oklch(0.6 0.08 1) |
1 (red) |
--chart-4 |
oklch(0.55 0.08 181) |
oklch(0.6 0.08 181) |
181 (teal) |
--chart-5 |
oklch(0.62 0.08 51) |
oklch(0.65 0.08 51) |
51 (orange) |
Typography
The kit uses three font families loaded via next/font/google:
| Font | Variable | Usage | Loaded Weights |
|---|---|---|---|
| Familjen Grotesk | --font-familjen |
Headings (--font-heading) |
Variable |
| Inter Tight | --font-inter-tight |
Body text (--font-sans) |
Variable |
| IBM Plex Mono | --font-plex-mono |
Code and data (--font-mono) |
400, 500 |
Font Application
/* globals.css */
--font-sans: var(--font-inter-tight), ui-sans-serif, system-ui, sans-serif;
--font-heading: var(--font-familjen), var(--font-inter-tight), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-plex-mono), ui-monospace, monospace;
Typography Scale
| Element | Font | Weight | Letter Spacing | Line Height |
|---|---|---|---|---|
| Body | Inter Tight | 400 | -0.011em | 1.6 |
| Headings (h1-h6) | Familjen Grotesk | 600 | -0.025em | 1.2 |
| Code / Data | IBM Plex Mono | 400-500 | Default | Default |
Base Font Size
The body font size is set to 0.875rem (14px) -- smaller than typical defaults, consistent with the Operator profile's dense layout.
Border Radius
The radius system uses a base value with multipliers:
| Token | Multiplier | Computed Value (base: 0.375rem) |
|---|---|---|
--radius-sm |
0.6x | 0.225rem |
--radius-md |
0.8x | 0.3rem |
--radius-lg |
1.0x (base) | 0.375rem |
--radius-xl |
1.4x | 0.525rem |
--radius-2xl |
1.8x | 0.675rem |
--radius-3xl |
2.2x | 0.825rem |
--radius-4xl |
2.6x | 0.975rem |
The base radius of 0.375rem (6px) produces subtle rounding consistent with the Operator profile's flat, precise aesthetic.
Surface Treatment
Surfaces follow the Operator profile: flat with hairline borders, no shadows.
- Cards use
--cardbackground with--borderhairline border - Popovers use
--popoverbackground with border - Sidebar uses
--sidebarbackground with--sidebar-border - No box-shadow is applied to surfaces
Dark Mode Strategy
Dark mode is implemented via class-based toggling with next-themes:
// app/layout.tsx
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
Key Dark Mode Decisions
- System-aware -- defaults to the user's OS preference
- Class-based -- the
.darkclass is toggled on<html> - No transitions --
disableTransitionOnChangeprevents flash during toggle - Inverted lightness -- dark mode tokens invert lightness while preserving hue and chroma
- Alpha borders -- dark mode borders use
oklch(1 0 0 / 12%)for semi-transparent white borders - Boosted chroma -- primary and status colors get slightly higher lightness in dark mode for readability
Sidebar Dark Mode
| Token | Light | Dark |
|---|---|---|
--sidebar |
oklch(0.975 0.004 250) |
oklch(0.15 0.014 255) |
--sidebar-primary |
oklch(0.48 0.14 115) |
oklch(0.72 0.14 115) |
--sidebar-accent |
oklch(0.93 0.008 250) |
oklch(0.22 0.018 255) |
--sidebar-border |
oklch(0.92 0.004 250) |
oklch(1 0 0 / 12%) |
Custom CSS Utilities
Selection Color
::selection {
background: oklch(from var(--primary) l c h / 0.25);
color: var(--foreground);
}
Custom Scrollbar
.custom-scrollbar {
scrollbar-width: thin;
scrollbar-color: oklch(from var(--muted-foreground) l c h / 0.25) transparent;
}
Keyboard Badge
kbd {
font-family: var(--font-mono);
font-size: 0.6875rem;
background: var(--muted);
border: 1px solid var(--border);
}
Animation Tokens
| Class | Animation | Duration |
|---|---|---|
.animate-fade-in-up |
Fade in + translate up 20px | 0.5s ease-out |
.animate-fade-in |
Fade in | 0.4s ease |
.animate-scale-in |
Fade in + scale from 0.95 | 0.3s ease |
.animate-float |
Gentle vertical float | 6s infinite |
.animate-shimmer |
Loading shimmer gradient | 1.5s infinite |
.stagger-1 through .stagger-6 |
Stagger delays | 100ms increments |
All animations respect prefers-reduced-motion:
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.01ms !important; }
}
Related Docs
- Customization -- change the primary hue and fonts
- Components -- how tokens are consumed by components
- Accessibility -- contrast and motion considerations