A dark, sharp, OKLCH system for data-dense dashboards. Link two files, use the tokens, and every screen comes out consistent, with no hardcoded values and no guesswork.
Two stylesheets carry the whole system: fonts, palette, type scale, spacing, and every component on this page. Link them in the head, in this order, then use the tokens.
<link rel="stylesheet" href="https://design.seantippen.com/tokens.css"> <link rel="stylesheet" href="https://design.seantippen.com/components.css">
var(--token) in CSS and tok('--token') in Chart.js. Never paste a hex, rgba, or oklch value. The one exception is Chart.js dataset colors, which the Canvas API cannot resolve from CSS.0 everywhere except pills and toggles. No gradient page backgrounds, no gradient text.Stops a reader's dark-mode browser from re-mapping the palette and making the brand look wrong. Add a theme-color meta matching --color-bg for the address bar.
<meta name="color-scheme" content="dark"> <meta name="darkreader-lock">
OKLCH for all custom colors. Hex only for Chart.js data series (Canvas API). Click any swatch to copy.
Where each token earns its place. Click any swatch above to copy its value.
| Token | Role |
|---|---|
| --color-accent | Primary accent. Active states, focus, the one color a layout leans on. |
| --color-secondary | Coral counterpoint. A second series or a contrast CTA, used sparingly. |
| --color-bg | Page canvas. Flat, never gradient. |
| --color-surface to -overlay | Five-step elevation ladder. Depth comes from lightness, not shadow. |
| --color-text to -disabled | Four-step text hierarchy. Text is never pure white. |
| --color-success / warning / danger / info | Status only. Amber is a warning, never decoration. |
Computed live from the tokens against the current canvas. Toggle the theme and watch it recalculate. Body text holds AA or better.
| Pair | Ratio | Grade |
|---|---|---|
| Body text on canvas | — | |
| Secondary text on canvas | — | |
| Muted text on canvas | — | |
| Accent on canvas | — | |
| Disabled text on canvas | — |
Inter for all text, JetBrains Mono for data values and code. Fluid sizing via clamp().
--text-3xl
--text-2xl
--text-xl
--text-lg
--text-base
--text-sm
--text-xs
Fluid spacing tokens scale between viewport breakpoints. Bars shown at actual computed size.
Interactive elements with ripple clicks, stretch toggles, underline focus, and toast notifications.
Outline is the preferred selection state. All buttons get ripple on click and lift on hover.
Bottom border only. Accent underline grows from center on focus.
Toasts slide from the right. Text colored by type — especially red for danger.
Shimmer animation using surface tokens. Use for any loading state.
KPIs, charts, and tables. No card wrappers — data lives directly on the surface.
Left-border accent markers — no card wrappers. Each KPI gets a different accent color via nth-child.
Chart.js 4.4.7. All colors via tok() — hex data series tokens for Canvas compatibility.
| Region | Revenue | Growth | Status |
|---|---|---|---|
| Northeast | $842,500 | +12.3% | Growing |
| Southeast | $621,300 | +8.1% | Growing |
| Midwest | $534,700 | +2.4% | Stable |
| West Coast | $412,800 | -3.7% | Declining |
| Mountain | $289,100 | +15.2% | Emerging |
Bare table — no card wrapper. Hover highlights rows. Monospace numbers via font-variant-numeric.
Lift over scale. Move your mouse over each demo to see the effect.
Click any card to preview the animation. All use ease-out timing.
Frosted glass, neumorphic inset, and three-level shadow elevation.
Three elevation levels. Use sparingly — shadows convey hierarchy, not decoration.
What not to do — and what to do instead.
var(--color-bg) + noise grain overlay
border-radius — puffy, rounded shapes look unprofessional
border-radius: 0 everywhere
transform: scale() on hover — janky, displaces layout
translateY(-2px) lift + shadow increase
var(--color-text) or var(--color-accent)
oklch(0.74 0.18 30) secondary
var(--color-bg) — content is the decoration
Triumph projects swap the accent palette to green/blue. Everything else uses the master system unchanged.
Composite patterns for data-dense surfaces. Delta chips, stat bands, dense tables with totals, progress, tabs, segmented toggles, drawers, stale timestamps. All ship in components.css; link the sheet and the class names are ready.
No bespoke CSS. Every block below is a stock component, composed the way a real dashboard composes them.
| Rep | Sold | Revenue |
|---|---|---|
| Avery | 1,284 | $58,210 |
| Mateo | 1,102 | $49,870 |
| Priya | 986 | $44,120 |
| Jordan | 742 | $33,540 |
| Total | 4,114 | $185,740 |
Monospace, tabular numerals, arrow glyph. .plain removes the border for inline use next to a value.
Horizontal strip of KPIs with eyebrow, value, optional delta, and context line. For dashboard headers.
| Match | Sold | Revenue | Fill |
|---|---|---|---|
| Stone Stadium · 4/26 | 3,142 | $42,580 | 88% |
| Clemson · 5/09 | 2,988 | $38,210 | 84% |
| Stone Stadium · 5/17 | 2,210 | $28,440 | 62% |
| GE Vernova · 6/03 | 1,870 | $23,110 | 52% |
| Total | 10,210 | $132,340 | 72% |
Add .dense for tighter rows. Wrap a totals row in tr.total. td.pos / td.neg for colored numerics.
Leading dot communicates status at a glance. .live pulses.
Underline on active. Horizontal scroll on overflow.
Time-range or mode switcher. Active fill uses the accent.
Filters left, actions right. Stale indicator pairs with the actions group.
Fixed 80×24 slot, pairs with a stat value for trend-at-a-glance. Use .sparkline.lg for 120×36.
Right-side slide-in with scrim. For row drill-downs without leaving the page.
Fresh, aging, dead. Colored dot signals freshness without reading the number.
Shimmer placeholder sized like the table that replaces it.
Run this before any dashboard goes live. If every line is true, it is on brand.
tokens.css before components.cssvar(--token), charts via tok('--token')0 except pills and togglestabular-nums on every numeric column and KPItranslateY, never scale()prefers-reduced-motion respectedThe names you will reach for most. The full set lives in tokens.css; copy the link from Start Here.
/* Accent and surface */ --color-accent --color-secondary --color-bg --color-surface --color-surface-hover --color-surface-raised --color-surface-overlay /* Text hierarchy */ --color-text --color-text-secondary --color-text-muted --color-text-disabled /* Status */ --color-success --color-warning --color-danger --color-info /* Chart data series (hex, for Chart.js) */ --green --blue --red --amber --purple --cyan --yellow --orange /* Type */ --font-sans --font-mono --text-xs to --text-4xl /* fluid clamp scale */ /* Spacing */ --space-xs to --space-xl --space-section /* Motion */ --duration-fast --duration-normal --ease-out --ease-spring /* Radius (sharp by default) */ --radius: 0 --radius-full: 9999px