- Complete UI refactor with charcoal/bone color palette - Add Space Grotesk font for headlines, Inter for body - Update all 24+ pages with new design system - Add LinearPrimitives reusable components - Improve dark mode support throughout - Add subtle micro-animations and transitions
8.7 KiB
8.7 KiB
Linear-Inspired Design System
Version: 2.0.0 Date: 2025-12-12 Status: 🚀 Implementing
🎯 Design Philosophy
Inspired by Linear, our design prioritizes:
- Speed Over Spectacle - UI should feel instant
- Keyboard-First - Power users can navigate without a mouse
- Clarity Over Decoration - Every element serves a purpose
- Minimalist Density - More information, less noise
- Subtle Motion - Micro-animations guide, never distract
🎨 Color Palette
Core Colors (Charcoal & Bone)
/* Light Mode ("Bone") */
--color-bg-primary: #FFFFFF; /* Pure white */
--color-bg-secondary: #FAFAFA; /* Off-white */
--color-bg-tertiary: #F5F5F5; /* Light gray */
--color-bg-elevated: #FFFFFF; /* Cards, modals */
--color-text-primary: #171717; /* Near black */
--color-text-secondary: #525252; /* Dark gray */
--color-text-tertiary: #A3A3A3; /* Mid gray */
--color-text-quaternary: #D4D4D4; /* Light gray (disabled) */
--color-border-default: #E5E5E5; /* Subtle border */
--color-border-subtle: #F5F5F5; /* Very subtle */
--color-border-strong: #D4D4D4; /* Emphasis */
/* Dark Mode ("Charcoal") */
--color-bg-primary: #0A0A0A; /* Near black */
--color-bg-secondary: #171717; /* Charcoal */
--color-bg-tertiary: #262626; /* Dark gray */
--color-bg-elevated: #1F1F1F; /* Cards, modals */
--color-text-primary: #FAFAFA; /* Off-white */
--color-text-secondary: #A3A3A3; /* Mid gray */
--color-text-tertiary: #737373; /* Dark gray */
--color-text-quaternary: #525252; /* Darker gray (disabled) */
--color-border-default: #262626; /* Subtle border */
--color-border-subtle: #1F1F1F; /* Very subtle */
--color-border-strong: #404040; /* Emphasis */
Accent Colors (Minimal, Purposeful)
/* Accent - Desaturated Blue (Linear-style) */
--color-accent: #5E6AD2; /* Primary accent */
--color-accent-hover: #6E7AE2; /* Hover state */
--color-accent-muted: rgba(94, 106, 210, 0.15);
/* Status Colors (Desaturated) */
--color-success: #22C55E; /* Green - success */
--color-success-muted: rgba(34, 197, 94, 0.15);
--color-warning: #EAB308; /* Yellow - warning */
--color-warning-muted: rgba(234, 179, 8, 0.15);
--color-error: #EF4444; /* Red - error */
--color-error-muted: rgba(239, 68, 68, 0.15);
--color-info: #5E6AD2; /* Same as accent */
--color-info-muted: rgba(94, 106, 210, 0.15);
Semantic Mapping
| Usage | Light Mode | Dark Mode |
|---|---|---|
| Page background | #FFFFFF |
#0A0A0A |
| Card background | #FFFFFF |
#171717 |
| Sidebar background | #FAFAFA |
#0F0F0F |
| Primary text | #171717 |
#FAFAFA |
| Secondary text | #525252 |
#A3A3A3 |
| Borders | #E5E5E5 |
#262626 |
| Focus ring | #5E6AD2 |
#5E6AD2 |
📝 Typography
Font Stack
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
Type Scale
| Name | Size | Weight | Line Height | Usage |
|---|---|---|---|---|
xs |
11px | 400 | 1.4 | Labels, metadata |
sm |
13px | 400 | 1.5 | Secondary text |
base |
14px | 400 | 1.6 | Body text |
lg |
16px | 500 | 1.5 | Subheadings |
xl |
18px | 600 | 1.4 | Headings |
2xl |
24px | 600 | 1.3 | Page titles |
3xl |
30px | 700 | 1.2 | Hero text |
Letter Spacing
--tracking-tight: -0.02em; /* Headings */
--tracking-normal: -0.01em; /* Body */
--tracking-wide: 0.02em; /* All caps labels */
📐 Spacing & Layout
Spacing Scale
--space-0: 0;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;
Border Radius
--radius-sm: 4px; /* Buttons, inputs */
--radius-md: 6px; /* Cards */
--radius-lg: 8px; /* Modals */
--radius-xl: 12px; /* Large cards */
--radius-full: 9999px; /* Pills, avatars */
✨ Micro-Animations
Timing Functions
/* Linear's signature easing */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in-out-expo: cubic-bezier(0.87, 0, 0.13, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
Animation Durations
--duration-instant: 50ms; /* Color changes */
--duration-fast: 100ms; /* Hover states */
--duration-normal: 150ms; /* Most transitions */
--duration-slow: 250ms; /* Modals, drawers */
--duration-slower: 400ms; /* Page transitions */
Standard Transitions
/* Hover state */
.interactive {
transition:
background-color var(--duration-fast) var(--ease-out-expo),
color var(--duration-fast) var(--ease-out-expo),
border-color var(--duration-fast) var(--ease-out-expo),
transform var(--duration-normal) var(--ease-spring);
}
/* Subtle press effect */
.interactive:active {
transform: scale(0.98);
}
/* Focus ring animation */
.focus-ring:focus-visible {
box-shadow: 0 0 0 2px var(--color-accent);
transition: box-shadow var(--duration-normal) var(--ease-out-expo);
}
Component-Specific Animations
/* Button hover glow */
@keyframes button-glow {
0% { box-shadow: 0 0 0 0 rgba(94, 106, 210, 0.4); }
100% { box-shadow: 0 0 0 4px rgba(94, 106, 210, 0); }
}
/* Card hover lift */
@keyframes card-lift {
0% { transform: translateY(0); }
100% { transform: translateY(-2px); }
}
/* Fade in */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* Slide up */
@keyframes slide-up {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* Scale in */
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
/* Progress bar shimmer */
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
🧱 Component Patterns
Buttons
/* Base button */
.btn {
height: 32px;
padding: 0 12px;
font-size: 13px;
font-weight: 500;
border-radius: var(--radius-sm);
transition: all var(--duration-fast) var(--ease-out-expo);
}
/* Primary (accent) */
.btn-primary {
background: var(--color-accent);
color: white;
}
.btn-primary:hover {
background: var(--color-accent-hover);
}
/* Secondary (ghost) */
.btn-secondary {
background: transparent;
color: var(--color-text-secondary);
border: 1px solid var(--color-border-default);
}
.btn-secondary:hover {
background: var(--color-bg-tertiary);
color: var(--color-text-primary);
}
Cards
.card {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-md);
transition: border-color var(--duration-fast) var(--ease-out-expo);
}
.card:hover {
border-color: var(--color-border-default);
}
Inputs
.input {
height: 36px;
padding: 0 12px;
font-size: 14px;
background: var(--color-bg-primary);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-sm);
transition: all var(--duration-fast) var(--ease-out-expo);
}
.input:focus {
border-color: var(--color-accent);
box-shadow: 0 0 0 3px var(--color-accent-muted);
}
📱 Responsive Breakpoints
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
🌙 Dark Mode Strategy
- Default: Follow system preference (
prefers-color-scheme) - Toggle: Available in settings
- Persistence: localStorage key
theme - Transition: Smooth 150ms color transition on toggle
♿ Accessibility
- Focus visible: All interactive elements have visible focus ring
- Contrast: Minimum 4.5:1 for text, 3:1 for large text/graphics
- Motion: Respect
prefers-reduced-motion - Touch targets: Minimum 44×44px
- Keyboard: Full keyboard navigation support
🚀 Implementation Priority
-
Phase 1: Colors & Typography (2-3 hours)
- Update
tailwind.config.jswith new color tokens - Update
index.csswith new font imports - Create CSS custom properties
- Update
-
Phase 2: Core Components (3-4 hours)
- Update Button component
- Update Card component
- Update Input component
- Update focus states
-
Phase 3: Layouts (2-3 hours)
- Update Sidebar
- Update PageHeader
- Update Navigation
-
Phase 4: Micro-Animations (2-3 hours)
- Add hover transitions
- Add press effects
- Add page transitions
-
Phase 5: Polish (2-3 hours)
- Dark mode refinement
- Accessibility audit
- Performance check