Back to skills
SkillHub ClubDesign ProductDesignerFrontend

frontend-design

Create components and pages following the Family Gifting Dashboard "Soft Modernity" design system. Use when building UI components, pages, or implementing design specifications. Includes Apple-inspired warmth, generous radii, diffused shadows, and token-optimized styling.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
0
Hot score
74
Updated
March 20, 2026
Overall rating
C1.1
Composite score
1.1
Best-practice grade
N/A

Install command

npx @skill-hub/cli install miethe-family-shopping-dashboard-frontend-design
ui-designdesign-systemfrontendcomponent-librarystyling

Repository

miethe/family-shopping-dashboard

Skill path: .claude/skills/frontend-design

Create components and pages following the Family Gifting Dashboard "Soft Modernity" design system. Use when building UI components, pages, or implementing design specifications. Includes Apple-inspired warmth, generous radii, diffused shadows, and token-optimized styling.

Open repository

Best for

Primary workflow: Design Product.

Technical facets: Designer, Frontend.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: miethe.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install frontend-design into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/miethe/family-shopping-dashboard before adding frontend-design to shared team environments
  • Use frontend-design for design workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: frontend-design
description: Create components and pages following the Family Gifting Dashboard "Soft Modernity" design system. Use when building UI components, pages, or implementing design specifications. Includes Apple-inspired warmth, generous radii, diffused shadows, and token-optimized styling.
---

# Frontend Design — Soft Modernity

Build UI components and pages following the Family Gifting Dashboard design system. This skill provides token-optimized references for the project's "Soft Modernity" aesthetic—Apple-inspired warmth, generous radii, diffused shadows, and mobile-first patterns.

## Design Philosophy: "Soft Modernity"

**Aesthetic**: Apple-inspired warmth with generous radii and diffused shadows
**Feel**: Welcoming, refined, familiar—like a well-designed iOS app
**Never**: Pure white backgrounds, harsh shadows, sharp corners, cold colors

### Core Characteristics

