Premium Frontend Design Skill
This skill guides creation of production-grade frontend interfaces that feel ALIVE — not generic, not copy-paste, but genuinely crafted experiences that users remember.
"The difference between a good interface and an unforgettable one is intentionality in every pixel."
Dependencies (Flexible — Choose What Fits)
This skill is framework-flexible. Pick packages based on user preference and project needs.
Core 3D (for WebGL templates) pnpm add three @react-three/fiber @react-three/drei
Animation (choose based on user preference) Library Best For Complexity Bundle Size CSS/Tailwind Simple transitions, micro-interactions Low 0KB Framer Motion React-native feel, layout animations, gestures Medium ~30KB GSAP Complex timelines, scroll-triggered, text effects High ~60KB GSAP + Club SplitText, ScrollTrigger, MorphSVG High ~80KB
Framer Motion (simpler, React-idiomatic)
pnpm add framer-motion
GSAP (powerful, timeline-based)
pnpm add gsap @gsap/react
Note: SplitText, ScrollTrigger require GSAP Club license
Decision Guide:
User says "simple" or "lightweight" → CSS + Framer Motion User says "complex animations" or "scroll effects" → GSAP User says "text animations" or "split text" → GSAP + SplitText User doesn't specify → Default to Framer Motion (simpler API) Optional Enhancements
Mesh gradients (for mesh-gradient-hero)
pnpm add @paper-design/shaders-react
Icons
pnpm add lucide-react
Charts/Sparklines (for dashboards)
pnpm add recharts
or lightweight: pnpm add @visx/shape @visx/scale
Browser Compatibility Notes backdrop-filter: Not supported in Firefox < 103 (add fallback bg) WebGL: Provide CSS fallback for older devices @starting-style: Chrome 117+, Safari 17.4+ (progressive enhancement) Core Philosophy The "Alive" Principle
An interface feels alive when:
It breathes: Subtle ambient animations, particles, or shader effects create constant but non-distracting motion It responds: Micro-interactions acknowledge every user action with satisfying feedback It has depth: Layers, parallax, glassmorphism, and shadows create dimensional space It surprises: At least one element breaks expectations in a delightful way Design Thinking (Before ANY Code)
Before writing a single line, answer these:
Purpose: What problem does this solve? Who uses it?
Tone: Pick ONE extreme direction (not a blend):
Brutally minimal Maximalist chaos Retro-futuristic / Cyberpunk Organic / Natural Luxury / Refined Playful / Toy-like Editorial / Magazine Brutalist / Raw Art Deco / Geometric Industrial / Utilitarian Bio-luminescent / Sci-fi Mission Control / Technical
The One Thing: What single element will someone remember? Every great interface has a signature moment.
Constraints: Framework, performance budgets, accessibility requirements.
CRITICAL: Bold maximalism and refined minimalism both work. The key is intentionality, not intensity. A single, perfectly-executed animation beats 50 mediocre ones.
Wow + Clarity Framework
Use this whenever the brief is vague or when you need to justify design decisions. The goal is wow factor with purpose.
- Hierarchy Guardrails 1 hero flourish (shader, particle system, or globe). Everything else supports readability. 1 supporting flourish (micro-interactions, animated stat card, or glowing CTA). No more. Layout rule: Hero (wild) → Content blocks (calm) → Proof (calm) → CTA (highlighted). If the page has more than one scroll-length of copy, every second section should be mostly static.
- Typography Discipline Max 2 headliner fonts (display + body). Monospace only for data. Headline letter-spacing ≥ -0.04em. Anything tighter kills readability. Body width target: 55–75 characters per line on desktop, 35–45 on mobile. Always pair big display text with a plain supporting sentence under 80 characters.
- Color & Contrast Rules Limit neon usage to primary CTA + 1 accent. Everything else stays in zinc/neutral palette. If background is busy (shader, gradients, particles), add a bg-black/70 or bg-slate-950/70 scrim behind text. Keep contrast ratios ≥ 4.5:1 for body copy even if the aesthetic is cyberpunk. Add a grayscale preview check before shipping: if it looks muddy, dial the palette back.
- Motion Throttle Default: CSS or Framer Motion with durations ≤ 400ms, easing cubic-bezier(0.34, 1.56, 0.64, 1). Escalate to GSAP/WebGL only if the brief explicitly asks for cinematic or interactive experiences. Max 1 continuous animation per viewport (e.g., shader OR wave bars, not both). Provide a “calm mode”: disable non-essential motion when prefers-reduced-motion is on OR when user scrolls past hero.
- When Requirements Are Vague Situation Default Optional Upgrade User only says “clean SaaS” mesh-gradient-hero + bento-grid Swap hero background for CPPN if they later ask for “more energy” User says “dashboard” with no flair bento-grid + dashboard-widgets + CSS glow pills Add digital-liquid shader only after data viz is signed off User says “hero section” but nothing else Text-first layout + CSS gradient Offer shader/globe as a suggestion, never as default
If the prompt does not explicitly mention WebGL, assume CSS-first and opt-in to shaders only when the user embraces the cost.
Anti-Patterns (NEVER Do This) Visual Anti-Patterns
❌ White/light backgrounds as default (dark mode is premium) ❌ Generic gradients (purple-to-blue on white is AI slop) ❌ Evenly-distributed, timid color palettes ❌ Static, lifeless backgrounds ❌ Cookie-cutter component layouts ❌ Missing loading/transition states ❌ Jarring, un-eased animations
Typography Anti-Patterns
❌ Inter, Roboto, Arial, system fonts for headlines ❌ Same font for everything ❌ Default line-heights and letter-spacing ❌ Boring, predictable type scales
Code Anti-Patterns
❌ Inline styles scattered randomly ❌ No CSS variables for theming ❌ Animations without will-change or GPU acceleration ❌ Canvas/WebGL without requestAnimationFrame ❌ Missing cleanup in useEffect
Design System 1. Color Architecture
Rule: ONE dominant accent, everything else supports it.
// Premium Dark Theme (Default) const colors = { // Backgrounds (layer from darkest to lightest) bg: { void: '#000000', // True black for maximum contrast primary: '#050505', // Main background elevated: '#0a0a0a', // Cards, modals subtle: '#111111', // Hover states },
// Glass surfaces glass: { bg: 'rgba(255, 255, 255, 0.03)', border: 'rgba(255, 255, 255, 0.08)', hover: 'rgba(255, 255, 255, 0.06)', },
// Text hierarchy text: { primary: '#ffffff', secondary: '#a1a1aa', // zinc-400 muted: '#71717a', // zinc-500 ghost: '#3f3f46', // zinc-700 },
// Accent (choose ONE per project) accent: '#ff4d00', // Neon Orange // accent: '#00f3ff', // Neon Cyan // accent: '#ccff00', // Neon Lime // accent: '#F5E445', // Premium Yellow // accent: '#a855f7', // Electric Purple }
Accent Usage Rules:
Primary actions: Full accent color Secondary elements: Accent at 20% opacity Borders/lines: Accent at 30% opacity Glows: Accent with blur, 40-60% opacity Never use accent for large background areas 2. Typography System
Rule: Display font for impact, Body font for reading, Mono for data.
/ Tier 1: Display/Headlines - BOLD, characterful / --font-display: 'Chakra Petch', 'Orbitron', 'Bebas Neue', 'Playfair Display';
/ Tier 2: Headings - Geometric, modern / --font-heading: 'Manrope', 'Outfit', 'Syne', 'Space Grotesk';
/ Tier 3: Body - Clean, highly legible / --font-body: 'Plus Jakarta Sans', 'DM Sans', 'Satoshi', 'General Sans';
/ Tier 4: Data/Code - ALWAYS monospace / --font-mono: 'JetBrains Mono', 'Fira Code', 'IBM Plex Mono';
Typography Patterns:
/ Hero Headlines: Massive, tight, aggressive / .headline { font-family: var(--font-display); font-size: clamp(3rem, 12vw, 10rem); font-weight: 800; line-height: 0.9; letter-spacing: -0.03em; text-transform: uppercase; }
/ Section Titles / .section-title { font-family: var(--font-heading); font-size: clamp(1.5rem, 4vw, 3rem); font-weight: 700; letter-spacing: -0.02em; }
/ Technical Labels / .label { font-family: var(--font-mono); font-size: 0.75rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); }
/ Data Display / .data { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
- Spacing & Layout
Rule: Asymmetry creates interest. Grids are starting points, not prisons.
/ Spacing scale (use consistently) / --space-1: 0.25rem; / 4px / --space-2: 0.5rem; / 8px / --space-3: 0.75rem; / 12px / --space-4: 1rem; / 16px / --space-6: 1.5rem; / 24px / --space-8: 2rem; / 32px / --space-12: 3rem; / 48px / --space-16: 4rem; / 64px / --space-24: 6rem; / 96px / --space-32: 8rem; / 128px /
/ Use generous padding on containers / .container { padding-inline: clamp(1rem, 5vw, 4rem); }
/ Hero sections need breathing room / .hero { min-height: 100vh; padding-block: var(--space-32); }
Bento Grid Pattern (for dashboards):
.bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(150px, auto); gap: var(--space-4); }
/ Feature card spans / .card-hero { grid-column: span 2; grid-row: span 2; } .card-wide { grid-column: span 2; } .card-tall { grid-row: span 2; }
Visual Effects Library 1. Glassmorphism (The Right Way) .glass { background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; }
/ Elevated glass (for modals, dropdowns) / .glass-elevated { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.05); }
-
CRT Scanlines Overlay .scanlines::before { content: ''; position: fixed; inset: 0; background: repeating-linear-gradient( 0deg, rgba(0, 0, 0, 0.1) 0px, rgba(0, 0, 0, 0.1) 1px, transparent 1px, transparent 2px ); pointer-events: none; z-index: 9999; }
-
Film Grain Texture .grain::before { content: ''; position: fixed; inset: 0; opacity: 0.03; pointer-events: none; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); }
-
Tech Grid Background .tech-grid { background-image: linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px); background-size: 60px 60px; }
-
Neon Glow Effects / Text glow / .neon-text { text-shadow: 0 0 10px currentColor, 0 0 20px currentColor, 0 0 40px currentColor, 0 0 80px currentColor; }
/ Box glow / .neon-box { box-shadow: 0 0 20px var(--accent-alpha-40), 0 0 40px var(--accent-alpha-20), inset 0 0 20px var(--accent-alpha-10); }
/ Border glow / .neon-border { border: 1px solid var(--accent); box-shadow: 0 0 10px var(--accent-alpha-50), inset 0 0 10px var(--accent-alpha-20); }
Animation Patterns Philosophy Entrance animations: Use once, make them count Micro-interactions: Subtle, fast (150-300ms) Ambient motion: Infinite, very slow, non-distracting Page transitions: Smooth, coordinated Choosing the Right Tool Need Use Why Hover/focus states CSS Zero JS, instant Simple entrance CSS keyframes Lightweight Layout animations Framer Motion layout prop magic Gesture-based Framer Motion Built-in drag/pan Scroll-triggered GSAP ScrollTrigger Most powerful Text splitting GSAP SplitText Industry standard Complex timelines GSAP Precise control SVG morphing GSAP MorphSVG No alternative
Default to simpler solutions. Escalate complexity only when needed.
CSS Keyframes Library @keyframes fade-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes scale-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
@keyframes slide-in-right { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
@keyframes pulse-glow { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
@keyframes rotate-slow { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes scan-line { 0% { transform: translateY(-100%); } 100% { transform: translateY(100vh); } }
Staggered Entrance Pattern .stagger-container > * { opacity: 0; animation: fade-up 0.6s ease-out forwards; }
.stagger-container > :nth-child(1) { animation-delay: 0.1s; } .stagger-container > :nth-child(2) { animation-delay: 0.2s; } .stagger-container > :nth-child(3) { animation-delay: 0.3s; } .stagger-container > :nth-child(4) { animation-delay: 0.4s; } .stagger-container > *:nth-child(5) { animation-delay: 0.5s; }
CSS-Only Patterns (Zero Dependencies) / View transition entrance (Chrome 111+, Safari 18+) / @supports (view-transition-name: none) { .card { view-transition-name: card; }
::view-transition-old(card), ::view-transition-new(card) { animation-duration: 0.3s; } }
/ Scroll-driven animations (Chrome 115+) / @supports (animation-timeline: scroll()) { .parallax-bg { animation: parallax linear; animation-timeline: scroll(); }
@keyframes parallax { from { transform: translateY(0); } to { transform: translateY(-30%); } } }
/ Hover with spring-like feel / .spring-hover { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } .spring-hover:hover { transform: scale(1.05); }
/ Glow pulse / .glow-pulse { animation: glow-pulse 2s ease-in-out infinite; } @keyframes glow-pulse { 0%, 100% { box-shadow: 0 0 20px var(--accent-alpha-40); } 50% { box-shadow: 0 0 40px var(--accent-alpha-60); } }
GSAP Patterns (When You Need Power) // Stagger entrance on scroll gsap.from('.card', { scrollTrigger: { trigger: '.cards-section', start: 'top 80%', }, y: 60, opacity: 0, duration: 0.8, stagger: 0.1, ease: 'power3.out', });
// Text scramble effect const scrambleText = (el: HTMLElement, text: string) => { const chars = '!<>-_\/[]{}—=+*^?#'; let iteration = 0;
const interval = setInterval(() => { el.innerText = text .split('') .map((char, i) => i < iteration ? char : chars[Math.floor(Math.random() * chars.length)] ) .join('');
if (iteration >= text.length) clearInterval(interval);
iteration += 1/3;
}, 30); };
// Smooth parallax gsap.to('.parallax-bg', { scrollTrigger: { scrub: 1, }, y: '-30%', ease: 'none', });
Framer Motion Patterns
// Page transitions
// Hover glow effect
// Stagger children <motion.div initial="hidden" animate="visible" variants={{ hidden: {}, visible: { transition: { staggerChildren: 0.1 } }, }}
{items.map(item => (
))}
3D & WebGL Patterns Tech Stack npm install three @react-three/fiber @react-three/drei
Basic Scene Setup import { Canvas } from '@react-three/fiber'; import { Stars, Float, MeshDistortMaterial } from '@react-three/drei';
const Scene = () => (
);
Particle Sphere (Data Globe)
const ParticleSphere = ({ count = 3000, color = '#ff4d00' }) => {
const ref = useRef
const positions = useMemo(() => { const pos = new Float32Array(count * 3); for (let i = 0; i < count; i++) { const theta = Math.random() * Math.PI * 2; const phi = Math.acos(Math.random() * 2 - 1); const r = 2; pos[i * 3] = r * Math.sin(phi) * Math.cos(theta); pos[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta); pos[i * 3 + 2] = r * Math.cos(phi); } return pos; }, [count]);
useFrame(() => { if (ref.current) ref.current.rotation.y += 0.001; });
return (
Sentient Core (AI Brain)
const SentientCore = () => (
Performance Rules Always use requestAnimationFrame via useFrame Reduce particle counts on mobile (check window.innerWidth) Use useMemo for geometry/position calculations Cleanup animations in useEffect return Set transparent and opacity for depth sorting Component Patterns 1. Loading + Page Transitions
Default (ship this unless user asks otherwise):
Skeleton placeholders on cards/sections (see shimmer pattern above) Simple fade/slide page transition using CSS or Framer Motion route transitions
Optional Cinematic Mode (only when the user wants a narrative boot sequence and there's time/budget):
GSAP-powered preloader with boot logs, text scramble, shader/WebGL background Coordinated timeline that fades into actual content once data is ready 2. Glassmorphic Navbar
- Live Terminal const Terminal = ({ logs }) => { const scrollRef = useRef();
useEffect(() => { scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight); }, [logs]);
return (
- Stat Cards with Sparklines
const StatCard = ({ icon, label, value, change, trend }) => (
<motion.div
whileHover={{ scale: 1.02, boxShadow: '0 0 30px var(--accent-alpha-30)' }}
className="glass p-4"
<div className="flex justify-between"> <div className="p-2 rounded-lg bg-accent/10 text-accent">{icon}</div> <span className={change > 0 ? 'text-green-400' : 'text-red-400'}> {change > 0 ? '+' : ''}{change}% </span> </div> <div className="mt-4 font-mono text-2xl font-bold">{value}</div> <div className="text-xs text-zinc-500 uppercase">{label}</div> <Sparkline data={trend} className="mt-2 h-8" />);
Quality Checklist
Before shipping, verify:
Visual Dark mode is default and premium-feeling ONE dominant accent color used consistently Glass effects have proper blur AND borders Scanlines or grain overlay for texture No pure white text on dark (#f4f4f5 max) Typography Display font for headlines (not Inter/Roboto) Monospace for ALL data/code/numbers Proper hierarchy (3-4 distinct levels) Tracking adjusted for large text Animation Entrance animations on load (staggered) Hover states with transform AND glow Smooth 60fps for all canvas No animation without purpose Code CSS variables for all colors Responsive (mobile-first) Cleanup functions in useEffect Error boundaries for 3D content Accessibility (Premium ≠ Inaccessible)
Great design is inclusive. These aren't optional.
Motion Sensitivity / ALWAYS respect user preferences / @media (prefers-reduced-motion: reduce) { , ::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }
// React hook for motion preference const usePrefersReducedMotion = () => { const [prefersReduced, setPrefersReduced] = useState(false);
useEffect(() => { const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); setPrefersReduced(mq.matches); const handler = (e: MediaQueryListEvent) => setPrefersReduced(e.matches); mq.addEventListener('change', handler); return () => mq.removeEventListener('change', handler); }, []);
return prefersReduced; };
// Usage const shouldAnimate = !usePrefersReducedMotion();
WebGL Accessibility // Always mark decorative 3D as hidden
// Provide text alternatives for meaningful 3D
Color Contrast Element Minimum Ratio Target Body text 4.5:1 7:1 Large text (18px+) 3:1 4.5:1 UI components 3:1 4.5:1 Decorative N/A N/A / Safe text colors on dark backgrounds / --text-primary: #ffffff; / ✓ 21:1 on #000 / --text-secondary: #a1a1aa; / ✓ 7.2:1 on #000 / --text-muted: #71717a; / ✓ 4.6:1 on #000 / --text-ghost: #52525b; / ⚠ 3.2:1 - decorative only /
Keyboard Navigation
// Interactive 3D elements need keyboard support
// Focus indicators for glass components .glass:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
Mobile & Responsive Strategy
Premium experiences adapt gracefully. Don't just shrink — reimagine.
WebGL Fallbacks // Detect low-power devices const useIsLowPowerDevice = () => { const [isLowPower, setIsLowPower] = useState(false);
useEffect(() => { const isLow = window.innerWidth < 768 || navigator.hardwareConcurrency <= 4 || /Android|iPhone|iPad/.test(navigator.userAgent); setIsLowPower(isLow); }, []);
return isLowPower; };
// Usage in hero components const HeroBackground = () => { const isLowPower = useIsLowPowerDevice();
if (isLowPower) {
return
return
Performance Budgets
Device JS Budget Animation Target
Desktop < 500KB 60fps WebGL
Tablet < 300KB 30fps or CSS-only
Mobile < 150KB CSS-only, no WebGL
Touch Interactions
// Replace hover with tap/long-press on mobile
// Increase touch targets .button { min-height: 44px; / Apple HIG / min-width: 44px; padding: 12px 24px; }
Responsive Typography / Fluid type scale / --text-hero: clamp(2.5rem, 8vw, 7rem); --text-h1: clamp(2rem, 5vw, 4rem); --text-h2: clamp(1.5rem, 3vw, 2.5rem); --text-body: clamp(1rem, 2vw, 1.125rem);
/ Reduce letter-spacing on mobile / @media (max-width: 768px) { .headline { letter-spacing: -0.02em; / Less aggressive than desktop / } }
Loading & Error States
Premium UX handles every state beautifully.
Skeleton Loaders
// Glass skeleton with shimmer
const Skeleton = ({ className }: { className?: string }) => (
<div
className={relative overflow-hidden rounded-lg bg-white/5 ${className}}
<div className="absolute inset-0 -translate-x-full animate-[shimmer_2s_infinite] bg-gradient-to-r from-transparent via-white/10 to-transparent" />