UMBRA Design System & Stylesheet
This document defines the visual language for every page in UMBRA.
All frontend work MUST conform to these patterns. Do not invent new colors, spacing scales, or component styles.
Visual Reference
The dashboard establishes the canonical look: a near-black canvas with thin-bordered cards, cyan accent highlights, the Sora/DM Sans type pairing, and subtle glow effects on interactive elements. Every page should feel like a natural extension of this dashboard.
Key visual characteristics:
- Ultra-dark background (~#0a0a0a) with very subtle card elevation (#0d0d0d cards)
- Thin 1px borders in dark gray (#262626) - never heavy or prominent
- Accent color (default cyan) used sparingly: active nav, stat icons, today highlights, hover glows
- High-contrast white text on dark, with muted gray (#a3a3a3) for secondary content
- Cards have a gentle hover glow (
shadow-accent/5) - not dramatic, just a hint of life
- Clean data-dense layouts with compact spacing - no wasted space
- Type badges and priority pills use color-coded backgrounds at 10% opacity (e.g.
bg-blue-500/10 text-blue-400)
1. Color Tokens (CSS Custom Properties)
All colors use HSL format via CSS variables. Never hardcode hex values.
Surface Colors
| Token |
HSL |
Approx Hex |
Usage |
--background |
0 0% 3.9% |
#0a0a0a |
Page background |
--card |
0 0% 5% |
#0d0d0d |
Card/panel surfaces |
--card-elevated |
0 0% 7% |
#121212 |
Hover states, elevated surfaces |
--secondary |
0 0% 10% |
#1a1a1a |
Secondary backgrounds, muted surfaces |
--muted |
0 0% 10% |
#1a1a1a |
Disabled/muted backgrounds |
Text Colors
| Token |
HSL |
Approx Hex |
Usage |
--foreground |
0 0% 98% |
#fafafa |
Primary text |
--muted-foreground |
0 0% 63.9% |
#a3a3a3 |
Secondary/subtle text |
Border & Input
| Token |
HSL |
Approx Hex |
Usage |
--border |
0 0% 14.9% |
#262626 |
All borders, dividers |
--input |
0 0% 14.9% |
#262626 |
Input borders |
Semantic Colors
| Token |
HSL |
Usage |
--destructive |
0 62.8% 30.6% |
Delete actions, errors |
--primary / --accent-color / --ring |
Dynamic (accent HSL) |
Primary actions, focus rings |
Accent System (Dynamic)
The accent is composed of three root variables set at runtime by useTheme:
--accent-h (hue)
--accent-s (saturation)
--accent-l (lightness)
These feed into --primary, --ring, and --accent-color.
Presets:
| Name |
H |
S |
L |
Approx Color |
cyan (default) |
187 |
85.7% |
53.3% |
Teal-cyan |
blue |
217 |
91.2% |
59.8% |
Bright blue |
purple |
258 |
89.5% |
66.3% |
Violet |
orange |
21 |
94.6% |
53.3% |
Warm orange |
green |
142 |
70.6% |
45.3% |
Emerald |
Semantic Color Assignments (Fixed per feature)
These are NOT theme-dependent. They provide visual consistency for item types:
| Feature |
Text |
Background |
Usage |
| Todos |
text-blue-400 |
bg-blue-500/10 |
Todo badges, type indicators |
| Events |
text-purple-400 |
bg-purple-500/10 |
Event badges, calendar items |
| Reminders |
text-orange-400 |
bg-orange-500/10 |
Reminder badges |
| Projects |
text-blue-400 |
bg-blue-500/10 |
Project counts |
| In Progress |
text-purple-400 |
bg-purple-500/10 |
Status indicators |
| Open Todos |
text-teal-400 |
bg-teal-500/10 |
Stat cards |
| Weather |
text-amber-400 |
bg-amber-500/10 |
Weather widget |
| High priority |
text-red-400 |
bg-red-500/10 |
Priority pills |
| Medium priority |
text-yellow-400 |
bg-yellow-500/10 |
Priority pills |
| Low priority |
text-green-400 |
bg-green-500/10 |
Priority pills |
2. Typography
Font Families
- Headings:
Sora (font-heading) — all h1-h6, card titles, stat values, logo
- Body:
DM Sans (font-body) — everything else
Type Scale (as used)
| Class |
Usage |
text-[9px] |
Micro labels (priority badges, type tags) |
text-[10px] tracking-wider uppercase |
Stat labels |
text-[11px] |
Small metadata |
text-xs |
Timestamps, secondary metadata |
text-sm |
Body text, list items, form inputs, nav items |
text-lg font-semibold |
Card titles (with font-heading) |
text-xl font-bold tabular-nums |
Stat values (with font-heading) |
text-3xl font-bold tracking-tight |
Page greeting/hero (with font-heading) |
Font Rendering
font-feature-settings: "rlig" 1, "calt" 1;
-webkit-font-smoothing: antialiased;
3. Spacing & Layout
Border Radius
| Token |
Value |
Usage |
rounded-lg |
0.5rem (8px) |
Cards, panels |
rounded-md |
~6px |
Buttons, inputs |
rounded-sm |
~4px |
Small elements |
rounded-full |
Pill |
Badges, dots, icon containers |
Page Layout
App Shell: flex h-screen overflow-hidden bg-background
├── Sidebar: w-64 (expanded) / w-16 (collapsed), border-r, bg-card
│ transition-all duration-300
└── Content: flex-1 flex flex-col overflow-hidden
├── MobileHeader: md:hidden, h-14, border-b, bg-card
└── <main>: flex-1 overflow-y-auto
└── Page content: px-6 py-6
Grid Patterns
| Pattern |
Classes |
Usage |
| Stat row |
grid gap-2.5 grid-cols-2 lg:grid-cols-4 |
Dashboard stats |
| Main + sidebar |
grid gap-5 lg:grid-cols-5 → lg:col-span-3 + lg:col-span-2 |
Dashboard layout |
| Card grid |
grid gap-4 md:grid-cols-2 lg:grid-cols-3 |
List pages (people, locations, projects) |
Standard Spacing
- Page padding:
px-6 py-6
- Card padding:
p-5 (header), p-5 pt-0 (content/footer)
- Card gap in grid:
gap-4 to gap-5
- List row padding:
py-1.5 px-2
- Section gap:
space-y-4 to space-y-6
4. Component Patterns
Card
Base: rounded-lg border bg-card text-card-foreground shadow-sm
Hover: hover:shadow-lg hover:shadow-accent/5 hover:border-accent/20
Motion: transition-all duration-200
Header: flex flex-col space-y-1.5 p-5
Title: font-heading text-lg font-semibold leading-none tracking-tight
Content: p-5 pt-0
Footer: flex items-center p-5 pt-0
Button (CVA variants)
Base: inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium
transition-colors focus-visible:ring-2 focus-visible:ring-ring
Variants:
default: bg-accent text-accent-foreground hover:bg-accent/90
destructive: bg-destructive text-destructive-foreground hover:bg-destructive/90
outline: border border-input bg-background hover:bg-accent/10 hover:text-accent
secondary: bg-secondary text-secondary-foreground hover:bg-secondary/80
ghost: hover:bg-accent/10 hover:text-accent
link: text-accent underline-offset-4 hover:underline
Sizes:
default: h-10 px-4 py-2
sm: h-9 px-3
lg: h-11 px-8
icon: h-10 w-10
Badge
Base: inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold
Variants:
default: border-transparent bg-accent text-accent-foreground
secondary: border-transparent bg-secondary text-secondary-foreground
destructive: border-transparent bg-destructive text-destructive-foreground
outline: text-foreground
Input / Select / Textarea
h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
placeholder:text-muted-foreground
focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
disabled:cursor-not-allowed disabled:opacity-50
Dialog
Overlay: fixed inset-0 bg-background/80 backdrop-blur-sm
Content: border bg-card p-6 shadow-lg rounded-lg max-w-xl
Header: flex flex-col space-y-1.5
Title: text-lg font-semibold leading-none tracking-tight
Footer: flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2
Close: absolute right-4 top-4, X icon h-4 w-4
Empty State
Container: flex flex-col items-center justify-center py-16 px-4
Icon: rounded-full bg-muted p-4 mb-4, icon h-8 w-8 text-muted-foreground
Title: text-lg font-semibold mb-1
Desc: text-sm text-muted-foreground text-center max-w-sm mb-4
Action: <Button> with Plus icon
Skeleton
Base: animate-pulse rounded-md bg-muted
5. Widget & Data Patterns
Stat Card
Container: Card with bg-gradient-to-br from-accent/[0.03] to-transparent
Icon: p-1.5 rounded-md bg-{color}/10
Label: text-[10px] tracking-wider uppercase text-muted-foreground
Value: font-heading text-xl font-bold tabular-nums
List Row (full-page lists: todos, reminders, etc.)
Container: space-y-0.5 (tight stacking, no card borders)
Row: flex items-center gap-3 px-3 py-2 rounded-md
hover:bg-card-elevated transition-colors duration-150
Layout: Everything on ONE line. Left-to-right:
[interactive control] [title truncate flex-1] [pills/badges] [metadata] [actions]
Title: text-sm font-medium truncate flex-1 min-w-0
Metadata: text-[11px] shrink-0, right-aligned before actions
Actions: ghost icon buttons h-7 w-7, last in row
List view vs card view: If a page shows items as a list (todos, upcoming),
use single-line rows with hover:bg-card-elevated — no borders, no shadow,
no multi-line content. If a page shows items as a grid of cards (projects,
people, locations), use the Card component with p-5, multi-line content, and
hover:shadow-lg hover:shadow-accent/5.
Widget List Row (dashboard widgets — more compact)
flex items-center gap-2 py-1.5 px-2 rounded-md
hover:bg-card-elevated transition-colors duration-150
Icon wrapped in p-1.5 rounded-md bg-{color}/10
alongside CardTitle
"View all" or count label: text-xs text-muted-foreground
Priority Pills
text-[9px] px-1.5 py-0.5 rounded-full font-medium
High: bg-red-500/20 text-red-400
Medium: bg-yellow-500/20 text-yellow-400
Low: bg-green-500/20 text-green-400
Type Tags
text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide
Todo: bg-blue-500/15 text-blue-400
Event: bg-purple-500/15 text-purple-400
Reminder: bg-orange-500/15 text-orange-400
Week Timeline (today highlight)
bg-accent/10 border border-accent/30 rounded-lg
shadow-[0_0_12px_hsl(var(--accent-color)/0.15)]
6. Navigation
Base: flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium
transition-all duration-200 border-l-2
Active: bg-accent/15 text-accent border-accent
Inactive: text-muted-foreground hover:bg-accent/10 hover:text-accent border-transparent
Icon: h-5 w-5 shrink-0
Logo
font-heading text-xl font-bold tracking-tight text-accent
Logout
hover:bg-destructive/10 hover:text-destructive
7. Animations & Transitions
Keyframes
| Name |
Effect |
Duration |
Easing |
fade-in |
opacity 0 → 1 |
300ms |
ease-out |
slide-up |
opacity 0→1 + Y(8px→0) |
400ms |
ease-out |
Staggered Entry (dashboard sections)
animationDelay: '50ms' / '100ms' / '150ms'
animationFillMode: 'backwards'
Standard Transitions
| Usage |
Classes |
| Interactive elements |
transition-all duration-200 |
| Color changes |
transition-colors duration-150 |
| Sidebar collapse |
transition-all duration-300 |
8. Scrollbar
scrollbar-width: thin;
scrollbar-color: hsl(0 0% 20%) transparent; /* #333 thumb on transparent */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb:hover { background: hsl(var(--accent-color) / 0.5); }
9. Design Principles
- Dark-first: Everything is designed for the dark palette. There is no light mode.
- Accent as highlight, not flood: The accent color draws attention to interactive/important elements. It should never dominate a section.
- 10% opacity backgrounds: Feature colors (blue, purple, orange, etc.) are always at 10-20% opacity for backgrounds, with 400-level text on top. Never use full-saturation backgrounds.
- Data density: Compact rows, small text for metadata, efficient use of space. This is a productivity tool, not a marketing page.
- Consistent card pattern: Every distinct content section is a Card. Cards have uniform padding (p-5), title styling, and hover behavior.
- Typography hierarchy: Sora for headings/numbers, DM Sans for body. This distinction creates visual rhythm without extra decoration.
- Subtle motion: Hover glows, fade-ins, and slide-ups. No bouncing, spinning, or attention-grabbing animations.
- Icon consistency: All icons from Lucide React, sized at
h-5 w-5 for nav/actions, h-4 w-4 for inline, h-8 w-8 for empty states.