Design Language System
Use this guide when creating or modifying UI components to ensure visual consistency across the portfolio.
Colour Tokens
Base Palette
| Token |
OKLCH |
Hex |
Usage |
--background |
oklch(0.985 0.005 90) |
#FAF9F7 |
Page background (warm paper white) |
--foreground |
oklch(0.15 0.01 270) |
#1F1F23 |
Primary text (deep charcoal) |
--card |
oklch(1 0 0) |
#FFFFFF |
Card backgrounds |
--card-foreground |
oklch(0.15 0.01 270) |
#1F1F23 |
Card text |
--muted |
oklch(0.96 0.005 90) |
#F3F2F0 |
Muted backgrounds |
--muted-foreground |
oklch(0.45 0.01 270) |
#6B6B73 |
Secondary text |
--border |
oklch(0.90 0.005 90) |
#E5E4E2 |
Borders |
Coral Accent (Primary)
| Token |
OKLCH |
Hex |
Usage |
--primary |
oklch(0.60 0.18 25) |
#E07356 |
Primary buttons, links, accents |
--primary-foreground |
oklch(0.98 0.01 25) |
#FDF8F7 |
Text on primary |
--ring |
oklch(0.70 0.15 25) |
#EBA08A |
Focus rings, hover glows |
Typography
Fonts
- Sans (Body):
Figtree via --font-sans
- Mono (Code):
Geist Mono via --font-mono
Scale
| Variant |
Class |
Usage |
h1 |
text-5xl font-bold tracking-tight |
Page titles |
h2 |
text-3xl font-semibold tracking-tight |
Section headings |
h3 |
text-xl font-semibold |
Card titles |
body-lg |
text-lg leading-relaxed |
Introductions, emphasis |
body |
text-base |
Default body text |
body-sm |
text-sm |
Secondary content |
caption |
text-sm text-muted-foreground |
Metadata, dates |
label |
text-xs font-medium uppercase tracking-wider |
Labels, categories |
Heading Styles
- Letter-spacing:
-0.03em on h1, h2
- Use
tracking-tight class
- Colour:
text-foreground
Spacing
Rules
- Use
gap-* utilities for spacing between elements
- Avoid
margin-top, prefer margin-bottom or gap
- Use even numbers only:
gap-2, gap-4, gap-6, gap-8, gap-12
- Use
gap-* instead of gap-x-*/gap-y-* (cleaner, same behaviour in flex containers)
Spacing Scale (Even Numbers Only)
| Gap |
Size |
Purpose |
gap-2 |
8px |
Micro: icons + text, badges, inline elements |
gap-4 |
16px |
Standard: card content, grids, forms |
gap-6 |
24px |
Section: between related components |
gap-8 |
32px |
Macro: major page divisions, section separators |
gap-12 |
48px |
Page: hero to content transitions |
Guidelines
- Default to gap-4 when unsure
- Icon + heading pairs:
gap-2
- Card internal content:
gap-4
- Section containers:
gap-6 or gap-8
Background Effects (Moderate)
Gradient Orbs
.gradient-orb {
position: fixed;
border-radius: 50%;
filter: blur(120px);
opacity: 0.15;
pointer-events: none;
}
- Colour:
bg-rose-400
- Primary orb: 600px, top-right
- Secondary orb: 500px, bottom-left (opacity * 0.7)
Noise Texture
.noise-overlay {
opacity: 0.04;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml,...");
}
Animations
Priority: CSS First
Use CSS for all simple animations. Only use motion/react for scroll-triggered or complex sequenced animations.
CSS Specs
| Animation |
Duration |
Easing |
Properties |
| Hover states |
200ms |
ease-out |
All transitions |
| Page transitions |
250ms |
cubic-bezier(0.4, 0, 0.2, 1) |
Opacity, transform |
| Card hover |
200ms |
ease-out |
translateY(-2px), box-shadow |
| Button hover |
200ms |
ease-out |
scale(1.02), box-shadow |
Hover Effects
- Cards:
hover:-translate-y-0.5 + coral glow shadow
- Buttons:
hover:scale-[1.02] + shadow lift
- Links: Underline slides in from left via
::after
- Icons:
hover:scale-110 or subtle rotation
When to Use motion/react
- Scroll-triggered animations (fade-in on scroll)
- Complex multi-step sequences
- Gesture-based interactions
Component Patterns
Cards
className="rounded-2xl border border-border bg-card p-6 shadow-sm
transition-all duration-200 hover:-translate-y-0.5"
// Add coral glow on hover via inline style or CSS
Buttons
// Primary
className="rounded-full bg-primary text-primary-foreground px-6 py-3
font-medium transition-all duration-200 hover:scale-[1.02] hover:shadow-lg"
// Outline
className="rounded-full border-2 border-primary text-primary px-6 py-3
font-medium transition-all duration-200 hover:scale-[1.02]"
Badges
className="rounded-full bg-primary text-primary-foreground px-4 py-1.5
font-medium text-sm transition-all duration-200 hover:-translate-y-0.5"
Header
className="fixed top-0 left-0 z-50 w-full border-b border-border
bg-background/90 backdrop-blur-lg px-8 py-3"
Component Rules
- NEVER modify
components/ui/* (shadcn/ui primitives)
- Use composition via
components/shared/ wrappers
- Styling changes go in CSS or wrapper components
- Use
cn() utility for conditional classes
- Use CVA for component variants
Quick Reference
Coral Glow Shadow
style={{ boxShadow: `0 8px 30px -10px oklch(0.60 0.18 25 / 0.4)` }}
// Or use: shadow-[0_8px_30px_-10px_theme(colors.primary/0.4)]
Semantic Colours
- Primary text:
text-foreground
- Secondary text:
text-muted-foreground
- Accent:
text-primary
- Backgrounds:
bg-background, bg-card, bg-muted
- Borders:
border-border
1---2name: design-language-system-23description: Maintains visual consistency across portfolio UI components. Use when creating or modifying components, styling pages, or ensuring design consistency. Includes coral colour tokens (OKLCH), typography scale, spacing rules, animations, and component patterns.4---5
6# Design Language System
7
8Use this guide when creating or modifying UI components to ensure visual consistency across the portfolio.
9
10---
11
12## Colour Tokens
13
14### Base Palette
15
16| Token | OKLCH | Hex | Usage |
17|-------|-------|-----|-------|
18| `--background` | `oklch(0.985 0.005 90)` | `#FAF9F7` | Page background (warm paper white) |
19| `--foreground` | `oklch(0.15 0.01 270)` | `#1F1F23` | Primary text (deep charcoal) |
20| `--card` | `oklch(1 0 0)` | `#FFFFFF` | Card backgrounds |
21| `--card-foreground` | `oklch(0.15 0.01 270)` | `#1F1F23` | Card text |
22| `--muted` | `oklch(0.96 0.005 90)` | `#F3F2F0` | Muted backgrounds |
23| `--muted-foreground` | `oklch(0.45 0.01 270)` | `#6B6B73` | Secondary text |
24| `--border` | `oklch(0.90 0.005 90)` | `#E5E4E2` | Borders |
25
26### Coral Accent (Primary)
27
28| Token | OKLCH | Hex | Usage |
29|-------|-------|-----|-------|
30| `--primary` | `oklch(0.60 0.18 25)` | `#E07356` | Primary buttons, links, accents |
31| `--primary-foreground` | `oklch(0.98 0.01 25)` | `#FDF8F7` | Text on primary |
32| `--ring` | `oklch(0.70 0.15 25)` | `#EBA08A` | Focus rings, hover glows |
33
34---
35
36## Typography
37
38### Fonts
39- **Sans (Body)**: `Figtree` via `--font-sans`
40- **Mono (Code)**: `Geist Mono` via `--font-mono`
41
42### Scale
43
44| Variant | Class | Usage |
45|---------|-------|-------|
46| `h1` | `text-5xl font-bold tracking-tight` | Page titles |
47| `h2` | `text-3xl font-semibold tracking-tight` | Section headings |
48| `h3` | `text-xl font-semibold` | Card titles |
49| `body-lg` | `text-lg leading-relaxed` | Introductions, emphasis |
50| `body` | `text-base` | Default body text |
51| `body-sm` | `text-sm` | Secondary content |
52| `caption` | `text-sm text-muted-foreground` | Metadata, dates |
53| `label` | `text-xs font-medium uppercase tracking-wider` | Labels, categories |
54
55### Heading Styles
56- Letter-spacing: `-0.03em` on h1, h2
57- Use `tracking-tight` class
58- Colour: `text-foreground`
59
60---
61
62## Spacing
63
64### Rules
65- Use `gap-*` utilities for spacing between elements
66- Avoid `margin-top`, prefer `margin-bottom` or `gap`
67- Use even numbers only: `gap-2`, `gap-4`, `gap-6`, `gap-8`, `gap-12`
68- Use `gap-*` instead of `gap-x-*`/`gap-y-*` (cleaner, same behaviour in flex containers)
69
70### Spacing Scale (Even Numbers Only)
71
72| Gap | Size | Purpose |
73|-----|------|---------|
74| `gap-2` | 8px | Micro: icons + text, badges, inline elements |
75| `gap-4` | 16px | **Standard**: card content, grids, forms |
76| `gap-6` | 24px | Section: between related components |
77| `gap-8` | 32px | Macro: major page divisions, section separators |
78| `gap-12` | 48px | Page: hero to content transitions |
79
80### Guidelines
81- **Default to gap-4** when unsure
82- Icon + heading pairs: `gap-2`
83- Card internal content: `gap-4`
84- Section containers: `gap-6` or `gap-8`
85
86---
87
88## Background Effects (Moderate)
89
90### Gradient Orbs
91```css
92.gradient-orb {
93 position: fixed;
94 border-radius: 50%;
95 filter: blur(120px);
96 opacity: 0.15;
97 pointer-events: none;
98}
99```
100- Colour: `bg-rose-400`
101- Primary orb: 600px, top-right
102- Secondary orb: 500px, bottom-left (opacity * 0.7)
103
104### Noise Texture
105```css
106.noise-overlay {
107 opacity: 0.04;
108 mix-blend-mode: overlay;
109 background-image: url("data:image/svg+xml,...");
110}
111```
112
113---
114
115## Animations
116
117### Priority: CSS First
118Use CSS for all simple animations. Only use `motion/react` for scroll-triggered or complex sequenced animations.
119
120### CSS Specs
121
122| Animation | Duration | Easing | Properties |
123|-----------|----------|--------|------------|
124| Hover states | 200ms | ease-out | All transitions |
125| Page transitions | 250ms | cubic-bezier(0.4, 0, 0.2, 1) | Opacity, transform |
126| Card hover | 200ms | ease-out | translateY(-2px), box-shadow |
127| Button hover | 200ms | ease-out | scale(1.02), box-shadow |
128
129### Hover Effects
130- **Cards**: `hover:-translate-y-0.5` + coral glow shadow
131- **Buttons**: `hover:scale-[1.02]` + shadow lift
132- **Links**: Underline slides in from left via `::after`
133- **Icons**: `hover:scale-110` or subtle rotation
134
135### When to Use motion/react
136- Scroll-triggered animations (fade-in on scroll)
137- Complex multi-step sequences
138- Gesture-based interactions
139
140---
141
142## Component Patterns
143
144### Cards
145```tsx
146className="rounded-2xl border border-border bg-card p-6 shadow-sm
147 transition-all duration-200 hover:-translate-y-0.5"
148// Add coral glow on hover via inline style or CSS
149```
150
151### Buttons
152```tsx
153// Primary
154className="rounded-full bg-primary text-primary-foreground px-6 py-3
155 font-medium transition-all duration-200 hover:scale-[1.02] hover:shadow-lg"
156
157// Outline
158className="rounded-full border-2 border-primary text-primary px-6 py-3
159 font-medium transition-all duration-200 hover:scale-[1.02]"
160```
161
162### Badges
163```tsx
164className="rounded-full bg-primary text-primary-foreground px-4 py-1.5
165 font-medium text-sm transition-all duration-200 hover:-translate-y-0.5"
166```
167
168### Header
169```tsx
170className="fixed top-0 left-0 z-50 w-full border-b border-border
171 bg-background/90 backdrop-blur-lg px-8 py-3"
172```
173
174---
175
176## Component Rules
177
1781. **NEVER modify** `components/ui/*` (shadcn/ui primitives)
1792. Use composition via `components/shared/` wrappers
1803. Styling changes go in CSS or wrapper components
1814. Use `cn()` utility for conditional classes
1825. Use CVA for component variants
183
184---
185
186## Quick Reference
187
188### Coral Glow Shadow
189```tsx
190style={{ boxShadow: `0 8px 30px -10px oklch(0.60 0.18 25 / 0.4)` }}
191// Or use: shadow-[0_8px_30px_-10px_theme(colors.primary/0.4)]
192```
193
194### Semantic Colours
195- Primary text: `text-foreground`
196- Secondary text: `text-muted-foreground`
197- Accent: `text-primary`
198- Backgrounds: `bg-background`, `bg-card`, `bg-muted`
199- Borders: `border-border`