Design System - Spark App Template
Design Philosophy
Beautiful web applications transcend mere functionality—they evoke emotion and create memorable experiences. This guide provides comprehensive design principles and techniques for crafting exceptional interfaces.
Core Principles
Simplicity Through Reduction
- Remove until reaching the simplest effective solution
- Each element must justify its existence
- Complexity should be hidden, not eliminated
Material Honesty
- Digital materials have unique properties; embrace them
- Don't simulate physical materials poorly
- Use affordances that make sense in a digital context
Obsessive Detail
- Excellence emerges from hundreds of thoughtful micro-decisions
- Transitions, spacing, typography - every pixel matters
- Consistency compounds into quality
Coherent Design Language
- Every element should feel part of a unified system
- Establish rules, then follow them religiously
- Variations should be intentional, not accidental
Distinctive Visual Identity
- Create memorable aesthetics, not generic patterns
- Avoid "AI slop" aesthetics (centered layouts, purple gradients, overused fonts)
- Push beyond safe choices into distinctive territory
Design Courage
Committing to Bold Directions
Don't design timidly. Choose a strong direction and commit fully:
Brutally Minimal
- Stark black and white (or near-monochrome)
- Massive whitespace
- Unapologetic typography scale
- No decoration, pure function
- Think: Apple, Stripe, Linear
Atmospheric & Immersive
- Rich backgrounds (gradients, patterns, textures)
- Depth through layering
- Glassmorphism and blur effects
- Subtle animations creating ambiance
- Think: Vercel, Raycast, Arc Browser
Brutalist & Raw
- Exposed structure
- Monospace fonts everywhere
- Grid systems visible
- Terminal aesthetics
- Think: Developer tools, crypto platforms
Organic & Natural
- Warm earth tones
- Rounded corners (large radius)
- Hand-drawn elements
- Playful micro-interactions
- Think: Notion, Figma, Framer
Futuristic & Technical
- Sharp angles and geometry
- Neon accents
- Dark interfaces with bright highlights
- Motion that feels robotic/precise
- Think: Gaming platforms, tech showcases
Choose ONE direction. Don't mix brutalist grids with organic curves. Commit fully.
Spatial Composition
Beyond the Grid
While grids provide structure, exceptional interfaces know when to break free:
Asymmetry
- Intentional imbalance creates visual interest
- Place large elements off-center
- Use negative space asymmetrically
- Example: Hero section with content flush-left, image breaking right edge
Overlap & Layering
- Elements overlapping create depth
- Cards partially covering backgrounds
- Images bleeding out of containers
- Use z-index thoughtfully (don't stack more than 3 layers)
Diagonal Flow
- Break horizontal/vertical monotony
- Diagonal sections (clip-path or transform)
- Rotated elements (subtle: 2-5 degrees)
- Guides eye movement across page
Grid-Breaking Elements
- Establish a grid, then strategically break it
- Full-bleed images while content stays contained
- Elements that span multiple columns
- Floating elements anchored to grid points
Negative Space
Generous Space = Luxury
- Mobile: py-12 to py-16 between sections
- Desktop: py-20 to py-32 between sections
- Don't fear empty space
- Breathing room makes content feel premium
Controlled Density
- Dashboards can be denser (gap-2 to gap-4)
- Marketing content needs space (gap-8 to gap-12)
- Match density to context and user task
Atmospheric Backgrounds
Move beyond solid colors. Backgrounds set mood and create depth.
Gradient Techniques
Mesh Gradients (multiple radial gradients):
.atmospheric-bg {
background:
radial-gradient(circle at 20% 30%, oklch(0.75 0.15 330 / 0.3), transparent 50%),
radial-gradient(circle at 80% 70%, oklch(0.70 0.18 240 / 0.25), transparent 50%),
radial-gradient(circle at 50% 50%, oklch(0.65 0.12 180 / 0.2), transparent 70%),
oklch(0.98 0.01 75);
}Directional Gradients (subtle, not garish):
.subtle-gradient {
background: linear-gradient(
135deg,
oklch(0.98 0.01 85),
oklch(0.96 0.015 75)
);
}Animated Gradients (use sparingly):
@keyframes gradient-shift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.animated-gradient {
background: linear-gradient(
270deg,
oklch(0.97 0.02 330),
oklch(0.96 0.02 240),
oklch(0.97 0.02 180)
);
background-size: 200% 200%;
animation: gradient-shift 15s ease infinite;
}Geometric Patterns
Repeating Lines (subtle texture):
.lines-bg {
background-image: repeating-linear-gradient(
45deg,
transparent,
transparent 10px,
oklch(0.90 0.01 75 / 0.5) 10px,
oklch(0.90 0.01 75 / 0.5) 11px
);
}Grid Pattern:
.grid-bg {
background-size: 40px 40px;
background-image:
linear-gradient(oklch(0.85 0.01 75 / 0.2) 1px, transparent 1px),
linear-gradient(90deg, oklch(0.85 0.01 75 / 0.2) 1px, transparent 1px);
}Dot Pattern:
.dot-bg {
background-image: radial-gradient(
oklch(0.80 0.02 75 / 0.3) 1px,
transparent 1px
);
background-size: 20px 20px;
}Noise & Texture
SVG Noise (adds organic feel):
<svg style="position: absolute; width: 0; height: 0;">
<filter id="noise">
<feTurbulence
type="fractalNoise"
baseFrequency="0.9"
numOctaves="4"
stitchTiles="stitch"
/>
<feColorMatrix type="saturate" values="0" />
<feBlend in="SourceGraphic" mode="multiply" />
</filter>
</svg>.textured-bg {
background: oklch(0.97 0.01 75);
filter: url(#noise);
opacity: 0.05;
}CSS Grain (performance-friendly):
.grain-overlay {
position: relative;
}
.grain-overlay::after {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
opacity: 0.03;
pointer-events: none;
}Glassmorphism
.glass-card {
background: oklch(1 0 0 / 0.7);
backdrop-filter: blur(12px) saturate(180%);
border: 1px solid oklch(1 0 0 / 0.18);
box-shadow: 0 8px 32px oklch(0 0 0 / 0.1);
}Use when:
- Elements floating over rich backgrounds
- Modal dialogs, popovers
- Navigation bars over content
- Overlays that maintain context
Avoid when:
- Main content areas (readability suffers)
- Dense information (text needs solid backgrounds)
- Mobile devices (performance impact)
Micro-Interactions
Small moments of delight that make interfaces feel alive.
Hover Transformations
Lift on Hover:
.card-hover {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px oklch(0 0 0 / 0.15);
}Scale & Brighten:
.button-hover {
transition: all 0.2s ease;
}
.button-hover:hover {
transform: scale(1.02);
filter: brightness(1.1);
}Border Glow:
.glow-hover {
position: relative;
transition: border-color 0.3s ease;
}
.glow-hover::before {
content: '';
position: absolute;
inset: -2px;
border-radius: inherit;
padding: 2px;
background: linear-gradient(45deg, var(--primary), var(--accent));
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: 0;
transition: opacity 0.3s ease;
}
.glow-hover:hover::before {
opacity: 1;
}Loading States
Skeleton Screens (better than spinners):
export function SkeletonCard() {
return (
<div className="animate-pulse space-y-4 rounded-lg border p-6">
<div className="h-4 w-3/4 rounded bg-muted"></div>
<div className="h-4 w-1/2 rounded bg-muted"></div>
<div className="h-24 w-full rounded bg-muted"></div>
</div>
);
}Progress Indicators:
<div className="relative h-2 w-full overflow-hidden rounded-full bg-muted">
<motion.div
className="h-full bg-primary"
initial={{ x: '-100%' }}
animate={{ x: '0%' }}
transition={{ duration: 0.5 }}
style={{ width: `${progress}%` }}
/>
</div>Scroll-Triggered Animations
Fade & Slide In:
import { motion } from 'motion/react';
export function FadeInSection({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
}Staggered Children:
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
};
<motion.div variants={containerVariants} initial="hidden" animate="visible">
{items.map(item => (
<motion.div key={item.id} variants={itemVariants}>
{item.content}
</motion.div>
))}
</motion.div>Cursor Effects
Custom Cursor (desktop only):
.custom-cursor-area {
cursor: none;
}
.custom-cursor {
position: fixed;
width: 20px;
height: 20px;
border: 2px solid oklch(0.50 0.15 265);
border-radius: 50%;
pointer-events: none;
transform: translate(-50%, -50%);
transition: transform 0.1s ease, width 0.2s ease, height 0.2s ease;
z-index: 9999;
}
.custom-cursor-area:hover .custom-cursor {
width: 40px;
height: 40px;
}Proximity Effects (elements respond to cursor):
import { motion } from 'motion/react';
import { useState } from 'react';
export function ProximityCard() {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect();
setMousePosition({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};
return (
<motion.div
onMouseMove={handleMouseMove}
className="relative overflow-hidden rounded-lg border p-6"
style={{
background: `radial-gradient(circle at ${mousePosition.x}px ${mousePosition.y}px, oklch(0.96 0.02 265 / 0.3), transparent 60%)`
}}
>
Card content
</motion.div>
);
}Progressive Enhancement
Start with solid foundations, add enhancements for capable devices:
Motion Preferences
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}Performance Considerations
- Use
transformandopacityfor animations (GPU-accelerated) - Avoid animating
width,height,margin,padding - Debounce scroll/mouse events
- Use
will-changesparingly (only during animation) - Lazy load heavy effects below the fold
Color Psychology
Colors evoke emotions. Choose deliberately:
Trust & Stability: Blues, grays Energy & Urgency: Reds, oranges Growth & Success: Greens Creativity & Luxury: Purples Clarity & Optimism: Yellows (use sparingly) Sophistication: Black, deep blues, burgundy
Contrast for Meaning:
- High contrast = important, actionable
- Low contrast = secondary, supporting
- Color = meaning (but don't rely solely on color)
Responsive Design Principles
Mobile-First:
- Design for smallest screen first
- Progressively enhance for larger screens
- Touch targets minimum 44x44px
Breakpoints (Tailwind defaults):
- sm: 640px (large phones)
- md: 768px (tablets)
- lg: 1024px (laptops)
- xl: 1280px (desktops)
- 2xl: 1536px (large monitors)
Content Reorganization:
- Mobile: Stack vertically, single column
- Tablet: 2-column grids, collapsible sidebars
- Desktop: Multi-column layouts, persistent navigation
Accessibility Requirements
Design must be inclusive:
Color Contrast:
- WCAG AA: 4.5:1 for normal text, 3:1 for large text
- WCAG AAA: 7:1 for normal text, 4.5:1 for large text
- Use online contrast checkers for OKLCH values
Focus States:
- Visible focus indicators on all interactive elements
- Don't remove default outlines without replacing them
- Keyboard navigation must work everywhere
Semantic HTML:
- Use proper heading hierarchy (h1 → h6)
- Label all form inputs
- Alt text for all images
- ARIA labels where needed
Motion:
- Respect prefers-reduced-motion
- Never flash content faster than 3Hz
- Provide alternatives to motion-dependent UI
Design System Documentation
Maintain a living style guide:
- Color Palette: All colors with names, OKLCH values, usage
- Typography Scale: All sizes, weights, line-heights
- Spacing System: Standardized spacing values
- Component Library: All components with variants
- Animation Library: Reusable animation patterns
- Icon System: Icon usage guidelines
- Patterns: Common UI patterns (forms, tables, cards)
Anti-Patterns to Avoid
Don't:
- Center everything (especially long text)
- Use pure black (#000000) on pure white (#FFFFFF)
- Mix font families excessively (max 2-3)
- Animate everything
- Use low-contrast gray text (accessibility issue)
- Copy trends blindly (glassmorphism, neumorphism, etc.)
- Use default shadows/borders without customization
- Neglect empty/error/loading states
Do:
- Establish clear visual hierarchy
- Use consistent spacing system
- Design for real content (not Lorem Ipsum)
- Test with actual data volumes
- Consider edge cases (very long names, no data, etc.)
- Iterate based on user feedback
- Measure and optimize performance
Resources & Inspiration
Color Tools:
- OKLCH Color Picker: oklch.com
- Radix Colors: radix-ui.com/colors
- Contrast Checker: whocanuse.com
Typography:
- Google Fonts: fonts.google.com
- Font Pairing: fontpair.co
- Type Scale: type-scale.com
Inspiration:
- Godly: godly.website
- Awwwards: awwwards.com
- SaaS Landing Pages: saaslandingpage.com
- Component Gallery: component.gallery
Remember: The best design is invisible. When users accomplish their goals effortlessly, you've succeeded. Style should enhance function, never obscure it.