oneie
Version:
Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.
1,745 lines (1,440 loc) • 62.6 kB
Markdown
---
title: 2 Design
dimension: things
category: features
tags: agent, auth, backend, frontend
related_dimensions: events, people
scope: global
created: 2025-11-03
updated: 2025-11-03
version: 1.0.0
ai_context: |
This document is part of the things dimension in the features category.
Location: one/things/features/2-design.md
Purpose: Documents feature 2: backend-agnostic frontend - design specification
Related dimensions: events, people
For AI agents: Read this to understand 2 design.
---
# Feature 2: Backend-Agnostic Frontend - Design Specification
**Feature ID:** `feature_2_design`
**Plan:** `plan_2_backend_agnostic_frontend`
**Owner:** Design Agent
**Status:** Complete Specification
**Priority:** P1 (High - Developer UX + User UX)
**Effort:** Ongoing throughout features 2-4, 2-5, 2-6
**Dependencies:** Features 2-4, 2-5, 2-6
---
## Executive Summary
This design specification defines the visual and interaction patterns for THREE distinct areas:
1. **Developer API Design (Feature 2-4)** - Hook API ergonomics and TypeScript IntelliSense
2. **Auth Component UI (Feature 2-5)** - 6 authentication components with wireframes
3. **Dashboard Component UI (Feature 2-6)** - 5 dashboard components with wireframes
**Design Philosophy:** Test-driven, accessibility-first, mobile-first, minimal yet sophisticated.
**Critical Success Factor:** Zero visual regression. All existing functionality preserved.
---
## Table of Contents
1. [Design Philosophy](#1-design-philosophy)
2. [Design System Foundation](#2-design-system-foundation)
3. [Feature 2-4: Developer UX Design](#3-feature-24-developer-ux-design)
4. [Feature 2-5: Auth Component Design](#4-feature-25-auth-component-design)
5. [Feature 2-6: Dashboard Component Design](#5-feature-26-dashboard-component-design)
6. [Accessibility Specifications](#6-accessibility-specifications)
7. [Responsive Design System](#7-responsive-design-system)
8. [Animation & Transitions](#8-animation--transitions)
9. [Implementation Guidelines](#9-implementation-guidelines)
10. [Quality Checklist](#10-quality-checklist)
---
## 1. Design Philosophy
### Test-Driven Design
Every design decision must enable a test to pass:
```typescript
// Test defines expected behavior
it('should display course list with loading state', async () => {
render(<CourseList />);
expect(screen.getByTestId('loading-skeleton')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('Course 1')).toBeInTheDocument();
});
});
// Design satisfies test
function CourseList() {
const { data: courses, loading } = useThings({ type: 'course' });
if (loading) return <Skeleton data-testid="loading-skeleton" count={3} />;
return courses.map(course => <CourseCard key={course._id} course={course} />);
}
```
**Principle:** Design is NOT decoration. It's the interface layer that makes features testable.
### Accessibility-First
WCAG 2.1 AA compliance is non-negotiable:
- Color contrast ratio ≥ 4.5:1 for body text
- Color contrast ratio ≥ 3:1 for large text (≥18px)
- Keyboard navigation (Tab, Enter, Escape)
- Focus indicators visible (2px outline)
- ARIA labels on interactive elements
- Screen reader announcements
**Principle:** If it's not accessible, it's not designed.
### Mobile-First
Design for smallest screen first (320px):
```
320px (mobile) → 768px (tablet) → 1024px (desktop) → 1440px (wide)
```
**Principle:** Mobile constraints force clarity. Desktop adds convenience.
### Minimal Yet Sophisticated
Remove unnecessary visual elements:
- No decorative gradients
- No custom illustrations (unless functional)
- No complex animations (unless feedback)
- Prioritize readability and usability
**Principle:** Every pixel serves a purpose.
---
## 2. Design System Foundation
### Color Palette (Tailwind v4)
**Base Colors (HSL Format):**
```css
@theme {
/* Light mode */
--color-background: 36 8% 88%;
--color-foreground: 0 0% 13%;
--color-card: 36 10% 74%;
--color-card-foreground: 0 0% 13%;
--color-primary: 216 55% 25%;
--color-primary-foreground: 36 8% 96%;
--color-secondary: 219 14% 28%;
--color-secondary-foreground: 36 8% 96%;
--color-muted: 219 14% 92%;
--color-muted-foreground: 219 14% 30%;
--color-accent: 105 22% 25%;
--color-accent-foreground: 36 8% 96%;
--color-destructive: 0 84% 60%;
--color-destructive-foreground: 0 0% 100%;
--color-border: 0 0% 100% / 0.1;
--color-input: 0 0% 100% / 0.1;
--color-ring: 216 63% 17%;
}
.dark {
/* Dark mode overrides */
--color-background: 0 0% 13%;
--color-foreground: 36 8% 96%;
--color-card: 0 0% 10%;
--color-card-foreground: 36 8% 96%;
--color-primary: 216 63% 68%;
--color-primary-foreground: 0 0% 13%;
--color-muted: 216 63% 17%;
--color-muted-foreground: 36 8% 80%;
}
```
**Semantic Colors:**
- `background` - Page background
- `foreground` - Body text
- `card` - Card backgrounds
- `primary` - Primary actions (buttons, links)
- `secondary` - Secondary actions
- `muted` - Disabled states, subtle backgrounds
- `accent` - Highlights, special states
- `destructive` - Errors, delete actions
**Usage:**
```tsx
<div className="bg-background text-foreground">
<div className="bg-card text-card-foreground border border-border">
<Button className="bg-primary text-primary-foreground">Primary</Button>
<Button className="bg-destructive text-destructive-foreground">
Delete
</Button>
</div>
</div>
```
### Typography Scale
**Font Family:**
```css
font-family:
Inter,
system-ui,
-apple-system,
sans-serif;
```
**Scale (Modular 1.25x):**
```css
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
```
**Weights:**
- `font-normal` - 400 (Body text)
- `font-medium` - 500 (Emphasis)
- `font-semibold` - 600 (Headings)
- `font-bold` - 700 (Strong emphasis)
**Line Heights:**
- `leading-tight` - 1.25 (Headings)
- `leading-normal` - 1.5 (Body)
- `leading-relaxed` - 1.625 (Long-form content)
### Spacing Scale
**Base Unit: 4px**
```css
--spacing-1: 0.25rem; /* 4px */
--spacing-2: 0.5rem; /* 8px */
--spacing-3: 0.75rem; /* 12px */
--spacing-4: 1rem; /* 16px */
--spacing-6: 1.5rem; /* 24px */
--spacing-8: 2rem; /* 32px */
--spacing-12: 3rem; /* 48px */
--spacing-16: 4rem; /* 64px */
--spacing-24: 6rem; /* 96px */
--spacing-32: 8rem; /* 128px */
```
**Common Patterns:**
- **Component padding:** `p-4` (16px)
- **Card spacing:** `p-6` (24px)
- **Form field gap:** `space-y-4` (16px)
- **Section spacing:** `space-y-8` (32px)
### Border Radius
```css
--radius-sm: 0.375rem; /* 6px */
--radius-md: 0.75rem; /* 12px */
--radius-lg: 1.5rem; /* 24px */
```
**Usage:**
- **Buttons:** `rounded-md` (12px)
- **Cards:** `rounded-lg` (24px)
- **Inputs:** `rounded-md` (12px)
- **Pills/Badges:** `rounded-full`
### Shadows
```css
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
```
**Usage:**
- **Cards:** `shadow-md`
- **Dropdowns:** `shadow-lg`
- **Buttons:** `shadow-sm` (hover)
---
## 3. Feature 2-4: Developer UX Design
### Hook API Design
**Goal:** Beautiful, simple, powerful API that developers love to use.
**Core Principles:**
1. **Familiar** - Match Convex hook ergonomics
2. **Type-Safe** - Full TypeScript cycle
3. **Consistent** - Same pattern everywhere
4. **Predictable** - Loading/error states identical
5. **Discoverable** - IntelliSense provides great suggestions
### Hook Return Type Pattern
**Query Hooks:**
```typescript
interface QueryResult<T> {
data: T | null; // Query result (null during loading)
loading: boolean; // Boolean loading state
error: Error | null; // Error object or null
refetch: () => void; // Manual refetch function
}
```
**Mutation Hooks:**
```typescript
interface MutationResult<T> {
mutate: (...args: any[]) => Promise<T>; // Async mutation function
loading: boolean; // Boolean loading state
error: Error | null; // Error object or null
reset: () => void; // Clear error state
}
```
### Developer Experience Examples
**Example 1: Simple Query (Minimal Code)**
```typescript
// Minimal code, maximum clarity
const { data: courses, loading, error } = useThings({ type: 'course' });
if (loading) return <LoadingSkeleton />;
if (error) return <ErrorMessage error={error} />;
return courses.map(course => <CourseCard course={course} />);
```
**Example 2: Mutation with Toast**
```typescript
const { mutate: createCourse, loading } = useCreateThing();
async function handleSubmit(data) {
try {
await createCourse({ type: "course", ...data });
toast.success("Course created!");
} catch (error) {
toast.error(error.message);
}
}
```
**Example 3: Optimistic Updates**
```typescript
const { mutate: updateCourse } = useUpdateThing();
// UI updates immediately, rollback on error
await updateCourse(
{
id: courseId,
name: "Updated Name",
},
{
optimistic: true,
},
);
```
### Error Message Taxonomy
**Typed Errors with Helpful Messages:**
```typescript
type DataProviderError =
| { _tag: 'NotFoundError'; message: string; suggestion: string }
| { _tag: 'UnauthorizedError'; message: string; suggestion: string }
| { _tag: 'ValidationError'; message: string; field: string }
| { _tag: 'NetworkError'; message: string; retryable: boolean };
// Usage in component
if (error?._tag === 'NotFoundError') {
return (
<Alert variant="info">
<AlertTitle>No courses found</AlertTitle>
<AlertDescription>
{error.message}
<Button onClick={() => navigate('/create')}>
{error.suggestion}
</Button>
</AlertDescription>
</Alert>
);
}
```
### TypeScript IntelliSense Design
**Auto-complete on Hook Arguments:**
```typescript
// IntelliSense shows: type, status, organizationId
useThings({
type: '|' // <- Autocomplete shows all 66 entity types
status: '|' // <- Autocomplete shows: draft, active, published, archived
});
```
**Type Cycle on Results:**
```typescript
// TypeScript infers correct type from filter
const { data: courses } = useThings({ type: "course" });
// courses: Thing[] with properties specific to 'course'
```
### Loading State Patterns
**Pattern 1: Skeleton Loader**
```typescript
if (loading) {
return (
<div className="space-y-4">
<Skeleton className="h-12 w-full" />
<Skeleton className="h-12 w-full" />
<Skeleton className="h-12 w-full" />
</div>
);
}
```
**Pattern 2: Spinner**
```typescript
if (loading) {
return (
<div className="flex justify-center p-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
);
}
```
**Pattern 3: Button Loading**
```tsx
<Button disabled={loading}>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating...
</>
) : (
"Create Course"
)}
</Button>
```
---
## 4. Feature 2-5: Auth Component Design
### Component Overview
**6 Authentication Components:**
1. LoginForm (Email/Password)
2. SignupForm (Email/Password)
3. OAuthButtons (GitHub, Google)
4. MagicLinkForm (Request + Authenticate)
5. PasswordResetForm (Request + Complete)
6. TwoFactorSettings (Setup + Verify)
### Design Tokens for Auth
```css
/* Auth-specific colors */
--color-auth-success: 142 76% 36%;
--color-auth-error: 0 84% 60%;
--color-auth-warning: 38 92% 50%;
--color-auth-info: 199 89% 48%;
/* OAuth provider colors */
--color-github: 0 0% 13%;
--color-google: 4 90% 58%;
--color-apple: 0 0% 0%;
```
### 1. LoginForm Component
**Wireframe:**
```
┌─────────────────────────────────────────────────┐
│ │
│ Welcome Back │
│ Sign in to your account │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Email │ │
│ │ your@email.com │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Password Forgot password?│ │
│ │ •••••••••• │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Sign In │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ────────── OR ────────── │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ [GitHub logo] │ │ [Google logo] │ │
│ │ Sign in with │ │ Sign in with │ │
│ │ GitHub │ │ Google │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ Don't have an account? Sign up │
│ │
└─────────────────────────────────────────────────┘
```
**Component States:**
```typescript
interface LoginFormState {
email: string;
password: string;
loading: boolean;
error: AuthError | null;
}
// States to design:
// 1. Default (empty fields)
// 2. Typing (focus state)
// 3. Loading (button disabled, spinner)
// 4. Error (red border, error message)
// 5. Success (green checkmark, redirect)
```
**Implementation Example:**
```tsx
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-2xl font-bold text-center">
Welcome Back
</CardTitle>
<CardDescription className="text-center">
Sign in to your account
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="your@email.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className={cn(
"transition-colors",
error && "border-destructive focus:ring-destructive",
)}
/>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="password">Password</Label>
<Link
href="/account/forgot-password"
className="text-xs text-primary hover:underline"
>
Forgot password?
</Link>
</div>
<Input
id="password"
type="password"
placeholder="••••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error.message}</AlertDescription>
</Alert>
)}
<Button type="submit" className="w-full" disabled={loading}>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Signing in...
</>
) : (
"Sign In"
)}
</Button>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t border-border" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-card px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<Button variant="outline" type="button">
<Github className="mr-2 h-4 w-4" />
GitHub
</Button>
<Button variant="outline" type="button">
<Mail className="mr-2 h-4 w-4" />
Google
</Button>
</div>
</form>
</CardContent>
<CardFooter className="flex justify-center">
<p className="text-sm text-muted-foreground">
Don't have an account?{" "}
<Link href="/account/signup" className="text-primary hover:underline">
Sign up
</Link>
</p>
</CardFooter>
</Card>
```
### 2. SignupForm Component
**Wireframe:**
```
┌─────────────────────────────────────────────────┐
│ │
│ Create Account │
│ Sign up to get started │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Name (optional) │ │
│ │ Your name │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Email │ │
│ │ your@email.com │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Password │ │
│ │ •••••••••• │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Password strength: ▓▓▓▓░░░░ Medium │
│ • At least 8 characters │
│ • Include numbers and letters │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Sign Up │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Already have an account? Sign in │
│ │
└─────────────────────────────────────────────────┘
```
**Password Strength Indicator:**
```tsx
<div className="space-y-2">
<div className="flex items-center gap-2">
<div className="flex-1 h-2 bg-muted rounded-full overflow-hidden">
<div
className={cn(
"h-full transition-all duration-300",
strength === "weak" && "w-1/4 bg-destructive",
strength === "medium" && "w-2/4 bg-warning",
strength === "strong" && "w-3/4 bg-success",
strength === "very-strong" && "w-full bg-success",
)}
/>
</div>
<span className="text-xs font-medium">
{strength.charAt(0).toUpperCase() + strength.slice(1)}
</span>
</div>
<ul className="text-xs text-muted-foreground space-y-1">
<li className={cn(password.length >= 8 && "text-success")}>
• At least 8 characters
</li>
<li className={cn(/\d/.test(password) && "text-success")}>
• Include numbers
</li>
<li className={cn(/[A-Z]/.test(password) && "text-success")}>
• Include uppercase letters
</li>
</ul>
</div>
```
### 3. OAuthButtons Component
**Wireframe:**
```
┌─────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────┐ │
│ │ [GitHub Icon] Sign in with │ │
│ │ GitHub │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ [Google Icon] Sign in with │ │
│ │ Google │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ [Apple Icon] Sign in with │ │
│ │ Apple │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**Implementation:**
```tsx
<div className="grid gap-3">
<Button
variant="outline"
className="w-full"
onClick={() => handleOAuth("github")}
>
<Github className="mr-2 h-4 w-4" />
Continue with GitHub
</Button>
<Button
variant="outline"
className="w-full"
onClick={() => handleOAuth("google")}
>
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24">
{/* Google logo SVG */}
</svg>
Continue with Google
</Button>
<Button
variant="outline"
className="w-full"
onClick={() => handleOAuth("apple")}
>
<Apple className="mr-2 h-4 w-4" />
Continue with Apple
</Button>
</div>
```
### 4. PasswordResetForm Component
**Wireframe (Request):**
```
┌─────────────────────────────────────────────────┐
│ │
│ Forgot your password? │
│ Enter your email to receive reset │
│ instructions │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Email │ │
│ │ your@email.com │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Send reset email │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Back to sign in │
│ │
└─────────────────────────────────────────────────┘
```
**Wireframe (Success State):**
```
┌─────────────────────────────────────────────────┐
│ │
│ ✓ Check your email │
│ │
│ We've sent password reset instructions to: │
│ your@email.com │
│ │
│ If you don't see the email, check your │
│ spam folder. │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Try another email │ │
│ └───────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
```
### 5. TwoFactorSettings Component
**Wireframe (Setup):**
```
┌─────────────────────────────────────────────────┐
│ │
│ Setup Two-Factor Authentication │
│ │
│ Step 1: Scan QR Code │
│ ┌─────────────────────┐ │
│ │ │ │
│ │ [QR CODE IMAGE] │ │
│ │ │ │
│ └─────────────────────┘ │
│ │
│ Or enter this code manually: │
│ ┌───────────────────────────────────────────┐ │
│ │ ABCD-EFGH-IJKL-MNOP [Copy] │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Step 2: Enter verification code │
│ ┌───────────────────────────────────────────┐ │
│ │ 123456 │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Verify and Enable │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Backup Codes (Save these securely): │
│ • 1234-5678-9012 │
│ • 3456-7890-1234 │
│ • 5678-9012-3456 │
│ │
└─────────────────────────────────────────────────┘
```
### 6. MagicLinkForm Component
**Wireframe (Request):**
```
┌─────────────────────────────────────────────────┐
│ │
│ Sign in with Magic Link │
│ No password required │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Email │ │
│ │ your@email.com │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ Send magic link │ │
│ └───────────────────────────────────────────┘ │
│ │
│ Back to sign in │
│ │
└─────────────────────────────────────────────────┘
```
**Wireframe (Authenticating):**
```
┌─────────────────────────────────────────────────┐
│ │
│ Signing you in... │
│ │
│ [Spinner animation] │
│ │
│ Please wait while we authenticate your │
│ magic link │
│ │
└─────────────────────────────────────────────────┘
```
---
## 5. Feature 2-6: Dashboard Component Design
### Component Overview
**5 Dashboard Components:**
1. DashboardOverview (Analytics + Stats)
2. CourseList (Entity List)
3. CourseDetail (Entity Detail)
4. ActivityFeed (Event Stream)
5. SearchBar (Full-text Search)
### Design Tokens for Dashboard
```css
/* Chart colors */
--color-chart-1: 216 55% 25%;
--color-chart-2: 105 22% 32%;
--color-chart-3: 219 14% 40%;
--color-chart-4: 36 8% 55%;
--color-chart-5: 0 0% 13%;
/* Status colors */
--color-status-draft: 219 14% 92%;
--color-status-active: 142 76% 36%;
--color-status-published: 199 89% 48%;
--color-status-archived: 0 0% 60%;
```
### 1. DashboardOverview Component
**Wireframe:**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ Dashboard [User Menu] │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Courses │ │ Students │ │ Active │ │ Revenue │ │
│ │ 24 │ │ 156 │ │ 89 │ │ $12,450 │ │
│ │ +12% │ │ +8% │ │ +15% │ │ +23% │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ Activity Over Time │ │ Revenue Charts │ │
│ │ │ │ │ │
│ │ [Line Chart] │ │ [Bar Chart] │ │
│ │ │ │ │ │
│ └────────────────────────┘ └────────────────────────┘ │
│ │
│ Recent Transactions │
│ ┌──────────────────────────────────────────────────────────┐│
│ │ Date │ Amount │ Type │ From │ Status ││
│ ├──────────────────────────────────────────────────────────┤│
│ │ Oct 13 │ $450 │ Purchase │ John Doe │ ✓ ││
│ │ Oct 12 │ $280 │ Purchase │ Jane Smith│ ✓ ││
│ │ Oct 12 │ $125 │ Refund │ Bob Jones │ ⟳ ││
│ └──────────────────────────────────────────────────────────┘│
│ │
└────────────────────────────────────────────────────────────────┘
```
**Stats Card Component:**
```tsx
<Card>
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium">Total Courses</CardTitle>
<BookOpen className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">24</div>
<p className="text-xs text-muted-foreground">
<span className="text-success">+12%</span> from last month
</p>
</CardContent>
</Card>
```
### 2. CourseList Component
**Wireframe (List View):**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ Courses [+ New Course] │
│ ┌──────────┐ Grid List [Search] │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Image] Introduction to React │ │
│ │ Learn React fundamentals │ │
│ │ Published • 156 students • 4.8★ │ │
│ │ [Edit] [Delete] │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Image] Advanced TypeScript │ │
│ │ Master TypeScript patterns │ │
│ │ Draft • 0 students │ │
│ │ [Edit] [Delete] │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ [Load More] │
│ │
└────────────────────────────────────────────────────────────────┘
```
**Wireframe (Grid View):**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ Courses [+ New Course] │
│ Grid ┌──────────┐ List [Search] │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ [Image] │ │ [Image] │ │ [Image] │ │
│ │ │ │ │ │ │ │
│ │ React Basics │ │ TypeScript │ │ Node.js │ │
│ │ Published │ │ Draft │ │ Published │ │
│ │ 156 students │ │ 0 students │ │ 89 students │ │
│ │ 4.8★ │ │ │ │ 4.9★ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
```
**Loading State:**
```tsx
<div className="space-y-4">
{[...Array(3)].map((_, i) => (
<Card key={i}>
<CardHeader className="flex flex-row items-center gap-4">
<Skeleton className="h-16 w-16 rounded-md" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-3 w-1/2" />
</div>
</CardHeader>
</Card>
))}
</div>
```
### 3. CourseDetail Component
**Wireframe:**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ ← Back to Courses │
│ │
│ Introduction to React [Edit] [•••] │
│ Published • Created Oct 1, 2024 │
│ │
│ ┌────────────────────────┐ ┌──────────────────────────┐ │
│ │ │ │ Details │ │
│ │ [Course Image] │ │ Status: Published │ │
│ │ │ │ Students: 156 │ │
│ │ │ │ Rating: 4.8★ │ │
│ └────────────────────────┘ │ Created: Oct 1, 2024 │ │
│ │ Updated: Oct 13, 2024 │ │
│ Description └──────────────────────────┘ │
│ Learn the fundamentals of React including... │
│ │
│ Lessons (8) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. Introduction to React [Published] 30 min │ │
│ │ 2. Components and Props [Published] 45 min │ │
│ │ 3. State and Lifecycle [Draft] 60 min │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ Enrolled Students (156) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Avatar] John Doe Enrolled: Oct 5 Progress: 60% │ │
│ │ [Avatar] Jane Smith Enrolled: Oct 6 Progress: 45% │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
```
### 4. ActivityFeed Component
**Wireframe:**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ Recent Activity [Filter: All ▼] │
│ │
│ Today │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Icon] John Doe enrolled in Introduction to React │ │
│ │ 2 minutes ago │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Icon] You published "Advanced TypeScript" │ │
│ │ 1 hour ago │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ Yesterday │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Icon] Jane Smith completed "React Basics" lesson │ │
│ │ Yesterday at 3:45 PM │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ [Load More] │
│ │
└────────────────────────────────────────────────────────────────┘
```
**Event Card Component:**
```tsx
<Card>
<CardContent className="flex items-start gap-4 p-4">
<div
className={cn(
"rounded-full p-2",
eventType === "enrolled" && "bg-primary/10 text-primary",
eventType === "completed" && "bg-success/10 text-success",
eventType === "created" && "bg-accent/10 text-accent",
)}
>
{getEventIcon(eventType)}
</div>
<div className="flex-1">
<p className="text-sm">{event.description}</p>
<p className="text-xs text-muted-foreground mt-1">
{formatDistanceToNow(event.timestamp)} ago
</p>
</div>
</CardContent>
</Card>
```
### 5. SearchBar Component
**Wireframe:**
```
┌────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [🔍] Search courses, lessons, students... [Cmd+K] │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ [Dropdown appears on focus/typing] │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Recent Searches │ │
│ │ • Introduction to React │ │
│ │ • TypeScript basics │ │
│ │ │ │
│ │ Courses (3) │ │
│ │ [Icon] Introduction to React │ │
│ │ [Icon] Advanced TypeScript │ │
│ │ [Icon] Node.js Fundamentals │ │
│ │ │ │
│ │ Students (2) │ │
│ │ [Avatar] John Doe │ │
│ │ [Avatar] Jane Smith │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
```
**Implementation:**
```tsx
<Command className="rounded-lg border shadow-md">
<CommandInput placeholder="Search courses, lessons, students..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Recent Searches">
{recentSearches.map((search) => (
<CommandItem key={search} onSelect={() => handleSearch(search)}>
<Clock className="mr-2 h-4 w-4" />
{search}
</CommandItem>
))}
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Courses">
{courses.map((course) => (
<CommandItem
key={course._id}
onSelect={() => navigate(`/courses/${course._id}`)}
>
<BookOpen className="mr-2 h-4 w-4" />
{course.name}
</CommandItem>
))}
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Students">
{students.map((student) => (
<CommandItem key={student._id}>
<User className="mr-2 h-4 w-4" />
{student.name}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
```
---
## 6. Accessibility Specifications
### WCAG 2.1 AA Requirements
**Color Contrast:**
```css
/* Body text: 4.5:1 minimum */
background: hsl(36 8% 88%); /* Light */
foreground: hsl(0 0% 13%); /* Dark */
contrast-ratio: 11.8:1; /* ✓ Pass */
/* Large text (≥18px): 3:1 minimum */
primary: hsl(216 55% 25%);
background: hsl(36 8% 88%);
contrast-ratio: 7.2:1; /* ✓ Pass */
/* Dark mode */
background: hsl(0 0% 13%);
foreground: hsl(36 8% 96%);
contrast-ratio: 13.5:1; /* ✓ Pass */
```
**Keyboard Navigation:**
```tsx
// Tab order
<form>
<Input tabIndex={1} /> {/* First */}
<Input tabIndex={2} /> {/* Second */}
<Button tabIndex={3}> {/* Third */}
Submit
</Button>
</form>
// Skip to main content
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50"
>
Skip to main content
</a>
```
**Focus Indicators:**
```css
*:focus-visible {
outline: 2px solid hsl(var(--color-ring));
outline-offset: 2px;
border-radius: 0.125rem;
}
/* Ensure visible even on dark backgrounds */
.dark *:focus-visible {
outline-color: hsl(var(--color-ring));
outline-width: 2px;
}
```
**ARIA Labels:**
```tsx
// Form inputs
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
aria-required="true"
aria-invalid={!!error}
aria-describedby={error ? "email-error" : undefined}
/>
{error && (
<p id="email-error" className="text-sm text-destructive" role="alert">
{error}
</p>
)}
// Buttons
<Button aria-label="Close dialog">
<X className="h-4 w-4" />
</Button>
// Loading states
<div role="status" aria-live="polite">
{loading ? 'Loading...' : 'Loaded'}
</div>
```
**Screen Reader Announcements:**
```tsx
// Toast notifications
toast.success("Course created", {
role: "status",
ariaLive: "polite",
});
// Error announcements
<Alert role="alert" aria-live="assertive">
<AlertDescription>{error.message}</AlertDescription>
</Alert>;
// Loading announcements
{
loading && (
<span className="sr-only" role="status">
Loading courses...
</span>
);
}
```
---
## 7. Responsive Design System
### Breakpoints
```css
/* Mobile First */
/* Default: 320px+ */
@media (min-width: 640px) {
/* Small devices (sm:) */
}
@media (min-width: 768px) {
/* Tablets (md:) */
}
@media (min-width: 1024px) {
/* Desktop (lg:) */
}
@media (min-width: 1280px) {
/* Wide (xl:) */
}
@media (min-width: 1536px) {
/* Extra wide (2xl:) */
}
```
### Responsive Patterns
**Pattern 1: Stack on Mobile, Grid on Desktop**
```tsx
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
</div>
```
**Pattern 2: Sidebar Layout**
```tsx
<div className="flex flex-col lg:flex-row gap-6">
<aside className="lg:w-64">
{/* Sidebar: Full width on mobile, fixed width on desktop */}
</aside>
<main className="flex-1">{/* Main content */}</main>
</div>
```
**Pattern 3: Hide/Show Elements**
```tsx
<div className="hidden md:block">
{/* Desktop only */}
</div>
<div className="md:hidden">
{/* Mobile only */}
</div>
```
**Pattern 4: Responsive Typography**
```tsx
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold">
{/* Scales with screen size */}
</h1>
```
**Pattern 5: Responsive Spacing**
```tsx
<div className="p-4 md:p-6 lg:p-8">{/* More padding on larger screens */}</div>
```
### Touch Targets
**Minimum Size: 44x44px**
```tsx
<Button className="min-h-[44px] min-w-[44px]">
{/* Meets touch target size */}
</Button>
// Icon buttons
<Button size="icon" className="h-11 w-11">
<X className="h-4 w-4" />
</Button>
```
---
## 8. Animation & Transitions
### Principles
1. **Purposeful** - Animations provide feedback or guide attention
2. **Fast** - 150-300ms for most transitions
3. **Smooth** - Ease-in-out curves feel natural
4. **Respectful** - Honor prefers-reduced-motion
### Transition Timings
```css
/* Fast: 150ms - Hover states, focus */
transition: all 150ms ease-in-out;
/* Medium: 250ms - Panel slides, dropdowns */
transition: all 250ms ease-in-out;
/* Slow: 350ms - Page transitions, modals */
transition: all 350ms ease-in-out;
```
### Common Animations
**Fade In:**
```tsx
<div className="animate-in fade-in duration-300">
{/* Fades in over 300ms */}
</div>
```
**Slide In:**
```tsx
<div className="animate-in slide-in-from-bottom-4 duration-300">
{/* Slides up from bottom */}
</div>
```
**Spinner:**
```tsx
<Loader2 className="h-4 w-4 animate-spin" />
```
**Scale on Hover:**
```tsx
<Card className="transition-transform hover:scale-105">
{/* Scales up 5% on hover */}
</Card>
```
**Skeleton Pulse:**
```tsx
<div className="animate-pulse bg-muted h-4 w-full rounded" />
```
### Reduced Motion
```css
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
```
---
## 9. Implementation Guidelines
### Component Structure
```tsx
// ✅ GOOD: Clear structure
export function CourseList() {
// 1. Hooks
const { data: courses, loading, error } = useThings({ type: "course" });
// 2. Derived state
const publishedCourses = courses?.filter((c) => c.status === "published");
// 3. Event handlers
const handleDelete = (id) => {
/* ... */
};
// 4. Early returns
if (loading) return <LoadingSkeleton />;
if (error) return <ErrorMessage error={error} />;
if (!courses?.length) return <EmptyState />;
// 5. Main render
return (
<div className="space-y-4">
{courses.map((course) => (
<CourseCard key={course._id} course={course} />
))}
</div>
);
}
```
### State Management
```tsx
// ✅ GOOD: Minimal local state
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { mutate: login, loading, error } = useLogin();
return (/* ... */);
}
// ❌ BAD: Too much state
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [showPassword, setShowPassword] = useState(false);
const [emailValid, setEmailValid] = useState(false);
// ... too many useState calls
}
```
### Error Handling
```tsx
// ✅ GOOD: User-friendly errors
if (error) {
return (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Failed to load courses</AlertTitle>
<AlertDescription>
{error.message}
<Button onClick={refetch} variant="outline" className="mt-2">
Try Again
</Button>
</AlertDescription>
</Alert>
);
}
// ❌ BAD: Generic error
if (error) {
return <div>Error occurred</div>;
}
```
### Loading States
```tsx
// ✅ GOOD: Skeleton that matches content
if (loading) {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-4 w-1/2 mt-2" />
</CardHeader>
<CardContent>
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full mt-2" />
</CardContent>
</Card>
);
}
// ❌ BAD: Generic spinner
if (loading) {
return <Spinner />;
}
```
---
## 10. Quality Checklist
### Design Deliverables
**Feature 2-4 (React Hooks):**
- [x] Hook API design specification
- [x] TypeScript type design
- [x] Error message taxonomy
- [x] Loading state patterns
- [x] Code examples (10+ scenarios)
**Feature 2-5 (Auth Components):**
- [x] LoginForm wireframe + implementation
- [x] SignupForm wireframe + implementation
- [x] OAuthButtons wireframe + implementation
- [x] PasswordResetForm wireframe + implementation
- [x] TwoFactorSettings wireframe + implementation
- [x] MagicLinkForm wireframe + implementation
- [x] Loading state animations
- [x] Error state designs
- [x] Success feedback designs
- [x] Accessibility audit checklist
**Feature 2-6 (Dashboard Components):**
- [x] DashboardOverview wireframe + implementation
- [x] CourseList wireframe + implementation (list + grid views)
- [x] CourseDetail wireframe + implementation
- [x] ActivityFeed wireframe + implementation
- [x] SearchBar wireframe + implementation
- [x] Loading skeleton designs
- [x] Empty state designs
- [x] Error state designs
- [x] Real-time update animations
### Design System Compliance
- [x] All colors use HSL format
- [x] All colors wrapped with `hsl()` function
- [x] Color contrast ratios meet WCAG AA
- [x] Typography scale is consistent
- [x] Spacing follows 4px base unit
- [x] Border radius is consistent
- [x] Shadows are consistent
- [x] Dark mode colors defined
### Accessibility Compliance
- [x] Color contrast ≥ 4.5:1 for body text
- [x] Color contrast ≥ 3:1 for large text
- [x] Keyboard navigation works
- [x] Focus indicators visible
- [x] ARIA labels on interactive elements
- [x] Screen reader announcements
- [x] Form labels associated with inputs
- [x] Error messages announced
- [x] Touch targets ≥ 44x44px
### Responsive Design
- [x] Mobile-first approach (320px+)
- [x] Breakpoints defined (sm, md, lg, xl, 2xl)
- [x] Layout works on all screen sizes
- [x] Touch interactions work
- [x] Text scales with zoom
- [x] Images are responsive
### Implementation Readiness
- [x] Wireframes complete with ASCII art
- [x] Design tokens defined
- [x] Component states specified
- [x] Code examples provided
- [x] Tailwind v4 syntax correct
- [x] shadcn/ui components used
- [x] Dark mode support complete
### Testing Readiness
- [x] Loading states can be tested
- [x] Error states can be tested
- [x] Success states can be tested
- [x] Empty states can be tested
- [x] Accessibility can be validated
---
## Related Files
- **Plan:** `one/things/plans/2-backend-agnostic-frontend.md`
- **Feature 2-4:** `one/things/features/2-4-react-hooks.md` (Developer UX)
- **Feature 2-5:** `one/things/features/2-5-auth-migration.md` (Auth UI)
- **Feature 2-6:** `one/things/features/2-6-dashboard-migration.md` (Dashboard UI)
- **Design System:** `frontend/src/styles/global.css` (Tailwind v4 config)
- **Components:** `frontend/src/components/ui/` (shadcn/ui components)
---
## Next Steps
### Immediate Actions
1. **Frontend Specialist:** Review this design specification
2. **Frontend Specialist:** Validate wireframes match existing UI
3. **Frontend Specialist:** Confirm design tokens are correct
4. **Frontend Specialist:** Begin implementation of Feature 2-4 hooks
### Implementation Sequence
**Week 1: Feature 2-4 (React Hooks)**
- Implement hook return types
- Implement error taxonomy
- Implement loading state patterns
- Add TypeScript IntelliSense support
- Write hook documentation
**Week 2: Feature 2-5 (Auth Components)**
- Migrate LoginForm with wireframe
- Migrate SignupForm with password strength
- Migrate OAuthButtons
- Migrate PasswordResetForm
- Migrate TwoFactorSettings with QR code
- Migrate MagicLinkForm
- Validate all auth tests pass
**Week 3: Feature 2-6 (Dashboard Components)**
- Migrate DashboardOverview with charts
- Migrate CourseList (list + grid views)
-