- **Warm Foundation**: Cream (#FAF8F5) base, never pure white
- **Generous Radii**: 10-48px border radius (cards 20-36px)
- **Diffused Shadows**: Soft, layered shadows with subtle borders
- **Warm Ink**: Brown-based text (#2D2520) instead of pure black
- **Status Colors**: Muted, harmonious (Mustard, Sage, Terracotta, Lavender)
- **Glassmorphism**: Translucent surfaces with backdrop blur (80% opacity + 12px blur)
- **Mobile-First**: 44px touch targets, safe areas, 100dvh viewport

## When to Use This Skill

**Building Components**:
- New UI components (Button, Card, Input, Modal)
- Implementing from design specs
- Creating variant systems with CVA

**Building Pages**:
- Dashboard layouts
- Responsive patterns (sidebar + bottom nav)
- Kanban columns, carousels, stat cards

**Styling Decisions**:
- Which color/spacing/radius token to use
- Component variant selection
- Mobile vs desktop layout patterns

## Quick Token Reference

### Essential Colors

```yaml
Background:
  base: "#FAF8F5"           # Creamy base (never pure white)
  subtle: "#F5F2ED"         # Slightly darker
  elevated: "#FFFFFF"       # Pure white (cards only)

Text (Warm Ink):
  primary: "#2D2520"        # Headings (warm dark brown)
  secondary: "#5C534D"      # Body text
  tertiary: "#8A827C"       # Captions

Primary (Holiday Coral):
  main: "#E8846B"           # Primary buttons, CTAs
  hover: "#D66A51"          # Hover state

Status:
  idea: "#D4A853"           # Mustard (Ideas, Shortlisted)
  success: "#7BA676"        # Sage (Purchased, Gifted)
  warning: "#C97B63"        # Terracotta (Urgent)
  progress: "#8A78A3"       # Lavender (Buying, Ordered)

Borders:
  subtle: "#E8E3DC"         # Soft borders
  medium: "#D4CDC4"         # Default borders
  strong: "#B8AFA4"         # Emphasized
  focus: "#E8846B"          # Focus rings
```

### Spacing (8px Grid)

```yaml
1: 4px    # 0.25rem
2: 8px    # 0.5rem
3: 12px   # 0.75rem
4: 16px   # 1rem
5: 20px   # 1.25rem
6: 24px   # 1.5rem
8: 32px   # 2rem
10: 40px  # 2.5rem
12: 48px  # 3rem
```

### Border Radius

```yaml
small: 8px      # Pills, badges, small buttons
medium: 12px    # Inputs, standard buttons
large: 16px     # Default cards
xlarge: 20px    # Large cards
2xlarge: 24px   # Hero cards, stat cards
3xlarge: 32px   # Extra large containers
full: 9999px    # Avatars, circular elements
```

### Shadows

```yaml
subtle:     # Level 1 - Minimal cards
  "0 1px 2px rgba(45, 37, 32, 0.04), 0 0 0 1px rgba(45, 37, 32, 0.02)"

low:        # Level 2 - Most cards
  "0 2px 8px rgba(45, 37, 32, 0.06), 0 0 0 1px rgba(45, 37, 32, 0.03)"

medium:     # Level 3 - Hover, dropdowns
  "0 4px 16px rgba(45, 37, 32, 0.08), 0 1px 4px rgba(45, 37, 32, 0.04)"

high:       # Level 4 - Modals, sheets
  "0 8px 32px rgba(45, 37, 32, 0.12), 0 2px 8px rgba(45, 37, 32, 0.06)"
```

## Component Variant Guide

### Button

**Variants**: `primary` (coral CTA) | `secondary` (warm gray) | `ghost` (transparent) | `glass` (translucent) | `destructive` (terracotta)
**Sizes**: `sm` (32px) | `md` (44px, default) | `lg` (52px) | `xl` (60px)
**Touch Target**: All meet 44px minimum

```tsx
// CVA Example
<Button variant="primary" size="md">Add Gift</Button>
<Button variant="secondary" size="sm">Cancel</Button>
<Button variant="glass" size="lg">Dashboard</Button>
```

### Card

**Variants**: `default` (elevated white) | `glass` (translucent blur) | `elevated` (higher shadow) | `interactive` (hover lift) | `stat` (gradient status) | `flat` (minimal shadow)
**Padding**: 20-32px
**Radius**: 16-24px

```tsx
<Card variant="glass" className="p-6 rounded-xlarge">
  {/* Glassmorphism with backdrop blur */}
</Card>

<Card variant="stat" className="p-8 rounded-2xlarge">
  {/* Stat card with gradient background */}
</Card>
```

### StatusPill

**Statuses**: `idea` | `shortlisted` | `buying` | `ordered` | `purchased` | `delivered` | `gifted` | `urgent`
**Pattern**: Dot indicator (6px) + uppercase label (12px semibold)

```tsx
<StatusPill status="idea">Idea</StatusPill>
<StatusPill status="purchased">Purchased</StatusPill>
```

### Avatar

**Sizes**: `xs` (24px) | `sm` (32px) | `md` (40px) | `lg` (56px) | `xl` (80px)
**Features**: Status rings, gift count badges
**Border**: 2px white, shadow-low

```tsx
<Avatar size="lg" status="online" badge={3} />
```

## Layout Patterns

### Desktop Sidebar + Main

```tsx
<div className="flex h-screen">
  {/* Sidebar: 240px fixed */}
  <aside className="hidden md:block w-60 glass-panel">
    <nav>...</nav>
  </aside>

  {/* Main content: flex-1 */}
  <main className="flex-1 overflow-auto">
    {children}
  </main>
</div>
```

### Mobile Bottom Nav

```tsx
<nav className="
  fixed bottom-0 left-0 right-0 md:hidden
  h-14 pb-safe-area-inset-bottom
  glass-panel border-t border-subtle
  flex items-center justify-around
  z-50
">
  {/* 4-5 nav items max, 44px touch targets */}
</nav>
```

### Responsive Breakpoints

```yaml
xs: 375px   # iPhone SE
sm: 640px   # Small tablets
md: 768px   # Tablets (sidebar shows, bottom nav hides)
lg: 1024px  # Laptops
xl: 1280px  # Desktops
```

### Safe Areas (iOS)

```tsx
className="
  pt-safe-area-inset-top
  pb-safe-area-inset-bottom
  pl-safe-area-inset-left
  pr-safe-area-inset-right
"
```

### Viewport Height

```css
/* Always use 100dvh (dynamic) instead of 100vh */
height: 100dvh;  /* Fixes iOS address bar issue */
```

## Animation Guidelines

**Use**: `anime.js` for page animations (see `./references/animejs.md`)
**Pattern**: One well-orchestrated entrance > scattered micro-interactions
**Stagger**: Use `animation-delay` for sequential reveals

```tsx
// Staggered card entrance
{cards.map((card, i) => (
  <Card
    key={card.id}
    className="animate-slide-up-fade"
    style={{ animationDelay: `${i * 100}ms` }}
  >
    {card.content}
  </Card>
))}
```

**Durations**:
- Fast: 150ms (micro-interactions)
- Default: 200ms (most interactions)
- Slow: 300ms (page transitions)
- Spring: Use ease-spring for delight

## Detailed References

When you need more detail:

- **Token values**: `./references/soft-modernity-tokens.md` — Complete color palette, spacing scale, typography
- **Component variants**: `./references/component-variants.md` — CVA patterns, Tailwind classes, all variants
- **Layout patterns**: `./references/layout-mobile.md` — Responsive layouts, safe areas, touch targets
- **Animations**: `./references/animejs.md` — anime.js v4 reference (KEEP AS-IS)

## Project Design Docs

For comprehensive design specifications:

- `docs/designs/DESIGN-TOKENS.md` — Canonical token values
- `docs/designs/COMPONENTS.md` — Component specifications
- `docs/designs/LAYOUT-PATTERNS.md` — Page layouts
- `docs/designs/DESIGN-GUIDE.md` — Philosophy and principles

## Implementation Notes

**Tailwind Config**: All tokens pre-configured in `apps/web/tailwind.config.ts`
**CSS Variables**: Available in `apps/web/app/globals.css`
**CVA**: Use for component variants (class-variance-authority)
**Utilities**: Glassmorphism via `.glass-panel` utility class

**Mobile-First**: Default styles assume mobile, add `md:` prefix for tablet+, `lg:` for desktop

## Common Patterns

### Glassmorphism Panel

```tsx
<div className="glass-panel p-6 rounded-xlarge">
  {/* 80% opacity + 12px backdrop blur */}
</div>
```

### Stat Card Grid

```tsx
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
  <StatCard value={12} label="Ideas" color="idea" />
  <StatCard value={8} label="To Buy" color="warning" />
  <StatCard value={4} label="Purchased" color="success" />
</div>
```

### Avatar Carousel

```tsx
<div className="flex gap-6 overflow-x-auto pb-2">
  {members.map(member => (
    <Avatar
      key={member.id}
      size="lg"
      status={member.status}
      badge={member.giftCount}
    />
  ))}
</div>
```

### Interactive Card with Hover Lift

```tsx
<Card
  variant="interactive"
  className="
    transition-all duration-300
    hover:shadow-medium
    hover:scale-[1.01]
    active:scale-[0.99]
  "
>
  {content}
</Card>
```

## Best Practices

1. **Always use cream base** (#FAF8F5), never pure white backgrounds
2. **44px minimum** for all touch targets
3. **Generous radii** — cards 20-24px, containers 32-48px
4. **Diffused shadows** — use layered shadows (shadow + border)
5. **Warm text** — brown-based (#2D2520), not pure black
6. **Status colors** — muted harmonious palette (mustard, sage, terracotta, lavender)
7. **Safe areas** — always include on fixed/sticky elements
8. **Dynamic viewport** — use 100dvh, not 100vh
9. **Glassmorphism** — 80% opacity + 12px backdrop blur for floating panels
10. **Staggered animations** — one orchestrated entrance beats scattered micro-interactions

## Anti-Patterns to Avoid

❌ Pure white (#FFFFFF) background
❌ Pure black (#000000) text
❌ Sharp corners (< 8px radius)
❌ Harsh shadows (no blur, high opacity)
❌ Cold colors (blues, grays without warmth)
❌ Touch targets < 44px
❌ Fixed 100vh on mobile
❌ No safe-area handling on iOS
❌ Generic AI aesthetics (Inter font, purple gradients, cookie-cutter layouts)

---

**Last Updated**: 2025-11-28
**Design System Version**: 1.0
**Project**: Family Gifting Dashboard


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/animejs.md

```markdown
# Anime.js v4 Reference Guide for AI Assistants

## 🚨 CRITICAL: ALWAYS USE ANIME.JS V4 SYNTAX 🚨

**This project uses Anime.js v4.x.x - DO NOT use v3 syntax under any circumstances**

**If you're about to write `import anime from 'animejs'` - STOP!**
**That's v3. This project uses v4. Use the correct import below.**

## 🚀 Quick Start - Essential Setup

### 1. Correct v4 Import (REQUIRED)
```javascript
// ✅ CORRECT v4 imports
import { animate, createTimeline, stagger, utils, svg, eases, engine } from 'animejs';

// ❌ WRONG v3 import - NEVER USE THIS
// import anime from 'animejs';
```

### 2. Configure Time Units to Seconds (SET ONCE IN APP ENTRY POINT)
```javascript
// ⚠️ IMPORTANT: Set this ONLY ONCE in your app's main entry point
// For React: App.js/App.tsx or index.js/index.tsx
// For Vue: main.js/main.ts
// For vanilla JS: The main script file that loads first

import { engine } from 'animejs';

// Set ONLY in the app's entry point, NOT in components
engine.timeUnit = 's';

// Now ALL durations use seconds everywhere: 1 = 1 second, 0.5 = 500ms
// DO NOT set this in individual components - it's a global setting!
```

### 3. Single-Line Format for Simple Animations (REQUIRED)
```javascript
// ✅ GOOD - Clean, readable, one line for simple tweens
animate('.element', { x: 250, duration: 1, ease: 'outQuad' });

// ❌ BAD - Unnecessary multi-line for simple tweens
animate('.element', {
  x: 250,
  duration: 1,
  ease: 'outQuad'
});
```

## ✅ Quick Validation Checklist

Before generating anime.js code, verify:
- [ ] Using `import { animate, ... } from 'animejs'` NOT `import anime`
- [ ] Set `engine.timeUnit = 's'` ONLY ONCE in app entry point (NOT in components)
- [ ] Using seconds for all durations (1 = 1 second)
- [ ] Simple animations on ONE LINE
- [ ] Using `animate()` NOT `anime()`
- [ ] Using `createTimeline()` NOT `anime.timeline()`
- [ ] Using `ease:` NOT `easing:`
- [ ] Using `to:` for values, NOT `value:`
- [ ] Using `on` prefix for callbacks (onUpdate, onComplete)
- [ ] Using `loop` and `alternate` NOT `direction`
- [ ] Using correct v4 stagger syntax with `stagger()`
- [ ] Using shorthand properties (x, y, z) when possible

## 🎯 Core API - Most Common Patterns

### Basic Animation (single line for simple tweens)
```javascript
// Simple tween - ALWAYS one line
animate('.element', { x: 250, rotate: 180, duration: 0.8, ease: 'inOutQuad' });

// Fade in - one line
animate('.element', { opacity: [0, 1], y: [20, 0], duration: 0.6, ease: 'outQuad' });

// Scale bounce - one line
animate('.element', { scale: [0, 1], duration: 0.8, ease: 'outElastic(1, 0.5)' });

// Infinite loop - one line
animate('.element', { rotate: 360, duration: 2, loop: true, ease: 'linear' });
```

### Timeline Creation
```javascript
const tl = createTimeline({ defaults: { duration: 1, ease: 'outQuad' } });

tl.add('.element1', { x: 250 })
  .add('.element2', { y: 100 }, '+=0.2')  // 0.2s after previous
  .add('.element3', { rotate: 180 }, '<'); // at start of previous
```

### Stagger Animations (single line)
```javascript
animate('.elements', { x: 250, delay: stagger(0.1) });  // 0.1s between each
animate('.elements', { x: 250, delay: stagger(0.1, { from: 'center' }) });
```

## ❌ Common AI Mistakes to Avoid

### MISTAKE #1: Using v3 Import Pattern
```javascript
// ❌ WRONG - This is v3
import anime from 'animejs';
anime({ targets: '.element', translateX: 250 });

// ✅ CORRECT - Always use v4
import { animate } from 'animejs';
animate('.element', { x: 250 });
```

### MISTAKE #2: Using 'targets' Property
```javascript
// ❌ WRONG - 'targets' is v3
animate({ targets: '.element', translateX: 250 });

// ✅ CORRECT - First parameter is the target
animate('.element', { x: 250 });
```

### MISTAKE #3: Using 'easing' Instead of 'ease'
```javascript
// ❌ WRONG
animate('.element', { x: 250, easing: 'easeInOutQuad' });

// ✅ CORRECT
animate('.element', { x: 250, ease: 'inOutQuad' });
```

### MISTAKE #4: Using 'value' for Animation Values
```javascript
// ❌ WRONG - 'value' is v3
animate('.element', { x: { value: 250 } });

// ✅ CORRECT - Use 'to' for values
animate('.element', { x: { to: 250 } });
```

### MISTAKE #5: Wrong Timeline Syntax
```javascript
// ❌ WRONG - anime.timeline() is v3
const tl = anime.timeline();

// ✅ CORRECT - Use createTimeline
import { createTimeline } from 'animejs';
const tl = createTimeline();
```

## 📋 Property Syntax Reference (v3 → v4)

### Animation Values
```javascript
// ✅ v4: Use 'to' for target values
{ opacity: { to: 0.5 } }
{ x: { to: [0, 100] } }

// ❌ v3: DON'T use 'value'
// { opacity: { value: 0.5 } }
```

### Easing Functions
```javascript
// ✅ v4: Use 'ease' (no 'ease' prefix)
{ ease: 'inOutQuad' }
{ ease: 'outElastic(1, 0.5)' }
{ ease: 'cubicBezier(0.4, 0, 0.2, 1)' }

// ❌ v3: DON'T use 'easing' or 'ease' prefix
// { easing: 'easeInOutQuad' }
```

### Direction & Looping
```javascript
// ✅ v4
{
  loop: true,        // infinite loop
  loop: 3,          // loop 3 times
  alternate: true,   // alternate direction
  reversed: true     // play in reverse
}

// ❌ v3: DON'T use 'direction'
// { direction: 'alternate' }
```

### Transform Properties (Shorthand Preferred)
```javascript
// ✅ Both syntaxes work in v4:
animate('.element', { x: 100, y: 50, z: 25 });           // shorthand (preferred)
animate('.element', { translateX: 100, translateY: 50, translateZ: 25 }); // explicit
```

### Callbacks (ALL prefixed with 'on')
```javascript
// ✅ v4: Simple callback - keep on one line
animate('.element', { x: 250, duration: 1, onComplete: () => console.log('Done!') });

// ✅ v4: Multiple callbacks - use multi-line
animate('.element', {
  x: 250,
  duration: 1,
  onBegin: (anim) => console.log('Started'),
  onUpdate: (anim) => console.log('Progress:', anim.progress),
  onComplete: (anim) => console.log('Finished')
});

// ❌ v3: DON'T use unprefixed callbacks
// { update: () => {}, complete: () => {} }
```

## 📝 Code Formatting Guidelines

### ALWAYS Use Single-Line Format for Simple Animations
**This is mandatory for readability** - Use for animations with ≤4 properties:
```javascript
// ✅ GOOD - Clean, readable, one line
animate('.element', { x: 250, duration: 1, ease: 'outQuad' });
animate('.box', { opacity: 0.5, scale: 0.8, duration: 0.3 });

// ❌ BAD - Unnecessary multi-line for simple tweens
animate('.element', {
  x: 250,
  duration: 1,
  ease: 'outQuad'
});
```

### Multi-Line Format (Only for Complex Animations)
Use for animations with >4 properties or callbacks:
```javascript
// Complex animation with callbacks - multi-line is appropriate
animate('.element', {
  x: { to: [0, 100, 50], duration: 2 },
  y: { to: [0, -50, 0], duration: 2 },
  scale: [0, 1.2, 1],
  ease: 'outElastic(1, 0.5)',
  onComplete: () => console.log('Done!')
});
```

## 🎨 Common Animation Patterns

### Hover Animation (single line per animation)
```javascript
element.addEventListener('mouseenter', () => animate(element, { scale: 1.1, duration: 0.3, ease: 'outQuad' }));
element.addEventListener('mouseleave', () => animate(element, { scale: 1, duration: 0.3, ease: 'outQuad' }));
```

### Sequential Timeline
```javascript
const tl = createTimeline({ defaults: { duration: 0.5 } });
tl.add('.step1', { x: 100 })
  .add('.step2', { y: 100 })
  .add('.step3', { scale: 2 });
```

### Scroll-triggered Animation
```javascript
import { createScrollObserver } from 'animejs';

createScrollObserver({
  target: '.scroll-element',
  root: document.querySelector('.scroll-container'),
  play: () => animate('.element', { x: 250, duration: 1 }),
  visibility: 0.5
});
```

## 🔧 Advanced Features

### SVG Animations
```javascript
import { animate, svg } from 'animejs';

// Morph path (single line)
animate('#path1', { d: svg.morphTo('#path2'), duration: 1 });

// Draw SVG line
const drawable = svg.createDrawable('.svg-path');
animate(drawable, { draw: '0% 100%', duration: 2 });

// Motion path (single line for simple usage)
const motionPath = svg.createMotionPath('#motion-path');
animate('.element', { x: motionPath.translateX, y: motionPath.translateY, rotate: motionPath.rotate });
```

### Utility Functions
```javascript
import { utils } from 'animejs';

// DOM selection
const elements = utils.$('.elements');

// Get current value
const currentX = utils.get('.element', 'translateX');

// Set values immediately
utils.set('.element', { x: 100, opacity: 0.5 });

// Remove animations
utils.remove('.element');

// Math utilities
utils.random(0, 100);
utils.shuffle([1, 2, 3, 4]);
utils.lerp(0, 100, 0.5); // 50
utils.clamp(150, 0, 100); // 100
```

### TypeScript Support
```typescript
import { animate, createTimeline, JSAnimation, Timeline, AnimationParams, TimelineParams } from 'animejs';

// Single line for simple animations
const animation: JSAnimation = animate('.element', { x: 250, duration: 1 } as AnimationParams);

const timeline: Timeline = createTimeline({ defaults: { duration: 0.8 } } as TimelineParams);
```

## ⚡ Performance Tips

1. **Use transforms over position properties**
   ```javascript
   // ✅ Good - uses transform
   animate('.element', { x: 100 });
   
   // ❌ Avoid - triggers layout
   animate('.element', { left: 100 });
   ```

2. **Batch animations in timelines**
   ```javascript
   // ✅ Good - single timeline
   const tl = createTimeline();
   elements.forEach(el => tl.add(el, { x: 100 }));
   
   // ❌ Avoid - multiple animations
   elements.forEach(el => animate(el, { x: 100 }));
   ```

3. **Use will-change CSS property for complex animations**
   ```css
   .animated-element {
     will-change: transform, opacity;
   }
   ```

## 🚫 How to Identify V3 Code (DON'T USE)

If you see ANY of these patterns, it's v3 and MUST be updated:

```javascript
// All of these are V3 - NEVER USE:
anime({ ... })
anime.timeline()
anime.stagger()
anime.random()
anime.remove()
anime.get()
anime.set()
anime.running
{ targets: '...' }
{ easing: '...' }
{ value: ... }
{ direction: 'alternate' }
```

## 💡 AI Code Generation Rules

When asked to create animations with anime.js:

1. **ONLY** set `engine.timeUnit = 's'` ONCE in the app's main entry point (App.js, main.js, index.js) - NEVER in components
2. **ALWAYS** use seconds for all durations (1 = 1 second)
3. **ALWAYS** format simple animations on ONE LINE
4. **ALWAYS** start with v4 imports
5. **NEVER** use `anime()` function
6. **ALWAYS** use `animate()` for animations
7. **NEVER** include `targets` property
8. **ALWAYS** use `ease` not `easing`
9. **NEVER** use `value`, use `to` instead
10. **ALWAYS** prefix callbacks with `on`
11. **NEVER** use `direction`, use `alternate` and `reversed`
12. **ALWAYS** use `createTimeline()` for timelines
13. **PREFER** shorthand (`x`) over explicit (`translateX`)
14. **FORMAT** short animations on single line (≤4 properties)
15. **NEVER** generate v3 syntax under any circumstances

## NPM Installation
```bash
npm install animejs
```

## Version Check
```javascript
// Current version: 4.x.x
// If you see any code using anime({ targets: ... }), it's v3 and needs updating!
```

```

### references/soft-modernity-tokens.md

```markdown
# Soft Modernity Design Tokens

Token-optimized reference for the Family Gifting Dashboard design system. All values are canonical and pre-configured in `apps/web/tailwind.config.ts`.

---

## Color Palette

### Background Colors

```yaml
bg-base:      "#FAF8F5"    # Creamy off-white base (NEVER pure white)
bg-subtle:    "#F5F2ED"    # Slightly darker for contrast
bg-elevated:  "#FFFFFF"    # Pure white for elevated cards only

surface-primary:     "#FFFFFF"               # Floating cards, modals
surface-secondary:   "rgba(255,255,255,0.6)" # Glassy surfaces
surface-tertiary:    "rgba(242,238,230,0.5)" # Subtle backgrounds
surface-translucent: "rgba(250,248,245,0.8)" # Glassmorphism base
```

### Text Colors (Warm Ink)

```yaml
text-primary:   "#2D2520"  # Warm dark brown - headings
text-secondary: "#5C534D"  # Medium warm grey - body
text-tertiary:  "#8A827C"  # Light warm grey - captions
text-disabled:  "#C4BDB7"  # Disabled state
text-inverse:   "#FFFFFF"  # Text on dark backgrounds
```

### Primary Accent (Holiday Coral)

```yaml
primary-50:  "#FEF3F1"
primary-100: "#FDE5E0"
primary-200: "#FBC9BC"
primary-300: "#F5A894"
primary-400: "#EE8F76"
primary-500: "#E8846B"  # Main coral - buttons, CTAs
primary-600: "#D66A51"  # Hover state
primary-700: "#B95440"
primary-800: "#9A4234"
primary-900: "#7D352B"
```

### Status: Idea/Shortlisted (Muted Mustard)

```yaml
status-idea-50:  "#FDF9F0"
status-idea-100: "#FAF1DC"
status-idea-200: "#F4E0B3"
status-idea-300: "#E8CC85"
status-idea-400: "#DCB85E"
status-idea-500: "#D4A853"  # Mustard yellow
status-idea-600: "#B88F45"
status-idea-700: "#967538"
status-idea-800: "#735A2B"
status-idea-900: "#523F1F"
```

### Status: Purchased/Gifted (Soft Sage)

```yaml
status-success-50:  "#F3F7F2"
status-success-100: "#E4EDE2"
status-success-200: "#C5D8C1"
status-success-300: "#A0BD9B"
status-success-400: "#8AAA84"
status-success-500: "#7BA676"  # Sage green
status-success-600: "#668B61"
status-success-700: "#51704E"
status-success-800: "#3D543B"
status-success-900: "#2A3928"
```

### Status: Urgent/Attention (Muted Terracotta)

```yaml
status-warning-50:  "#FEF5F3"
status-warning-100: "#FCE9E5"
status-warning-200: "#F6CEC5"
status-warning-300: "#EBAB9D"
status-warning-400: "#DD9179"
status-warning-500: "#C97B63"  # Terracotta
status-warning-600: "#AC6350"
status-warning-700: "#8D4E40"
status-warning-800: "#6D3C31"
status-warning-900: "#4F2B23"
```

### Status: Buying/Ordered (Muted Lavender)

```yaml
status-progress-50:  "#F7F5F9"
status-progress-100: "#EDE8F2"
status-progress-200: "#D6CBDF"
status-progress-300: "#B9A7C7"
status-progress-400: "#A08DB4"
status-progress-500: "#8A78A3"  # Lavender
status-progress-600: "#70628A"
status-progress-700: "#594E6E"
status-progress-800: "#433B53"
status-progress-900: "#2F2A3A"
```

### Borders & Dividers

```yaml
border-subtle: "#E8E3DC"  # Soft borders
border-medium: "#D4CDC4"  # Default borders
border-strong: "#B8AFA4"  # Emphasized borders
border-focus:  "#E8846B"  # Focus rings (coral)

glass-border:        "rgba(255,255,255,0.4)"  # Glassmorphism borders
glass-border-strong: "rgba(255,255,255,0.6)"  # Emphasized glass
```

### Overlays

```yaml
overlay-light:  "rgba(45,37,32,0.08)"  # Subtle overlays
overlay-medium: "rgba(45,37,32,0.16)"  # Modal backgrounds
overlay-strong: "rgba(45,37,32,0.48)"  # Strong emphasis
```

### Neutral Warm Scale (Tailwind warm-*)

```yaml
warm-50:  "#FAF8F5"
warm-100: "#F5F2ED"
warm-200: "#EBE7E0"
warm-300: "#D4CDC4"
warm-400: "#C4BDB7"
warm-500: "#A69C94"
warm-600: "#8A827C"
warm-700: "#5C534D"
warm-800: "#3D3632"
warm-900: "#2D2520"
```

---

## Typography

### Font Stack

```yaml
primary: "'SF Pro Rounded', -apple-system, BlinkMacSystemFont, system-ui, sans-serif"
fallback: "'Nunito', 'Inter', system-ui, -apple-system, sans-serif"
```

### Type Scale

```yaml
display-large:   # 48px / 3rem
  size: 3rem
  line: 3.5rem      # 56px / 1.167
  weight: 800       # Heavy
  tracking: -0.02em

display-medium:  # 36px / 2.25rem
  size: 2.25rem
  line: 2.75rem     # 44px / 1.222
  weight: 800       # Heavy
  tracking: -0.015em

display-small:   # 28px / 1.75rem
  size: 1.75rem
  line: 2.25rem     # 36px / 1.286
  weight: 700       # Bold
  tracking: -0.01em

heading-1:       # 24px / 1.5rem
  size: 1.5rem
  line: 2rem        # 32px / 1.333
  weight: 700       # Bold
  tracking: -0.01em

heading-2:       # 20px / 1.25rem
  size: 1.25rem
  line: 1.75rem     # 28px / 1.4
  weight: 600       # Semibold
  tracking: -0.01em

heading-3:       # 18px / 1.125rem
  size: 1.125rem
  line: 1.625rem    # 26px / 1.444
  weight: 600       # Semibold
  tracking: -0.005em

body-large:      # 16px / 1rem
  size: 1rem
  line: 1.5rem      # 24px / 1.5
  weight: 400       # Regular

body-medium:     # 14px / 0.875rem
  size: 0.875rem
  line: 1.25rem     # 20px / 1.429
  weight: 400       # Regular

body-small:      # 12px / 0.75rem
  size: 0.75rem
  line: 1rem        # 16px / 1.333
  weight: 500       # Medium

label-large:     # 14px / 0.875rem
  size: 0.875rem
  line: 1.25rem     # 20px / 1.429
  weight: 600       # Semibold

label-small:     # 12px / 0.75rem
  size: 0.75rem
  line: 1rem        # 16px / 1.333
  weight: 600       # Semibold
  tracking: 0.01em
```

---

## Spacing Scale (8px Base Grid)

```yaml
spacing-1:  0.25rem   # 4px
spacing-2:  0.5rem    # 8px
spacing-3:  0.75rem   # 12px
spacing-4:  1rem      # 16px
spacing-5:  1.25rem   # 20px
spacing-6:  1.5rem    # 24px
spacing-8:  2rem      # 32px
spacing-10: 2.5rem    # 40px
spacing-12: 3rem      # 48px
spacing-16: 4rem      # 64px
spacing-20: 5rem      # 80px
spacing-24: 6rem      # 96px
```

**Tailwind Classes**: `p-1` through `p-24`, `m-1` through `m-24`, `gap-1` through `gap-24`

---

## Border Radius

```yaml
small:    0.5rem    # 8px  - Pills, badges, small buttons
medium:   0.75rem   # 12px - Inputs, standard buttons
large:    1rem      # 16px - Default cards
xlarge:   1.25rem   # 20px - Large cards
2xlarge:  1.5rem    # 24px - Hero cards, stat cards
3xlarge:  2rem      # 32px - Extra large containers
full:     9999px    # Avatars, circular elements
```

**Tailwind Classes**: `rounded-small`, `rounded-medium`, `rounded-large`, `rounded-xlarge`, `rounded-2xlarge`, `rounded-3xlarge`, `rounded-full`

---

## Shadows (Diffused Elevation)

```yaml
subtle:  # Level 1 - Minimal cards
  "0 1px 2px rgba(45, 37, 32, 0.04), 0 0 0 1px rgba(45, 37, 32, 0.02)"

low:  # Level 2 - Most cards
  "0 2px 8px rgba(45, 37, 32, 0.06), 0 0 0 1px rgba(45, 37, 32, 0.03)"

medium:  # Level 3 - Hover, dropdowns
  "0 4px 16px rgba(45, 37, 32, 0.08), 0 1px 4px rgba(45, 37, 32, 0.04)"

high:  # Level 4 - Modals, sheets
  "0 8px 32px rgba(45, 37, 32, 0.12), 0 2px 8px rgba(45, 37, 32, 0.06)"

extra-high:  # Level 5 - Fullscreen overlays
  "0 16px 48px rgba(45, 37, 32, 0.16), 0 4px 16px rgba(45, 37, 32, 0.08)"

translucent:  # Glassmorphism
  "0 4px 24px rgba(45, 37, 32, 0.10), 0 0 0 1px rgba(45, 37, 32, 0.04), inset 0 0 0 1px rgba(255, 255, 255, 0.2)"

glass:  # Glass panels
  "0 8px 32px 0 rgba(31, 38, 135, 0.07)"

glass-inset:  # Glass border highlight
  "inset 0 0 0 1px rgba(255, 255, 255, 0.1)"

glow:  # Accent glow (coral)
  "0 0 20px rgba(255, 102, 77, 0.3)"
```

**Tailwind Classes**: `shadow-subtle`, `shadow-low`, `shadow-medium`, `shadow-high`, `shadow-glass`

### Elevation Hierarchy

```yaml
Level 0 (Base):     no-shadow      # Background
Level 1 (Flat):     shadow-subtle  # Minimal cards
Level 2 (Default):  shadow-low     # Most cards
Level 3 (Raised):   shadow-medium  # Hover, dropdowns
Level 4 (Floating): shadow-high    # Modals, sheets
Level 5 (Overlay):  shadow-extra-high  # Fullscreen overlays
```

---

## Animations

### Durations

```yaml
fast:    150ms   # Fast interactions (hover, press)
default: 200ms   # Default interactions (most transitions)
slow:    300ms   # Slower, more dramatic (page transitions)
page:    400ms   # Page-level transitions
```

### Easing Functions

```yaml
ease-out:    cubic-bezier(0.16, 1, 0.3, 1)        # Entering elements (fast→slow)
ease-in:     cubic-bezier(0.7, 0, 0.84, 0)        # Exiting elements (slow→fast)
ease-in-out: cubic-bezier(0.65, 0, 0.35, 1)       # Smooth both ways
ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1)    # Bouncy (for delight)
```

### Keyframe Animations

```yaml
fadeIn:
  from: { opacity: 0 }
  to:   { opacity: 1 }

slideUpFade:
  from: { opacity: 0, transform: translateY(10px) }
  to:   { opacity: 1, transform: translateY(0) }

scaleIn:
  from: { opacity: 0, transform: scale(0.95) }
  to:   { opacity: 1, transform: scale(1) }

springIn:
  0%:   { transform: scale(0.9) }
  50%:  { transform: scale(1.05) }
  100%: { transform: scale(1) }

shimmer:
  100%: { transform: translateX(100%) }
```

**Tailwind Classes**: `animate-fade-in`, `animate-slide-up-fade`, `animate-scale-in`, `animate-spring-in`, `animate-shimmer`

---

## Breakpoints

```yaml
xs:  375px    # iPhone SE
sm:  640px    # Small tablets
md:  768px    # Tablets (sidebar shows, bottom nav hides)
lg:  1024px   # Laptops
xl:  1280px   # Desktops
2xl: 1536px   # Large desktops
```

**Tailwind Prefixes**: `xs:`, `sm:`, `md:`, `lg:`, `xl:`, `2xl:`

---

## Layout Tokens

### Max Content Widths

```yaml
max-content-width:  1280px  # 80rem - Max content width
max-reading-width:  640px   # 40rem - Comfortable reading
max-form-width:     512px   # 32rem - Form max width
```

### Grid Systems

```yaml
Desktop:
  columns: 12
  gutter: 24px

Tablet:
  columns: 8
  gutter: 16px

Mobile:
  columns: 4
  gutter: 16px
```

### Component Sizing

```yaml
Button Heights:
  small:  32px
  medium: 44px   # Default (touch target)
  large:  52px
  xlarge: 60px

Input Heights:
  standard: 48px  # Including border

Avatar Sizes:
  xs: 24px
  sm: 32px
  md: 40px   # Default
  lg: 56px
  xl: 80px

Card Padding:
  small:  20px   # spacing-5
  medium: 24px   # spacing-6
  large:  32px   # spacing-8

Sidebar Width:
  desktop: 240px  # Fixed on desktop
```

---

## Touch Targets (Mobile)

```yaml
minimum: 44px        # Minimum 44×44px touch area
icon-size: 24px      # Standard icon size
icon-touch-area: 44px  # Touch area for icon-only buttons
```

---

## Safe Areas (iOS/macOS)

```yaml
safe-area-inset-top:    env(safe-area-inset-top)
safe-area-inset-right:  env(safe-area-inset-right)
safe-area-inset-bottom: env(safe-area-inset-bottom)
safe-area-inset-left:   env(safe-area-inset-left)
```

**Tailwind Classes**: `pt-safe-area-inset-top`, `pb-safe-area-inset-bottom`, `pl-safe-area-inset-left`, `pr-safe-area-inset-right`

---

## CSS Custom Properties

All tokens available as CSS variables in `:root`:

```css
:root {
  /* Colors */
  --color-bg-base: #FAF8F5;
  --color-text-primary: #2D2520;
  --color-primary-500: #E8846B;

  /* Typography */
  --font-family: 'SF Pro Rounded', -apple-system, sans-serif;
  --font-size-body-large: 1rem;

  /* Spacing */
  --spacing-4: 1rem;
  --spacing-6: 1.5rem;

  /* Radius */
  --radius-large: 1rem;
  --radius-xlarge: 1.25rem;

  /* Shadows */
  --shadow-low: 0 2px 8px rgba(45, 37, 32, 0.06), 0 0 0 1px rgba(45, 37, 32, 0.03);

  /* Animations */
  --duration-default: 200ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}
```

---

**Reference**: See `apps/web/tailwind.config.ts` for complete implementation
**Version**: 1.0
**Last Updated**: 2025-11-28

```

### references/component-variants.md

```markdown
# Component Variants — CVA Patterns

Token-optimized component variant patterns using `class-variance-authority` (CVA). All examples use Tailwind classes configured in `apps/web/tailwind.config.ts`.

---

## Button Component

### Variants

```typescript
// CVA Definition
const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-medium font-semibold transition-all duration-200",
  {
    variants: {
      variant: {
        primary: "bg-primary-500 text-white hover:bg-primary-600 active:bg-primary-700 shadow-low hover:shadow-medium",
        secondary: "bg-warm-100 text-warm-900 border border-warm-300 hover:bg-warm-200 hover:border-warm-400",
        ghost: "bg-transparent text-warm-800 hover:bg-warm-100 active:bg-warm-200",
        glass: "bg-surface-secondary backdrop-blur-xs border border-glass-border text-warm-900 hover:bg-surface-tertiary shadow-glass",
        destructive: "bg-status-warning-500 text-white hover:bg-status-warning-600 active:bg-status-warning-700 shadow-low"
      },
      size: {
        sm: "h-8 px-3 text-body-small min-w-[44px]",
        md: "h-11 px-6 text-body-large min-w-[44px]",
        lg: "h-13 px-8 text-heading-3 min-w-[44px]",
        xl: "h-15 px-10 text-heading-2 min-w-[44px]"
      }
    },
    defaultVariants: {
      variant: "primary",
      size: "md"
    }
  }
);
```

### Tailwind Examples

```tsx
// Primary (Coral CTA)
<button className="
  inline-flex items-center justify-center
  h-11 px-6 min-w-[44px]
  bg-primary-500 text-white
  hover:bg-primary-600 active:bg-primary-700
  rounded-medium font-semibold
  shadow-low hover:shadow-medium
  transition-all duration-200
">
  Add Gift
</button>

// Secondary (Warm Gray)
<button className="
  inline-flex items-center justify-center
  h-11 px-6 min-w-[44px]
  bg-warm-100 text-warm-900
  border border-warm-300
  hover:bg-warm-200 hover:border-warm-400
  rounded-medium font-semibold
  transition-all duration-200
">
  Cancel
</button>

// Ghost (Transparent)
<button className="
  inline-flex items-center justify-center
  h-11 px-6 min-w-[44px]
  bg-transparent text-warm-800
  hover:bg-warm-100 active:bg-warm-200
  rounded-medium font-semibold
  transition-all duration-200
">
  Learn More
</button>

// Glass (Glassmorphism)
<button className="
  inline-flex items-center justify-center
  h-11 px-6 min-w-[44px]
  bg-surface-secondary backdrop-blur-xs
  border border-glass-border text-warm-900
  hover:bg-surface-tertiary
  rounded-medium font-semibold
  shadow-glass
  transition-all duration-200
">
  Dashboard
</button>

// Destructive (Terracotta)
<button className="
  inline-flex items-center justify-center
  h-11 px-6 min-w-[44px]
  bg-status-warning-500 text-white
  hover:bg-status-warning-600 active:bg-status-warning-700
  rounded-medium font-semibold
  shadow-low
  transition-all duration-200
">
  Delete
</button>
```

### States

```tsx
// Focus (All Variants)
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2

// Disabled (All Variants)
disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none

// Loading (All Variants)
// Add spinner overlay, maintain dimensions
```

---

## Card Component

### Variants

```typescript
// CVA Definition
const cardVariants = cva(
  "rounded-large transition-all duration-300",
  {
    variants: {
      variant: {
        default: "bg-surface-primary border border-border-subtle shadow-low hover:shadow-medium",
        glass: "bg-surface-secondary backdrop-blur-xs border border-glass-border shadow-glass",
        elevated: "bg-surface-primary border border-border-subtle shadow-medium hover:shadow-high",
        interactive: "bg-surface-primary border border-border-subtle shadow-low hover:shadow-medium hover:border-border-medium hover:scale-[1.01] active:scale-[0.99] cursor-pointer",
        stat: "border-2 shadow-medium",  // Gradient bg added via inline style
        flat: "bg-surface-secondary border border-border-subtle shadow-subtle"
      },
      padding: {
        sm: "p-5",     // 20px
        md: "p-6",     // 24px
        lg: "p-8"      // 32px
      }
    },
    defaultVariants: {
      variant: "default",
      padding: "md"
    }
  }
);
```

### Tailwind Examples

```tsx
// Default (Elevated White)
<div className="
  bg-surface-primary
  border border-border-subtle
  rounded-large p-6
  shadow-low hover:shadow-medium
  transition-all duration-300
">
  {content}
</div>

// Glass (Glassmorphism)
<div className="
  bg-surface-secondary backdrop-blur-xs
  border border-glass-border
  rounded-xlarge p-6
  shadow-glass
  transition-all duration-300
">
  {content}
</div>

// Interactive (Hover Lift)
<div className="
  bg-surface-primary
  border border-border-subtle
  rounded-large p-6
  shadow-low
  hover:shadow-medium hover:border-border-medium
  hover:scale-[1.01] active:scale-[0.99]
  cursor-pointer
  transition-all duration-300
">
  {content}
</div>

// Stat (Gradient Background)
<div className="
  bg-gradient-to-br from-status-idea-50 to-status-idea-100
  border-2 border-status-idea-200
  rounded-2xlarge p-8
  shadow-medium
">
  <div className="text-display-medium font-heavy text-status-idea-700">
    12
  </div>
  <div className="text-body-small font-semibold uppercase text-status-idea-600">
    Ideas
  </div>
</div>

// Flat (Minimal)
<div className="
  bg-surface-secondary
  border border-border-subtle
  rounded-large p-5
  shadow-subtle
">
  {content}
</div>
```

---

## StatusPill Component

### Variants

```typescript
// CVA Definition
const statusPillVariants = cva(
  "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-small text-label-small uppercase",
  {
    variants: {
      status: {
        idea:        "bg-status-idea-100 text-status-idea-800 border border-status-idea-300",
        shortlisted: "bg-status-idea-100 text-status-idea-800 border border-status-idea-300",
        buying:      "bg-status-progress-100 text-status-progress-800 border border-status-progress-300",
        ordered:     "bg-status-progress-100 text-status-progress-800 border border-status-progress-300",
        purchased:   "bg-status-success-100 text-status-success-800 border border-status-success-300",
        delivered:   "bg-status-success-100 text-status-success-800 border border-status-success-300",
        gifted:      "bg-status-success-100 text-status-success-800 border border-status-success-300",
        urgent:      "bg-status-warning-100 text-status-warning-800 border border-status-warning-300"
      }
    }
  }
);
```

### Tailwind Examples

```tsx
// Idea (Mustard)
<span className="
  inline-flex items-center gap-1.5
  px-3 py-1.5
  bg-status-idea-100 text-status-idea-800
  border border-status-idea-300
  rounded-small
  text-label-small uppercase font-semibold
">
  <span className="w-1.5 h-1.5 rounded-full bg-status-idea-600" />
  Idea
</span>

// Purchased (Sage)
<span className="
  inline-flex items-center gap-1.5
  px-3 py-1.5
  bg-status-success-100 text-status-success-800
  border border-status-success-300
  rounded-small
  text-label-small uppercase font-semibold
">
  <span className="w-1.5 h-1.5 rounded-full bg-status-success-600" />
  Purchased
</span>

// Urgent (Terracotta)
<span className="
  inline-flex items-center gap-1.5
  px-3 py-1.5
  bg-status-warning-100 text-status-warning-800
  border border-status-warning-300
  rounded-small
  text-label-small uppercase font-semibold
">
  <span className="w-1.5 h-1.5 rounded-full bg-status-warning-600" />
  Urgent
</span>

// Buying (Lavender)
<span className="
  inline-flex items-center gap-1.5
  px-3 py-1.5
  bg-status-progress-100 text-status-progress-800
  border border-status-progress-300
  rounded-small
  text-label-small uppercase font-semibold
">
  <span className="w-1.5 h-1.5 rounded-full bg-status-progress-600" />
  Buying
</span>
```

### Status Dot Colors

```yaml
idea:        bg-status-idea-600        # Mustard
shortlisted: bg-status-idea-600        # Mustard
buying:      bg-status-progress-600    # Lavender
ordered:     bg-status-progress-600    # Lavender
purchased:   bg-status-success-600     # Sage
delivered:   bg-status-success-600     # Sage
gifted:      bg-status-success-600     # Sage
urgent:      bg-status-warning-600     # Terracotta
```

---

## Avatar Component

### Variants

```typescript
// CVA Definition
const avatarVariants = cva(
  "rounded-full border-2 border-white shadow-low",
  {
    variants: {
      size: {
        xs: "w-6 h-6",      // 24px
        sm: "w-8 h-8",      // 32px
        md: "w-10 h-10",    // 40px
        lg: "w-14 h-14",    // 56px
        xl: "w-20 h-20"     // 80px
      }
    },
    defaultVariants: {
      size: "md"
    }
  }
);
```

### Tailwind Examples

```tsx
// Medium Avatar (40px, Default)
<div className="relative">
  <img
    src={avatarUrl}
    className="w-10 h-10 rounded-full border-2 border-white shadow-low object-cover"
    alt="User"
  />
</div>

// Large Avatar with Status Ring (56px)
<div className="relative">
  <img
    src={avatarUrl}
    className="w-14 h-14 rounded-full border-2 border-white shadow-low object-cover"
    alt="User"
  />
  {/* Status ring - online */}
  <div className="
    absolute -bottom-0.5 -right-0.5
    w-14 h-14 rounded-full
    ring-3 ring-status-success-500
    border-2 border-white
    pointer-events-none
  " />
</div>

// Avatar with Badge (Gift Count)
<div className="relative">
  <img
    src={avatarUrl}
    className="w-14 h-14 rounded-full border-2 border-white shadow-low object-cover"
    alt="User"
  />
  {/* Gift count badge */}
  <div className="
    absolute -bottom-1 -right-1
    w-6 h-6 rounded-full
    bg-primary-500 text-white
    border-2 border-white
    flex items-center justify-center
    text-xs font-bold
  ">
    3
  </div>
</div>

// Avatar Carousel (Overlap)
<div className="flex -space-x-3">
  <img className="w-10 h-10 rounded-full border-2 border-white shadow-low z-30" src={url1} />
  <img className="w-10 h-10 rounded-full border-2 border-white shadow-low z-20" src={url2} />
  <img className="w-10 h-10 rounded-full border-2 border-white shadow-low z-10" src={url3} />
  <div className="
    w-10 h-10 rounded-full border-2 border-white shadow-low z-0
    bg-warm-300 text-warm-800
    flex items-center justify-center
    text-xs font-bold
  ">
    +5
  </div>
</div>
```

### Status Ring Colors

```yaml
online:  ring-status-success-500   # Sage green
busy:    ring-status-warning-500   # Terracotta
away:    ring-status-idea-500      # Mustard
offline: ring-warm-400             # Warm gray
```

---

## Input Component

### Variants

```typescript
// CVA Definition
const inputVariants = cva(
  "w-full h-12 px-4 rounded-medium text-body-large transition-all duration-200",
  {
    variants: {
      variant: {
        default: "bg-white border-2 border-border-medium focus:border-primary-500 focus:ring-2 focus:ring-primary-200",
        error: "bg-white border-2 border-status-warning-500 focus:ring-2 focus:ring-status-warning-200",
        success: "bg-white border-2 border-status-success-500 focus:ring-2 focus:ring-status-success-200"
      },
      size: {
        md: "h-12 px-4 text-body-large",
        lg: "h-14 px-5 text-heading-3"
      }
    },
    defaultVariants: {
      variant: "default",
      size: "md"
    }
  }
);
```

### Tailwind Examples

```tsx
// Text Input (Default)
<input
  type="text"
  className="
    w-full h-12 px-4
    bg-white text-warm-900
    border-2 border-border-medium
    focus:border-primary-500 focus:ring-2 focus:ring-primary-200
    rounded-medium
    text-body-large placeholder:text-warm-400
    shadow-subtle
    transition-all duration-200
    outline-none
  "
  placeholder="Enter gift name..."
/>

// Textarea
<textarea
  className="
    w-full min-h-[120px] px-4 py-3
    bg-white text-warm-900
    border-2 border-border-medium
    focus:border-primary-500 focus:ring-2 focus:ring-primary-200
    rounded-medium
    text-body-large placeholder:text-warm-400
    shadow-subtle
    resize-y
    transition-all duration-200
    outline-none
  "
  placeholder="Add notes..."
/>

// Select
<select className="
  w-full h-12 px-4
  bg-white text-warm-900
  border-2 border-border-medium
  focus:border-primary-500 focus:ring-2 focus:ring-primary-200
  rounded-medium
  text-body-large
  shadow-subtle
  appearance-none
  bg-[url('data:image/svg+xml;base64,...')] bg-no-repeat bg-right-4 bg-[length:16px]
  transition-all duration-200
  outline-none
">
  <option>Select status...</option>
</select>

// Error State
<input
  className="
    w-full h-12 px-4
    bg-white text-warm-900
    border-2 border-status-warning-500
    focus:ring-2 focus:ring-status-warning-200
    rounded-medium
    text-body-large
    transition-all duration-200
    outline-none
  "
/>

// Disabled State
<input
  disabled
  className="
    w-full h-12 px-4
    bg-warm-100 text-warm-500
    border-2 border-border-subtle
    rounded-medium
    text-body-large
    opacity-40 cursor-not-allowed
  "
/>
```

---

## Modal Component

### Structure

```tsx
// Overlay
<div className="
  fixed inset-0 z-40
  bg-overlay-strong backdrop-blur-sm
  animate-fade-in
" />

// Container
<div className="
  fixed inset-0 z-50
  flex items-center justify-center
  p-6
">
  {/* Modal Box */}
  <div className="
    w-full max-w-lg
    bg-surface-primary
    border border-border-subtle
    rounded-2xlarge
    shadow-extra-high
    animate-scale-in
  ">
    {/* Header */}
    <div className="
      px-8 py-6
      border-b border-border-subtle
    ">
      <h2 className="text-heading-1 font-bold text-warm-900">
        Modal Title
      </h2>
      <p className="mt-1 text-body-medium text-warm-600">
        Optional subtitle
      </p>
    </div>

    {/* Content */}
    <div className="px-8 py-6">
      {content}
    </div>

    {/* Footer */}
    <div className="
      px-8 py-6
      border-t border-border-subtle
      flex items-center justify-end gap-3
    ">
      <Button variant="secondary" size="md">Cancel</Button>
      <Button variant="primary" size="md">Confirm</Button>
    </div>
  </div>
</div>
```

---

## Loading States

### Spinner

```tsx
<div className="
  w-8 h-8
  border-4 border-warm-200 border-t-primary-500
  rounded-full
  animate-spin
" />
```

### Skeleton Loader

```tsx
// Text Line
<div className="h-4 w-3/4 bg-warm-200 rounded-medium animate-pulse" />

// Image Block
<div className="h-48 w-full bg-warm-200 rounded-large animate-pulse" />

// Card Skeleton
<div className="bg-surface-primary border border-border-subtle rounded-large p-6 space-y-4">
  <div className="h-6 w-3/4 bg-warm-200 rounded-medium animate-pulse" />
  <div className="h-4 w-full bg-warm-200 rounded-medium animate-pulse" />
  <div className="h-4 w-5/6 bg-warm-200 rounded-medium animate-pulse" />
</div>
```

---

## Utility Classes

### Glassmorphism Panel

```tsx
// Pre-configured utility
<div className="glass-panel p-6 rounded-xlarge">
  {content}
</div>

// Manual (if utility not available)
<div className="
  bg-surface-secondary backdrop-blur-xs
  border border-glass-border
  shadow-glass
  p-6 rounded-xlarge
">
  {content}
</div>
```

### Focus Ring

```tsx
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
```

### Truncate Text

```tsx
className="truncate"           // Single line
className="line-clamp-2"       // Two lines
className="line-clamp-3"       // Three lines
```

---

**Reference**: See `apps/web/tailwind.config.ts` for complete CVA patterns
**Version**: 1.0
**Last Updated**: 2025-11-28

```

### references/layout-mobile.md

```markdown
# Layout & Mobile Patterns

Token-optimized layout patterns for responsive, mobile-first design. Covers shell layouts, safe areas, touch targets, and responsive breakpoints.

---

## App Shell Layout

### Desktop: Sidebar + Main Content

```tsx
<div className="flex h-screen">
  {/* Sidebar - 240px fixed, hidden on mobile */}
  <aside className="
    hidden md:block
    w-60                        /* 240px fixed width */
    bg-surface-secondary backdrop-blur-xs
    border-r border-border-subtle
    shadow-translucent
    overflow-y-auto
  ">
    {/* Logo */}
    <div className="p-6 border-b border-border-subtle">
      <Logo />
    </div>

    {/* Navigation */}
    <nav className="p-4 space-y-2">
      <NavItem href="/dashboard" icon={HomeIcon}>Dashboard</NavItem>
      <NavItem href="/people" icon={UsersIcon}>People</NavItem>
      <NavItem href="/occasions" icon={CalendarIcon}>Occasions</NavItem>
      <NavItem href="/lists" icon={ListIcon}>Lists</NavItem>
      <NavItem href="/gifts" icon={GiftIcon}>Gifts</NavItem>
    </nav>

    {/* User Profile (Bottom) */}
    <div className="mt-auto p-4 border-t border-border-subtle">
      <UserProfile />
    </div>
  </aside>

  {/* Main Content Area - flex-1 */}
  <main className="
    flex-1
    overflow-y-auto
    bg-bg-base
  ">
    {children}
  </main>
</div>
```

### Mobile: Bottom Navigation

```tsx
{/* Main content with bottom padding for nav */}
<main className="
  pb-20 md:pb-0              /* Space for bottom nav on mobile */
  overflow-y-auto
  bg-bg-base
">
  {children}
</main>

{/* Bottom Navigation - Fixed, Mobile Only */}
<nav className="
  fixed bottom-0 left-0 right-0
  md:hidden                   /* Hide on tablet+ */
  h-14                        /* 56px */
  pb-safe-area-inset-bottom   /* iOS safe area */
  bg-surface-secondary backdrop-blur-xs
  border-t border-border-subtle
  shadow-translucent
  flex items-center justify-around
  z-50
">
  {/* Nav Items (4-5 max) */}
  <NavButton href="/dashboard" icon={HomeIcon}>
    Dashboard
  </NavButton>
  <NavButton href="/people" icon={UsersIcon}>
    People
  </NavButton>
  <NavButton href="/occasions" icon={CalendarIcon}>
    Occasions
  </NavButton>
  <NavButton href="/lists" icon={ListIcon}>
    Lists
  </NavButton>
</nav>
```

### NavButton Component (Mobile Bottom Nav)

```tsx
<a
  href={href}
  className="
    flex flex-col items-center justify-center
    min-w-[44px] min-h-[44px]     /* Touch target */
    px-2 py-1
    text-warm-700
    hover:text-primary-500
    transition-colors duration-200
  "
>
  <Icon className="w-6 h-6" />
  <span className="text-xs mt-0.5">{label}</span>
</a>

{/* Active State */}
<a className="
  ...
  text-primary-500
  font-semibold
">
  <Icon className="w-6 h-6 fill-current" />
  <span className="text-xs mt-0.5">{label}</span>
</a>
```

---

## Safe Areas (iOS/Safari)

### Fixed Header with Safe Area

```tsx
<header className="
  fixed top-0 left-0 right-0
  pt-safe-area-inset-top        /* iOS notch padding */
  pl-safe-area-inset-left
  pr-safe-area-inset-right
  h-14                          /* + safe-area-inset-top */
  bg-surface-primary
  border-b border-border-subtle
  shadow-low
  z-40
">
  <div className="h-14 px-4 flex items-center justify-between">
    <Logo />
    <MenuButton />
  </div>
</header>
```

### Bottom Navigation with Safe Area

```tsx
<nav className="
  fixed bottom-0 left-0 right-0
  pb-safe-area-inset-bottom     /* iOS home indicator padding */
  pl-safe-area-inset-left
  pr-safe-area-inset-right
  h-14                          /* + safe-area-inset-bottom */
  ...
">
  {navItems}
</nav>
```

### Full-Width Background with Safe Area

```tsx
<div className="
  w-screen                      /* Full viewport width */
  bg-primary-100
  pl-safe-area-inset-left       /* Avoid notch/camera */
  pr-safe-area-inset-right
  pt-safe-area-inset-top
  pb-safe-area-inset-bottom
">
  {content}
</div>
```

---

## Viewport Height (100dvh Fix)

### Full-Height Container

```tsx
{/* ALWAYS use 100dvh (dynamic) instead of 100vh */}
<div className="h-dvh">         {/* Fixes iOS address bar issue */}
  {content}
</div>

{/* Fallback for older browsers */}
<div className="h-screen">      {/* Tailwind default: 100vh */}
  {content}
</div>

{/* Manual calculation (avoid if possible) */}
<div style={{ height: 'calc(100vh - 60px)' }}>
  {content}
</div>
```

### Full-Height with Header/Footer

```tsx
<div className="flex flex-col h-dvh">
  {/* Header - Fixed height */}
  <header className="h-14 flex-shrink-0">
    {header}
  </header>

  {/* Main - Flexible */}
  <main className="flex-1 overflow-y-auto">
    {content}
  </main>

  {/* Footer - Fixed height */}
  <footer className="h-14 flex-shrink-0">
    {footer}
  </footer>
</div>
```

---

## Touch Targets (44px Minimum)

### Button Touch Target

```tsx
{/* Minimum 44x44px for all interactive elements */}
<button className="
  min-h-[44px] min-w-[44px]     /* Minimum touch area */
  px-6 py-2                     /* Visual padding */
  ...
">
  Click Me
</button>
```

### Icon-Only Button

```tsx
<button className="
  w-11 h-11                     /* 44px exactly */
  flex items-center justify-center
  rounded-full
  hover:bg-warm-100
  transition-colors duration-200
">
  <Icon className="w-6 h-6" />  /* 24px icon */
</button>
```

### Checkbox/Radio Touch Area

```tsx
<label className="
  flex items-center
  min-h-[44px]                  /* Touch target height */
  cursor-pointer
">
  <input
    type="checkbox"
    className="
      w-5 h-5                   /* Visual size */
      cursor-pointer
    "
  />
  <span className="ml-3 text-body-large">
    Accept terms
  </span>
</label>
```

---

## Responsive Breakpoints

### Breakpoint Values

```yaml
xs:  375px   # iPhone SE
sm:  640px   # Small tablets
md:  768px   # Tablets (sidebar shows, bottom nav hides)
lg:  1024px  # Laptops
xl:  1280px  # Desktops
2xl: 1536px  # Large desktops
```

### Mobile-First Responsive Pattern

```tsx
<div className="
  w-full               /* Mobile: full width */
  md:w-1/2             /* Tablet: 50% */
  lg:w-1/3             /* Desktop: 33% */
  p-4                  /* Mobile: padding */
  md:p-6               /* Tablet: more padding */
  lg:p-8               /* Desktop: even more */
">
  Content
</div>
```

### Grid Responsive Pattern

```tsx
{/* Single column mobile, 2 cols tablet, 3 cols desktop */}
<div className="
  grid
  grid-cols-1          /* Mobile: 1 column */
  md:grid-cols-2       /* Tablet: 2 columns */
  lg:grid-cols-3       /* Desktop: 3 columns */
  gap-4                /* Mobile: 16px gap */
  md:gap-6             /* Tablet: 24px gap */
">
  {items.map(item => <Card key={item.id} {...item} />)}
</div>
```

### Show/Hide Based on Breakpoint

```tsx
{/* Visible on mobile only */}
<div className="block md:hidden">
  Mobile content
</div>

{/* Hidden on mobile, visible on tablet+ */}
<div className="hidden md:block">
  Desktop content
</div>

{/* Visible on desktop only */}
<div className="hidden lg:block">
  Desktop-only content
</div>
```

---

## Dashboard Layout

### Stat Cards Grid

```tsx
<div className="
  grid
  grid-cols-1           /* Mobile: stacked */
  md:grid-cols-3        /* Desktop: 3 columns */
  gap-6
  mb-12
">
  <StatCard value={12} label="Ideas" color="idea" />
  <StatCard value={8} label="To Buy" color="warning" />
  <StatCard value={4} label="Purchased" color="success" />
</div>
```

### Avatar Carousel (Horizontal Scroll)

```tsx
<div className="
  bg-surface-primary
  border border-border-subtle
  rounded-xlarge p-6
  shadow-low
">
  <h3 className="text-heading-2 font-semibold mb-4">
    Family Members
  </h3>

  {/* Horizontal scroll container */}
  <div className="
    flex gap-6
    overflow-x-auto          /* Horizontal scroll */
    pb-2                     /* Space for scrollbar */
    scrollbar-hide           /* Hide scrollbar (optional) */
  ">
    {members.map(member => (
      <div key={member.id} className="
        flex-shrink-0          /* Prevent shrinking */
        text-center
      ">
        <Avatar
          size="lg"
          src={member.avatar}
          status={member.status}
          badge={member.giftCount}
        />
        <p className="text-body-small font-semibold mt-2">
          {member.name}
        </p>
      </div>
    ))}
  </div>
</div>
```

### Kanban Columns (List Detail)

```tsx
{/* Desktop: Horizontal scroll, Mobile: Vertical stack */}
<div className="
  flex
  flex-col md:flex-row         /* Stack mobile, row desktop */
  gap-6
  overflow-x-auto              /* Horizontal scroll desktop */
  pb-6
">
  {/* Each column */}
  <div className="
    w-full md:w-80               /* Full mobile, 320px desktop */
    flex-shrink-0                /* Prevent shrinking */
    bg-surface-primary
    border border-border-subtle
    rounded-large
    shadow-low
  ">
    {/* Column Header */}
    <div className="
      px-4 py-3
      bg-gradient-to-br from-status-idea-50 to-status-idea-100
      border-b-4 border-status-idea-500
      rounded-t-large
    ">
      <h4 className="text-heading-3 font-semibold text-status-idea-700">
        Ideas <span className="text-body-small">(5)</span>
      </h4>
    </div>

    {/* Column Content */}
    <div className="
      p-4
      space-y-4
      min-h-[400px]
    ">
      {items.map(item => (
        <GiftCard key={item.id} {...item} />
      ))}
    </div>
  </div>

  {/* Repeat for other columns */}
</div>
```

---

## Content Spacing Patterns

### Page Container

```tsx
<div className="
  max-w-7xl mx-auto             /* Max 1280px, centered */
  px-4 md:px-6 lg:px-8          /* Responsive horizontal padding */
  py-6 md:py-8 lg:py-12        /* Responsive vertical padding */
">
  {pageContent}
</div>
```

### Section Spacing

```tsx
<section className="mb-12">     {/* 48px bottom margin */}
  <h2 className="text-heading-1 font-bold mb-6">
    Section Title
  </h2>
  {content}
</section>
```

### Card Grid Spacing

```tsx
<div className="
  grid
  grid-cols-1 md:grid-cols-2 lg:grid-cols-3
  gap-4 md:gap-6               /* Responsive gap */
">
  {cards}
</div>
```

---

## Z-Index Hierarchy

```yaml
z-0:    Base layer (backgrounds)
z-10:   Default content
z-20:   Sticky headers
z-30:   Dropdowns
z-40:   Modal overlays
z-50:   Modals, bottom nav
z-60+:  Tooltips, popovers
```

### Example Z-Index Usage

```tsx
{/* Modal Overlay */}
<div className="fixed inset-0 z-40 bg-overlay-strong" />

{/* Modal Content */}
<div className="fixed inset-0 z-50 flex items-center justify-center">
  {modal}
</div>

{/* Bottom Nav */}
<nav className="fixed bottom-0 left-0 right-0 z-50">
  {navItems}
</nav>
```

---

## Accessibility Considerations

### Focus Visible

```tsx
{/* All interactive elements */}
focus:outline-none
focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
```

### Skip to Content Link

```tsx
<a
  href="#main-content"
  className="
    sr-only                      /* Screen reader only by default */
    focus:not-sr-only            /* Visible when focused */
    focus:absolute focus:top-4 focus:left-4
    focus:z-50
    focus:px-4 focus:py-2
    focus:bg-primary-500 focus:text-white
    focus:rounded-medium
  "
>
  Skip to main content
</a>
```

### Responsive Font Sizes

```tsx
{/* Heading responsive sizing */}
<h1 className="
  text-display-small           /* Mobile: 28px */
  md:text-display-medium       /* Tablet: 36px */
  lg:text-display-large        /* Desktop: 48px */
">
  Page Title
</h1>
```

---

## Performance Optimizations

### Lazy Loading Images

```tsx
<img
  src={imageUrl}
  loading="lazy"                 /* Native lazy loading */
  className="rounded-large"
/>
```

### Scroll Snap (Carousels)

```tsx
<div className="
  flex gap-6
  overflow-x-auto
  snap-x snap-mandatory          /* Snap scrolling */
">
  <div className="snap-start flex-shrink-0">
    {item}
  </div>
</div>
```

---

## Common Layout Examples

### Modal Layout

```tsx
{/* Full pattern from component-variants.md */}
<div className="fixed inset-0 z-40 bg-overlay-strong backdrop-blur-sm" />
<div className="fixed inset-0 z-50 flex items-center justify-center p-6">
  <div className="
    w-full max-w-lg
    bg-surface-primary
    rounded-2xlarge
    shadow-extra-high
  ">
    {modalContent}
  </div>
</div>
```

### Sidebar Detail Panel

```tsx
<div className="flex h-screen">
  {/* Main content */}
  <main className="flex-1 overflow-y-auto">
    {content}
  </main>

  {/* Detail panel - slide in from right */}
  <aside className="
    w-96                         /* 384px */
    border-l border-border-subtle
    bg-surface-primary
    overflow-y-auto
    transform transition-transform duration-300
    translate-x-full             /* Hidden by default */
    data-[open=true]:translate-x-0  /* Visible when open */
  ">
    {detailContent}
  </aside>
</div>
```

---

**Reference**: See `docs/designs/LAYOUT-PATTERNS.md` for comprehensive layout specs
**Version**: 1.0
**Last Updated**: 2025-11-28

```

frontend-design | SkillHub