UNPKG

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
--- 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) -