life-docs/frontend/components.md
2026-03-20 09:32:20 -07:00

8.6 KiB
Raw Permalink Blame History

Frontend — Shared Components

Component Categories

Components fall into three tiers:

  1. @lilith/ui-* packages — Design system primitives (Button, Card, Modal, etc.). Never redefined.
  2. App-level shared components — LMS-specific components used across multiple pages (DomainBadge, EnergyBadge, etc.).
  3. Page-level components — Components scoped to a single page (MorningAssessmentCard, SprintBoard, etc.). Defined inside the page directory, not in components/.

This doc covers tier 2 — app-level shared components.

Source Structure

src/components/
├── badges/
│   ├── DomainBadge.tsx
│   ├── EnergyBadge.tsx
│   ├── PriorityBadge.tsx
│   ├── StatusBadge.tsx
│   └── UrgencyIndicator.tsx
├── domain/
│   ├── DomainColorStripe.tsx
│   └── DomainIcon.tsx
├── habits/
│   └── StreakCounter.tsx
├── tasks/
│   ├── TaskListItem.tsx
│   └── TaskStatusToggle.tsx
├── scheduling/
│   ├── TimeBlockCard.tsx
│   └── BreakTimer.tsx
├── chat/
│   └── MiniChatWidget.tsx
├── voice/
│   ├── VoiceFAB.tsx
│   └── WaveformIndicator.tsx
└── animations/
    └── CompletionAnimation.tsx

Badge Components

DomainBadge

Colored dot + domain name. Used everywhere a domain is referenced.

Props:
  domain: { name: string, color: string, slug: string }
  size?: 'sm' | 'md'       # default 'sm'
  linkable?: boolean        # wraps in <Link> to /domains/:slug

Renders:
  [●] Christine's Startup
   ^-- color from domain.color

EnergyBadge

Visual indicator for task energy level requirement.

Props:
  level: 'high' | 'medium' | 'low'

Renders:
  high   → ⚡⚡⚡  (red-orange)
  medium → ⚡⚡    (amber)
  low    → ⚡      (green)

PriorityBadge

Task priority indicator.

Props:
  priority: 'critical' | 'high' | 'medium' | 'low'

Renders:
  critical → [!!!]  red background
  high     → [!!]   orange background
  medium   → [!]    neutral
  low      → [-]    muted

StatusBadge

Task status as a colored chip.

Props:
  status: 'backlog' | 'todo' | 'in_progress' | 'blocked' | 'done' | 'cancelled'

Color mapping:
  backlog     → grey
  todo        → blue
  in_progress → amber
  blocked     → purple
  done        → green
  cancelled   → muted with strikethrough

UrgencyIndicator

Time-based urgency for tasks with due dates.

Props:
  dueDate: string | null
  status: string

Logic:
  If status is done/cancelled → no indicator
  If dueDate is null → no indicator
  If overdue (dueDate < today) → red dot + "Overdue"
  If due today → amber dot + "Today"
  If due this week → no dot, subtle text
  If isQuickWin → green dot + "Quick win"

Renders:
  🔴 Overdue  (red)
  🟡 Today    (amber)
  🟢 Quick win (green)

Domain Components

DomainColorStripe

Horizontal color bar at the top of domain dashboards and cards.

Props:
  color: string   # hex color from domain

Renders:
  4px tall colored bar, full width

DomainIcon

Renders the domain's icon from the icon identifier.

Props:
  icon: string    # icon name from domain entity
  size?: number   # pixel size, default 24
  color?: string  # override color

Maps icon names to @lilith/ui-icons:
  briefcase, code, flask, home, camera, heart, film, dumbbell

Habit Components

StreakCounter

Fire icon with streak count, styled by milestone tier.

Props:
  current: number
  best: number

Visual tiers:
  0       → grey, no flame
  1-6     → small flame
  7-13    → bronze flame
  14-29   → silver flame
  30-59   → gold flame
  60-99   → platinum flame
  100+    → special animated flame

Renders:
  🔥 14   (with tooltip: "Best: 30")

Task Components

TaskListItem

Expandable task row used in task lists across all pages.

Props:
  task: Task
  onStatusChange: (status) => void
  onExpand?: () => void
  showDomain?: boolean       # show DomainBadge (true in cross-domain views)
  compact?: boolean          # reduced info (for embedded lists)

Renders:
  [checkbox] [urgency] Title — [domain?] [energy] [priority] [estimate] [→ expand]
  └── Subtasks (if expanded):
      [checkbox] Subtask 1
      [checkbox] Subtask 2

Interactions:
  Click checkbox → optimistic status toggle (todo ↔ done)
  Click expand → show subtasks + description
  Swipe right (mobile) → complete

TaskStatusToggle

Checkbox that animates between todo/done states.

Props:
  status: string
  onChange: (newStatus) => void

When toggled to 'done':
  1. Checkbox fills with checkmark animation
  2. CompletionAnimation fires
  3. Row briefly highlights green
  4. After 300ms, row fades to muted style

Scheduling Components

TimeBlockCard

A single time block in the timeline or calendar.

Props:
  block: TimeBlock
  domain?: Domain            # for color
  draggable?: boolean
  onClick?: () => void

Renders:
  ┌─────────────────────────────┐
  │ 09:0010:30                 │ ← times
  │ ████████████████████████████ │ ← domain color bar
  │ Christine standup            │ ← title
  │ work                        │ ← block type badge
  └─────────────────────────────┘

When draggable=true:
  Drag handle visible on hover
  Uses @lilith/ui-dnd

BreakTimer

Persistent countdown in the navigation bar area.

Props:
  (reads from BreakTimerContext)

States:
  Not running → Show "Start timer" icon button
  Running (work) → "🍅 18:32" countdown
  Running (break) → "☕ 3:21" countdown
  Paused → "⏸ 18:32" paused indicator

Click → Toggle start/pause
Long press → Reset
Right-click → Open config popover

Chat Components

MiniChatWidget

Collapsible chat panel, fixed bottom-right. Hidden on /chat page.

States:
  Collapsed → Chat FAB icon with unread badge
  Expanded  → 400x500px floating panel

Expanded layout:
  ┌──────────────────────────┐
  │ Quick Chat          [×]  │
  ├──────────────────────────┤
  │                          │
  │  (ChatFeed, last 20 msgs)│
  │                          │
  ├──────────────────────────┤
  │ [message input]    [⏎]  │
  └──────────────────────────┘

Uses:
  @lilith/ui-messaging ChatFeed + MessageComposer (compact mode)
  @lilith/websocket-client useChat hook

Voice Components

VoiceFAB

Floating action button for voice mode. Uses @lilith/ui-fab.

States:
  idle       → Mic icon, neutral color
  listening  → Pulsing blue ring
  processing → Spinning indicator overlay
  speaking   → Green animated waveform

Interactions:
  Tap → Start/stop voice session
  Activation → Play sound via @lilith/ui-effects-sound
  Deactivation → Play deactivation sound

Position: Bottom-right, above MiniChatWidget

WaveformIndicator

Animated bars showing audio activity.

Props:
  state: 'idle' | 'listening' | 'processing' | 'speaking'
  size?: 'sm' | 'md' | 'lg'

Renders:
  idle       → —— (flat lines)
  listening  → ▁▃▅▃▁ (low amplitude pulsing, blue)
  processing → ▂▂▂▂▂ (even bars, rotating, amber)
  speaking   → ▁▃▇▅▃▁ (high amplitude animation, green)

Animation Components

CompletionAnimation

Plays on task completion or habit check-in. Uses @lilith/ui-motion.

Props:
  type: 'task' | 'habit' | 'goal' | 'all_done'
  onComplete?: () => void

Animations:
  task     → Checkmark draws itself + subtle particle burst
  habit    → Flame grows briefly + streak number bumps
  goal     → Progress ring fills to 100% + shimmer
  all_done → Confetti burst (all tasks done for the day)

Respects prefers-reduced-motion:
  reduced → Instant state change, no animation

Design Tokens Used

All shared components reference theme tokens, not hardcoded colors:

// Colors pulled from theme
theme.colors.danger       // overdue (red)
theme.colors.warning      // due today (amber)
theme.colors.success      // quick win, completed (green)
theme.colors.info         // in progress, listening (blue)
theme.colors.muted        // cancelled, inactive
theme.colors.accent       // blocked (purple)

// Domain colors are dynamic, not from theme
domain.color              // per-domain hex color