Built 2026-02-02 Draft

Section 1: Overview

1.1 What is Academy?

Status Defined

Vision Statement

For product designers and product managers who want to ship features using AI tools but lack structured learning resources,
Academy is an online learning platform that delivers practical, hands-on courses for AI-assisted development.
Unlike scattered blog posts and documentation, our product provides a structured path from beginner to shipping real code.

Essential Elements

Element Answer
Value Proposition Structured courses that take non-developers from zero to shipping features
Target Audience Technically curious designers and PMs with basic terminal/Git knowledge
Problem Context AI tools like Claude Code are powerful but learning resources are fragmented
Key Differentiator Hands-on, text-based learning path (not videos) specifically for non-developers

1.2 Problem Statement

Status Defined

Jobs-to-be-Done Format

When I want to ship a feature using AI tools like Claude Code,
but existing resources are scattered across blog posts, videos, and docs that assume too much technical knowledge,
help me follow a structured learning path with practical exercises,
so I can successfully build real products without becoming a full-time developer.

Problem Details

Question Answer
Who has this problem? Product designers and PMs who are technically curious
What is the problem? No structured path to learn AI-assisted development
How are they solving it today? Piecing together blog posts, YouTube videos, trial and error
Why is that inadequate? Wastes time, leads to frustration, incomplete understanding
What's the cost of not solving it? Designers/PMs give up or develop inefficient workflows

1.3 Success Metrics

Status Defined
Metric Baseline Target Measurement Method
Course completion rate N/A (new) >60% Users who finish all sessions / users who start
Time to first feature N/A <2 weeks Days from signup to first shipped feature (self-reported)
Comment engagement N/A >3 comments/session avg Total comments / total sessions
Return visits N/A >50% weekly return Users who return within 7 days

Guardrail Metrics (What Shouldn't Get Worse)

Metric Threshold
Page load time <2 seconds
Mobile usability score >90 (Lighthouse)

1.4 Scope Boundaries

Status Defined

✅ In Scope (v1)

  • Course catalog and detail pages
  • Session content (markdown/MDX rendering)
  • User authentication (email + OAuth)
  • Comments on sessions
  • Basic search
  • Responsive design

❌ Out of Scope (Explicitly NOT Building)

  • Video content — Text-based only for v1
  • Course marketplace — We create all content internally
  • Certificates/badges — No credentialing system
  • Progress tracking — No saved progress indicators (v1)
  • Live features — No real-time chat, video calls, or live classes
  • Mobile app — Web only, responsive design
  • Payment/subscriptions — Free access for v1
  • Instructor tools — Content managed via Git, not CMS

Section 2: Users & Personas

2.1 Primary Persona

Status Defined
Attribute Description
Role Senior Product Designer
Tech Level Expert product designer, low experience of development and AI

Section 3: User Flows

3.1 Core User Journey

Status Defined
┌─────────────────────────────────────────────────────────────────────────────┐
│                           LEARNER JOURNEY                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  1. DISCOVER          2. EXPLORE           3. LEARN            4. ENGAGE    │
│  ───────────          ──────────           ────────            ─────────    │
│  Land on home    →    Browse courses   →   Read session   →   Comment &    │
│  page                 View details         Follow along       discuss       │
│                                            with repo                         │
│                                                                              │
│  ┌──────────┐        ┌──────────┐        ┌──────────┐        ┌──────────┐  │
│  │   Home   │   →    │ Catalog  │   →    │ Session  │   →    │ Comments │  │
│  │   Page   │        │   Page   │        │   Page   │        │ Section  │  │
│  └──────────┘        └──────────┘        └──────────┘        └──────────┘  │
│                            ↓                                                 │
│                      ┌──────────┐                                           │
│                      │  Course  │                                           │
│                      │  Detail  │                                           │
│                      └──────────┘                                           │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

3.2 Key User Flows

Status Defined
Flow Description Pages Involved
Browse courses User explores available courses from catalog Home → Catalog → Course Detail
Take a session User reads session content and follows along Course Detail → Session
Search content User searches for specific topic Any page → Search results
Comment on session User asks question or shares insight Session → Comments
Sign in User authenticates to access features Any page → Auth → Return

Flow Detail: Browse Courses

1. User lands on Home page
2. User clicks "View Courses" or navigates to Catalog
3. User sees list of available courses with:
   - Course title
   - Description
   - Number of sessions
   - Estimated time
4. User clicks on a course
5. User sees Course Detail with:
   - Full description
   - Session list
   - Progress indicator (if signed in)
6. User clicks "Start Course" or selects a session

Flow Detail: Take a Session

1. User is on Course Detail page
2. User clicks on a session
3. Session page loads with:
   - Session title and metadata
   - Sidebar navigation (all sessions)
   - Breadcrumbs (Home > Course > Session)
   - Session content (rendered markdown)
   - Comments section at bottom
4. User reads content, follows along with repo
5. User can navigate to next/previous session
6. User can comment or ask questions

Section 4: Features & Requirements

4.1 Feature Overview

Status Defined
# Feature Status Description
F1 Home Page Academy overview and entry point
F2 Course Catalog List of all available courses
F3 Course Detail Course overview with session list
F4 Session Page Session content rendered from MDX
F5 Navigation Header, sidebar, and breadcrumbs
F6 User Authentication Sign in/sign up with Supabase Auth
F7 Comments Discussion on session pages
F8 Search Search across courses and sessions

Feature Details

Each feature has its own file in the features/ directory, following PRD best practices: - User stories in INVEST format - Requirements with MoSCoW priorities - Acceptance criteria in Given-When-Then format - Edge cases and error states documented

F1

Home Page

Route/
Auth RequiredYes
DependenciesF6

Description

Landing page that introduces Academy and directs users to courses. First impression for new visitors—must communicate value proposition within 5 seconds.

User Stories

As a... I want to... So that...
Visitor Understand what Academy offers I can decide if it's relevant to me
Visitor See featured courses I can quickly find popular content
Visitor Navigate to course catalog I can browse all available courses
Returning user Access courses quickly I don't waste time on marketing content

Requirements

ID Requirement
F1.1 Hero section with headline and tagline
F1.2 Primary CTA button ("Browse Courses" or "Get Started")
F1.3 Brief value proposition (2-3 bullet points)
F1.4 Featured courses section (2-3 course cards)
F1.5 Link to full course catalog
F1.6 Footer with basic links

UI Components

Component Description
Hero Full-width section with headline, subhead, CTA
CourseCard Reusable card showing course title, description, metadata
Footer Site-wide footer with links

Layout

┌─────────────────────────────────────────────────────────┐
│  Header (Logo, Nav, Sign In)                            │
├─────────────────────────────────────────────────────────┤
│                                                         │
│                      HERO SECTION                       │
│           "Learn to ship with AI"                       │
│     [Browse Courses]                                    │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│              FEATURED COURSES (3 cards)                 │
│   ┌─────────┐   ┌─────────┐   ┌─────────┐             │
│   │ Course  │   │ Course  │   │ Course  │             │
│   │   1     │   │   2     │   │   3     │             │
│   └─────────┘   └─────────┘   └─────────┘             │
│                                                         │
│              [View All Courses →]                       │
│                                                         │
├─────────────────────────────────────────────────────────┤
│  Footer                                                 │
└─────────────────────────────────────────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: Visitor understands Academy's value proposition
  Given I am a new visitor on the home page
  When the page loads
  Then I see a headline "Learn to ship with AI"
  And I see a tagline explaining the target audience
  And I see a primary CTA button

Scenario: Visitor navigates to course catalog
  Given I am on the home page
  When I click the "Browse Courses" button
  Then I am navigated to /courses

Scenario: Visitor clicks a featured course
  Given I am on the home page
  And there are featured courses displayed
  When I click on a course card
  Then I am navigated to /courses/[course-slug]

Scenario: Home page loads within performance budget
  Given I am accessing the home page
  When the page loads
  Then the Largest Contentful Paint is under 2 seconds
  And no layout shift occurs after initial render

Edge Cases & Error States

Scenario Expected Behavior
No featured courses exist Hide featured section, show only CTA to catalog
Images fail to load Show placeholder with course title
Slow network Show skeleton loading states

Technical Notes

  • Static page, can be fully server-rendered
  • Featured courses pulled from course metadata (marked as featured: true)

F2

Course Catalog

Status Defined
Dependencies None

Description

Page listing all available courses. Primary browse experience for discovering content.

User Stories

As a... I want to... So that...
Visitor See all available courses I can find one that interests me
Visitor See course metadata (duration, sessions) I can estimate time commitment
Visitor Click into a course I can learn more about it

Requirements

ID Requirement
F2.1 Display all published courses
F2.2 Course card with title, description
F2.3 Course card with session count
F2.4 Course card with estimated time
F2.5 Grid layout (responsive)
F2.6 Empty state if no courses

UI Components

Component Description
CourseCard Title, description, session count, time estimate, link
CourseGrid Responsive grid container
PageHeader "Courses" title

Layout

┌─────────────────────────────────────────────────────────┐
│  Header                                                 │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Courses                                                │
│  ─────────────────────────────                          │
│                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │   Course    │  │   Course    │  │   Course    │     │
│  │   Card 1    │  │   Card 2    │  │   Card 3    │     │
│  │             │  │             │  │             │     │
│  │ 12 sessions │  │ 8 sessions  │  │ 6 sessions  │     │
│  │ ~4 hours    │  │ ~3 hours    │  │ ~2 hours    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
│                                                         │
│  ┌─────────────┐  ┌─────────────┐                      │
│  │   Course    │  │   Course    │                      │
│  │   Card 4    │  │   Card 5    │                      │
│  └─────────────┘  └─────────────┘                      │
│                                                         │
├─────────────────────────────────────────────────────────┤
│  Footer                                                 │
└─────────────────────────────────────────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: Visitor sees all published courses
  Given there are 5 courses with status "published"
  And there are 2 courses with status "draft"
  When I visit /courses
  Then I see exactly 5 course cards
  And I do not see any draft courses

Scenario: Visitor views course metadata
  Given there is a course with 12 sessions and 4 hours total time
  When I view the course card
  Then I see "12 sessions"
  And I see "~4 hours"

Scenario: Visitor clicks a course card
  Given I am on the catalog page
  When I click on a course card for "The Shipping AI Designer"
  Then I am navigated to /courses/shipping-ai-designer

Scenario: Grid responds to screen size
  Given I am on the catalog page
  When my viewport is 1200px wide
  Then courses display in 3 columns
  When my viewport is 800px wide
  Then courses display in 2 columns
  When my viewport is 400px wide
  Then courses display in 1 column

Scenario: Empty state when no courses
  Given there are no published courses
  When I visit /courses
  Then I see a message "No courses available yet"
  And I see a link to go back home

Edge Cases & Error States

Scenario Expected Behavior
No published courses Show EmptyState component
Course missing required fields Skip that course, log warning
Very long course title Truncate with ellipsis after 2 lines

Technical Notes

  • Courses loaded from filesystem (/courses/*/course.json)
  • Only show courses with status: "published"

F3

Course Detail

Route/courses/[slug]
Auth RequiredYes
DependenciesF6

Description

Overview page for a single course showing full description and session list. Helps users understand the learning path before committing.

User Stories

ID As a... I want to... So that...
US3.1 Visitor Read the full course description I can decide if it's right for me
US3.2 Visitor See all sessions in order I understand the learning path
US3.3 Visitor Start the course easily I can begin learning immediately
US3.4 Visitor Jump to a specific session I can skip content I already know

Requirements

ID Requirement
F3.1 Course title
F3.2 Full course description (markdown rendered)
F3.3 Session list with titles in order
F3.4 Session descriptions (truncated)
F3.5 Estimated time per session
F3.6 "Start Course" CTA linking to first session
F3.7 Total course duration display
F3.8 Breadcrumbs: Home > Courses > [Course]

UI Components

Component Props Description
CourseHeader title, description, sessionCount, totalTime Course overview
SessionList sessions[] Ordered session list
SessionItem number, title, description, time, href Single session row
Breadcrumbs items[] Navigation trail

Layout

┌─────────────────────────────────────────────────────────┐
│  Header                                                 │
├─────────────────────────────────────────────────────────┤
│  Home > Courses > Course Name                           │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Course Title                                           │
│  ══════════════════════════════════════                 │
│                                                         │
│  Full course description goes here. Can be              │
│  multiple paragraphs with markdown formatting.          │
│                                                         │
│  12 sessions · ~4 hours total                           │
│                                                         │
│  [Start Course →]                                       │
│                                                         │
├─────────────────────────────────────────────────────────┤
│  Sessions                                               │
│  ─────────                                              │
│                                                         │
│  1. Introduction to AI-Assisted Development    ~20 min │
│     Get started with the basics...                      │
│                                                         │
│  2. Setting Up Your Environment                ~30 min │
│     Install and configure the tools...                  │
│                                                         │
│  3. Your First Feature                         ~45 min │
│     Build something real...                             │
├─────────────────────────────────────────────────────────┤
│  Footer                                                 │
└─────────────────────────────────────────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: Visitor views course details
  Given there is a course with slug "shipping-ai-designer"
  When I visit /courses/shipping-ai-designer
  Then I see the course title
  And I see the full course description rendered from markdown
  And I see the total session count and duration

Scenario: Visitor sees session list
  Given the course has 12 sessions
  When I view the course detail page
  Then I see all 12 sessions listed in order
  And each session shows its title and estimated time

Scenario: Visitor starts the course
  Given I am on a course detail page
  And the first session has slug "introduction"
  When I click "Start Course"
  Then I am navigated to /courses/[slug]/introduction

Scenario: Visitor jumps to specific session
  Given I am on a course detail page
  When I click on "Session 5: Advanced Patterns"
  Then I am navigated to /courses/[slug]/advanced-patterns

Scenario: Breadcrumb navigation works
  Given I am on the course detail page for "The Shipping AI Designer"
  When I click "Courses" in the breadcrumbs
  Then I am navigated to /courses

Scenario: Invalid course slug returns 404
  Given there is no course with slug "nonexistent-course"
  When I visit /courses/nonexistent-course
  Then I see a 404 error page

Edge Cases & Error States

Scenario Expected Behavior
Course has no sessions Show message "Sessions coming soon"
Invalid slug Return 404 page
Session missing time estimate Show "—" instead of time
Very long description Render full markdown, no truncation

Technical Notes

  • Dynamic route: /courses/[slug]
  • Data from course.json + session MDX frontmatter
  • Use generateStaticParams for static generation

F4

Session Page

Route/courses/[slug]/[session]
Auth RequiredYes
DependenciesF5 (Navigation), F7 (Comments)

Description

The core learning experience. Displays session content rendered from MDX with sidebar navigation and comments. This is where users spend most of their time—must be readable, navigable, and engaging.

User Stories

ID As a... I want to... So that...
US4.1 Learner Read well-formatted content I can learn the material clearly
US4.2 Learner See code with syntax highlighting I can understand and copy examples
US4.3 Learner Navigate to next/previous session I can progress through the course
US4.4 Learner See my position in the course I know how much is left
US4.5 Learner Ask questions via comments I can get help when stuck

Requirements

ID Requirement
Content
F4.1 Session title
F4.2 Reading time estimate
F4.3 Last updated date
Markdown Rendering
F4.4 Headings (h1-h6) with anchor links
F4.5 Paragraphs, bold, italic, strikethrough
F4.6 Bullet and numbered lists
F4.7 Code blocks with syntax highlighting
F4.8 Code blocks with copy button
F4.9 Inline code styling
F4.10 Images with alt text
F4.11 Links (internal and external, external open new tab)
F4.12 Callout boxes: <Tip>, <Warning>, <Info>
F4.13 Tables with responsive scroll
Navigation
F4.14 Previous session link (hide on first session)
F4.15 Next session link (or "Complete" on last)
F4.16 Sidebar with all sessions, current highlighted
F4.17 Breadcrumbs: Home > Course > Session
Integration
F4.18 Comments section at bottom

UI Components

Component Description
SessionHeader Title, metadata (time, date)
MDXContent Rendered markdown content
CodeBlock Syntax-highlighted code with copy button
Callout Styled tip/warning/info boxes
SessionNav Previous/Next navigation
CommentSection Comments list and form (F7)

Layout

┌─────────────────────────────────────────────────────────────────┐
│  Header                                                         │
├─────────────────────────────────────────────────────────────────┤
│  Home > Course > Session                                        │
├────────────────┬────────────────────────────────────────────────┤
│                │                                                │
│   SIDEBAR      │   Session Title                                │
│   ─────────    │   ════════════════════════════                 │
│                │   15 min read · Updated Jan 16                 │
│   1. Intro  ●  │                                                │
│   2. Setup     │   Content goes here. This is the main          │
│   3. First     │   learning content rendered from markdown.     │
│   4. [Current] │                                                │
│   5. Testing   │   ## Heading                                   │
│   6. Deploy    │                                                │
│   ...          │   Paragraph text with **bold** and *italic*.   │
│                │                                                │
│                │   ```javascript                                │
│                │   const example = "code";                      │
│                │   ```                                          │
│                │                                                │
│                │   ┌─────────────────────────────────┐          │
│                │   │ 💡 TIP: This is a callout box  │          │
│                │   └─────────────────────────────────┘          │
│                │                                                │
│                │   [← Previous]              [Next →]           │
│                │                                                │
│                ├────────────────────────────────────────────────┤
│                │                                                │
│                │   Comments (3)                                 │
│                │   ────────────                                 │
│                │                                                │
│                │   [Comment form - requires sign in]            │
│                │                                                │
│                │   ┌─────────────────────────────────┐          │
│                │   │ User1 · 2 hours ago             │          │
│                │   │ Great explanation!              │          │
│                │   └─────────────────────────────────┘          │
│                │                                                │
├────────────────┴────────────────────────────────────────────────┤
│  Footer                                                         │
└─────────────────────────────────────────────────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: Learner views session content
  Given I am on session "setting-up-environment" of course "shipping-ai-designer"
  When the page loads
  Then I see the session title "Setting Up Your Environment"
  And I see the reading time estimate
  And I see the session content rendered from MDX

Scenario: Code blocks render with highlighting
  Given the session contains a JavaScript code block
  When I view the session
  Then the code block has syntax highlighting
  And I see a "Copy" button on the code block
  When I click the "Copy" button
  Then the code is copied to my clipboard
  And the button shows "Copied!" temporarily

Scenario: Callout boxes render correctly
  Given the session contains a <Tip> component
  When I view the session
  Then I see a styled callout box with a lightbulb icon
  And the callout has a distinct background color (blue)

Scenario: Learner navigates to next session
  Given I am on session 3 of 12
  When I click "Next" at the bottom
  Then I am navigated to session 4
  And the sidebar updates to highlight session 4

Scenario: Learner on first session
  Given I am on session 1 of the course
  When I view the navigation
  Then I do not see a "Previous" button
  And I see a "Next" button

Scenario: Learner on last session
  Given I am on session 12 (last session)
  When I view the navigation
  Then I see a "Previous" button
  And I see a "Complete Course" button instead of "Next"

Scenario: Sidebar shows course progress
  Given I am on session 5 of a 12-session course
  When I view the sidebar
  Then I see all 12 sessions listed
  And session 5 is visually highlighted as current

Scenario: Mobile sidebar behavior
  Given I am viewing on a mobile device (< 768px)
  When the page loads
  Then the sidebar is hidden by default
  When I tap the menu toggle button
  Then the sidebar slides in as an overlay

Edge Cases & Error States

Scenario Expected Behavior
Invalid session slug Return 404 page
MDX syntax error Show error boundary, log to console
Image fails to load Show alt text in placeholder
Very long code block Horizontal scroll within code block
External link Opens in new tab with rel="noopener"

Technical Notes

  • Route: /courses/[slug]/[session]
  • Content from MDX files in /courses/[slug]/session-*.mdx
  • Use next-mdx-remote for MDX rendering
  • Syntax highlighting: shiki (better) or rehype-prism
  • Reading time: calculated from word count (~200 words/min)
  • Copy button: use navigator.clipboard.writeText()

F5

Navigation

ScopeSite-wide component
Auth RequiredYes
DependenciesF6 (for user menu)

Description

Site-wide navigation including header, sidebar (on session pages), and breadcrumbs. Enables users to move around the site and understand their location.

User Stories

ID As a... I want to... So that...
US5.1 User See where I am on the site I don't get lost
US5.2 User Navigate to any main section I can move around easily
US5.3 Learner See my position in a course I know how much is left
US5.4 User Access my account from anywhere I can sign in/out easily

Requirements

ID Requirement
Header
F5.1 Logo linking to home
F5.2 "Courses" link to catalog
F5.3 Search input (see F8)
F5.4 "Sign In" button (logged out)
F5.5 User avatar dropdown (logged in)
F5.6 Mobile hamburger menu (<768px)
Breadcrumbs
F5.7 Show on all pages except home
F5.8 Format: Home > Course > Session
F5.9 All segments are clickable links
Sidebar
F5.10 Show on session pages only
F5.11 List all sessions in order
F5.12 Visually highlight current session
F5.13 Collapse to toggle on mobile

UI Components

Component Props Description
Header user? Logo, nav, search, user menu
MobileMenu isOpen, links[] Hamburger slide-out menu
Breadcrumbs segments[] Navigation trail
Sidebar sessions[], currentSlug Session list
UserMenu user Avatar with dropdown

Header Layout

Desktop (>768px):
┌─────────────────────────────────────────────────────────┐
│  🎓 Academy          Courses    [🔍 Search]   [Sign In] │
└─────────────────────────────────────────────────────────┘

Logged in:
┌─────────────────────────────────────────────────────────┐
│  🎓 Academy          Courses    [🔍 Search]     [👤▾]   │
└─────────────────────────────────────────────────────────┘

Mobile (<768px):
┌─────────────────────────────────────────────────────────┐
│  [☰]    🎓 Academy                         [Sign In]   │
└─────────────────────────────────────────────────────────┘
Home  >  Courses  >  The Shipping AI Designer  >  Session 3
[link]   [link]      [link]                       [current]
┌──────────────────────┐
│  Sessions            │
│  ─────────           │
│  ○ 1. Introduction   │
│  ○ 2. Setup          │
│  ● 3. First Feature  │  ← Current (highlighted)
│  ○ 4. Testing        │
│  ○ 5. Deployment     │
└──────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: Header displays on all pages
  Given I am on any page of the site
  When the page loads
  Then I see the header with logo, nav, and user area

Scenario: Logo navigates to home
  Given I am on the course catalog page
  When I click the Academy logo
  Then I am navigated to the home page (/)

Scenario: User menu shows sign in when logged out
  Given I am not signed in
  When I view the header
  Then I see a "Sign In" button
  And I do not see a user avatar

Scenario: User menu shows avatar when logged in
  Given I am signed in as "alex@example.com"
  When I view the header
  Then I see my avatar
  When I click my avatar
  Then I see a dropdown with "Profile" and "Sign Out"

Scenario: Breadcrumbs show correct path
  Given I am on session "first-feature" of course "shipping-ai-designer"
  When I view the breadcrumbs
  Then I see "Home > Courses > The Shipping AI Designer > First Feature"
  And "Home", "Courses", and course name are clickable links

Scenario: Sidebar highlights current session
  Given I am on session 3 of a 5-session course
  When I view the sidebar
  Then I see all 5 sessions listed
  And session 3 is visually highlighted (filled circle, bold text)

Scenario: Mobile hamburger menu
  Given I am on a mobile device (<768px)
  When I tap the hamburger icon (☰)
  Then a slide-out menu appears with navigation links
  When I tap "Courses"
  Then the menu closes and I navigate to /courses

Edge Cases & Error States

Scenario Expected Behavior
Very long course name Truncate in breadcrumbs with ellipsis
20+ sessions in sidebar Scrollable sidebar, sticky header
User avatar fails to load Show initials fallback

Technical Notes

  • Header: Use <header> in root layout
  • Breadcrumbs: Generate from usePathname() + data lookup
  • Sidebar: Pass sessions as prop from page data
  • Mobile menu: Use Radix or Headless UI for accessibility

F6

User Authentication

Routes/sign-in, /sign-up, /profile
Auth ProviderSupabase Auth (Google OAuth)
DependenciesNone

Description

User sign up, sign in, and session management. Required for commenting and future features like progress tracking.

User Stories

ID As a... I want to... So that...
US6.1 Visitor Create an account I can access member features
US6.2 Visitor Sign in with email I can access my account
US6.3 Visitor Sign in with GitHub I can use my existing account
US6.4 User Stay signed in I don't have to log in every visit
US6.5 User Sign out I can secure my session
US6.6 User Reset my password I can recover my account

Requirements

ID Requirement
F6.1 Sign up with email/password
F6.2 Email verification
F6.3 Sign in with email/password
F6.4 Sign in with GitHub OAuth
F6.5 Sign in with Google OAuth
F6.6 Password reset via email
F6.7 Session persistence (30 days)
F6.8 Sign out
F6.9 Protected routes redirect to sign-in
F6.10 Return to previous page after sign-in

Auth Flows

Sign Up:

1. User clicks "Sign Up"
2. User enters email and password (or clicks OAuth)
3. User verifies email (if required)
4. User is signed in and redirected

Sign In:

1. User clicks "Sign In"
2. User enters email/password or clicks OAuth
3. User is signed in and redirected to previous page

Password Reset:

1. User clicks "Forgot Password"
2. User enters email
3. User receives reset link via email
4. User clicks link and sets new password
5. User is signed in

UI Components

Component Props Description
Sign In Page /sign-in Supabase Auth UI or custom form
Sign Up Page /sign-up Supabase Auth UI or custom form
<UserButton> afterSignOutUrl Avatar with dropdown

Acceptance Criteria (Given-When-Then)

Scenario: User signs up with email
  Given I am on the sign-up page
  When I enter a valid email and password
  And I click "Sign Up"
  Then my account is created
  And I am redirected to the home page
  And I see my avatar in the header

Scenario: User signs in with GitHub
  Given I am on the sign-in page
  When I click "Continue with GitHub"
  And I authorize the application on GitHub
  Then I am signed in
  And I am redirected to my previous page

Scenario: User signs out
  Given I am signed in
  When I click my avatar in the header
  And I click "Sign Out"
  Then I am signed out
  And I see "Sign In" button in the header

Scenario: Password reset flow
  Given I am on the sign-in page
  When I click "Forgot Password"
  And I enter my registered email
  And I click "Send Reset Link"
  Then I see a confirmation message
  And I receive an email with a reset link

Scenario: Protected route redirects
  Given I am not signed in
  When I try to access /profile
  Then I am redirected to /sign-in
  And after signing in, I am redirected to /profile

Scenario: Session persists across visits
  Given I signed in yesterday
  When I return to the site today
  Then I am still signed in
  And I see my avatar in the header

Edge Cases & Error States

Scenario Expected Behavior
Invalid email format Show inline validation error
Wrong password Show "Invalid credentials" message
Email already registered Show "Email already in use"
OAuth cancelled Return to sign-in page
Reset link expired Show error with option to request new link

Technical Notes

  • Using Supabase Auth (Google OAuth)
  • Supabase Auth with @supabase/ssr for Next.js
  • Middleware for protected routes in middleware.ts
  • User ID from Supabase Auth for comments relation

F7

Comments

LocationBottom of session pages
Auth RequiredYes
DependenciesF6 (Authentication)

Description

Discussion section on session pages for questions, answers, and community engagement. Enables learners to help each other and builds community.

User Stories

ID As a... I want to... So that...
US7.1 Learner Ask a question about content I can get help when stuck
US7.2 Learner Read others' questions I can learn from their answers
US7.3 Learner Reply to a comment I can help others or discuss
US7.4 Visitor Read comments without signing in I can see community activity

Requirements

ID Requirement
F7.1 Display comments below session content
F7.2 Show comment count in section header
F7.3 Show commenter name and avatar
F7.4 Show relative timestamp ("2 hours ago")
F7.5 Comment form for signed-in users
F7.6 "Sign in to comment" prompt for visitors
F7.7 Basic markdown in comments (bold, italic, code, links)
F7.8 Reply to comments (1 level nesting max)

UI Components

Component Props Description
CommentSection sessionId, comments[] Container with count, form, list
CommentForm onSubmit, placeholder Textarea and submit button
Comment author, avatar, content, timestamp, replies[] Single comment
CommentReply author, avatar, content, timestamp Nested reply (indented)
SignInPrompt redirectUrl "Sign in to comment" CTA

Layout

┌─────────────────────────────────────────────────────────┐
│  Comments (5)                                           │
│  ───────────                                            │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │  Write a comment...                              │   │
│  │                                                  │   │
│  │                                    [Post]        │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ 👤 Alex · 2 hours ago                           │   │
│  │                                                  │   │
│  │ Great explanation! How does this work with TS?  │   │
│  │                                                  │   │
│  │ [Reply]                                          │   │
│  │                                                  │   │
│  │   ┌─────────────────────────────────────────┐   │   │
│  │   │ 👤 Sam · 1 hour ago                     │   │   │
│  │   │ Good question! You would need to...     │   │   │
│  │   └─────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

Data Model

interface Comment {
  id: string;
  content: string;        // Markdown
  user_id: string;        // → User (Supabase Auth)
  sessionId: string;      // → Session
  parentId: string | null; // → Comment (for replies)
  createdAt: Date;
}

Acceptance Criteria (Given-When-Then)

Scenario: Signed-in user posts a comment
  Given I am signed in
  And I am on a session page
  When I type "Great explanation!" in the comment form
  And I click "Post"
  Then my comment appears at the top of the list
  And I see my avatar and name
  And I see "just now" as the timestamp

Scenario: Visitor sees sign-in prompt
  Given I am not signed in
  And I am on a session page
  When I view the comments section
  Then I see existing comments
  And I see "Sign in to comment" instead of the form
  When I click "Sign in to comment"
  Then I am redirected to /sign-in

Scenario: User replies to a comment
  Given I am signed in
  And there is an existing comment
  When I click "Reply" on that comment
  And I type my reply
  And I click "Post"
  Then my reply appears nested under the original comment
  And my reply is visually indented

Scenario: Comment renders markdown
  Given I post a comment with "Use `const` instead of **let**"
  When the comment is displayed
  Then "const" appears in code formatting
  And "let" appears in bold formatting

Scenario: Comments load on session page
  Given a session has 5 comments
  When I visit that session page
  Then I see "Comments (5)" as the section header
  And I see all 5 comments in reverse chronological order

Edge Cases & Error States

Scenario Expected Behavior
Empty comment submitted Show validation error, disable submit
Comment too long (>2000 chars) Show character count, disable submit
Network error on post Show error toast, keep content in form
User deleted their account Show "Deleted User" for their comments
Malicious HTML in comment Sanitize, only allow safe markdown

Technical Notes

  • Comments stored in Supabase PostgreSQL
  • Use Server Actions for posting (no API route needed)
  • Optimistic UI: show comment immediately, rollback on error
  • Rate limiting: max 10 comments per minute per user
  • Sanitize markdown with rehype-sanitize

F8

Search

Route/search?q=[query]
Auth RequiredYes
DependenciesF6

Description

Full-text search across courses and session content. Allows users to quickly find relevant content without browsing.

User Stories

ID As a... I want to... So that...
US8.1 Learner Search for a topic I can find relevant content quickly
US8.2 Learner See context in results I know which result is most relevant
US8.3 Visitor Search before signing up I can verify the content I need exists

Requirements

ID Requirement
F8.1 Search input in header (all pages)
F8.2 Search results page at /search
F8.3 Index course titles and descriptions
F8.4 Index session titles and descriptions
F8.5 Index session content (body text)
F8.6 Highlight matching text in snippets
F8.7 Link to course/session from each result
F8.8 "No results" empty state
F8.9 Debounced input (300ms)

UI Components

Component Props Description
SearchInput placeholder, onSubmit Input with search icon in header
SearchResults query, results[] Results list container
SearchResult title, snippet, href, type Single result card
NoResults query Empty state with suggestions

Layout

┌─────────────────────────────────────────────────────────┐
│  Header with [🔍 Search input]                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Search results for "claude code"                       │
│  ─────────────────────────────────                      │
│  12 results                                             │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ 📘 The Shipping AI Designer            [Course] │   │
│  │ Learn to ship features using **Claude Code**... │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ 📄 Session 3: Your First Feature      [Session] │   │
│  │ The Shipping AI Designer                         │   │
│  │ ...use **Claude Code** to implement the...      │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
│  ┌─────────────────────────────────────────────────┐   │
│  │ 📄 Session 5: Advanced Patterns       [Session] │   │
│  │ The Shipping AI Designer                         │   │
│  │ ...advanced **Claude Code** workflows...        │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
└─────────────────────────────────────────────────────────┘

Acceptance Criteria (Given-When-Then)

Scenario: User searches from header
  Given I am on any page
  When I type "claude code" in the search input
  And I press Enter
  Then I am navigated to /search?q=claude%20code
  And I see results matching "claude code"

Scenario: Search results display correctly
  Given there are courses and sessions containing "typescript"
  When I search for "typescript"
  Then I see matching courses with a "Course" badge
  And I see matching sessions with a "Session" badge
  And each result shows a highlighted snippet

Scenario: User clicks a search result
  Given I have search results displayed
  When I click on a session result
  Then I am navigated to that session page

Scenario: No results found
  Given there is no content matching "xyznonexistent"
  When I search for "xyznonexistent"
  Then I see "No results found for 'xyznonexistent'"
  And I see suggestions like "Try different keywords"

Scenario: Search is performant
  Given the search index contains 100 sessions
  When I search for "react"
  Then results appear in under 500ms

Scenario: Empty search query
  Given I am on the search page
  When I submit an empty search query
  Then I see a prompt to enter a search term
  And no results are displayed

Edge Cases & Error States

Scenario Expected Behavior
Very long query (>200 chars) Truncate to 200 chars
Special characters in query Escape/sanitize for search
Search service unavailable Show friendly error, suggest browsing
Query with only stopwords Show "Try more specific terms"

Technical Notes

Search Provider Comparison:

Provider Pros Cons Recommendation
PostgreSQL FTS Built-in, no extra cost Basic ranking, no typo tolerance ✅ Start here
Meilisearch Fast, typo-tolerant, self-hosted Extra service to maintain Upgrade path
Algolia Excellent UX, fully managed Costs at scale If budget allows

Implementation (PostgreSQL):

-- Add search vector column
ALTER TABLE sessions ADD COLUMN search_vector tsvector;

-- Create index
CREATE INDEX sessions_search_idx ON sessions USING GIN(search_vector);

-- Search query
SELECT * FROM sessions 
WHERE search_vector @@ plainto_tsquery('english', $1)
ORDER BY ts_rank(search_vector, plainto_tsquery('english', $1)) DESC;

Recommendation: Start with PostgreSQL full-text search. Migrate to Meilisearch if search quality becomes a user complaint.


Section 5: Information Architecture

5.1 Navigation Structure

Status Defined
Academy
├── Home (/)
├── Courses (/courses)
│   └── [Course] (/courses/[slug])
│       └── [Session] (/courses/[slug]/[session])
├── Search (/search)
├── Sign In (/sign-in)
├── Sign Up (/sign-up)
└── Profile (/profile)

5.2 Page Inventory

Status Defined
Page Path Auth Required Description
Home / Yes Landing page with featured courses
Course Catalog /courses Yes Browse all courses
Course Detail /courses/[slug] Yes Course overview and session list
Session /courses/[slug]/[session] Yes Session content with comments
Search /search Yes Search results
Sign In /sign-in No Authentication
Sign Up /sign-up No Registration
Profile /profile Yes User settings

Auth Rule: All pages require authentication except /sign-in and /sign-up.

5.3 Content Model

Status Defined

Course

Field Type Required Description
id string Yes Unique identifier
slug string Yes URL-friendly identifier
title string Yes Course title
description string Yes Short description (for cards)
longDescription markdown No Full description (for detail page)
sessions Session[] Yes Ordered list of sessions
estimatedTime string No e.g., "4 hours"
status enum Yes draft, published

Session

Field Type Required Description
id string Yes Unique identifier
slug string Yes URL-friendly identifier
title string Yes Session title
content markdown Yes Session content (from .md file)
order number Yes Position in course
estimatedTime string No e.g., "30 min"
courseId string Yes Parent course

User

Field Type Required Description
id uuid Yes Supabase auth user ID
email string Yes Email address
display_name string No Display name
avatar_url string No Profile image URL

Comment

Field Type Required Description
id uuid Yes Unique identifier
content string Yes Comment text (markdown supported)
user_id uuid Yes Author (FK to User)
session_id string Yes Parent session
parent_id uuid No Parent comment (for replies, 1 level only)
created_at datetime Yes When posted

5.4 Data Relationships

Course (1) ──────< Session (N)
                       │
                       └────< Comment (N) >──── User (1)
                                  │
                                  └── parent_id (self-reference, 1 level)
  • Course has many Sessions (1:N)
  • Session has many Comments (1:N)
  • User has many Comments (1:N)
  • Comment can have child Comments (1 level only)

Note: Course and Session content is stored in Markdown files (/courses), not in the database. Only User and Comment data is in Supabase.

Section 6: User Experience

6.1 Branding

Status Partial
Element Value
Brand Name Academy (by Samsen Studio)
Tagline "Learn to ship with AI"
Logo TBD
Voice Friendly, practical, encouraging
Personality Expert friend who explains things clearly

6.2 Key Screens

Status Defined
Screen Purpose Key Elements
Home Entry point, explain value Hero, featured courses, CTA
Catalog Browse courses Course cards grid
Course Detail Course overview Description, session list
Session Learn content Sidebar, content, comments

6.3 Design System

Status Partial

Spacing

Using Tailwind default spacing scale (4px base).

Components

Using shadcn/ui as base, customized to brand: - Button - Card - Input - Navigation Menu - Breadcrumb - Avatar - Badge


6.4 Responsive Design

Status Defined

All pages and components must adapt gracefully to different screen sizes using a mobile-first approach.

Breakpoints

Name Width Tailwind
Mobile <768px Default
Tablet 768-1024px md:
Desktop >1024px lg:

Requirements

ID Requirement
R7.1 Mobile-first CSS approach
R7.2 No horizontal scroll on any page
R7.3 Touch targets minimum 44×44px
R7.4 Body font size minimum 16px
R7.5 Images scale with container
R7.6 Code blocks horizontal scroll

Component Behaviors

Component Mobile (<768px) Tablet (768-1024px) Desktop (>1024px)
Header Hamburger menu Full nav Full nav
Course Grid 1 column 2 columns 3 columns
Session Sidebar Hidden (toggle) Visible (200px) Visible (260px)
Content Width 100% - padding Max 720px centered Max 720px centered

Mobile Layout (Session Page)

┌─────────────────────┐
│ ☰  Academy  [Sign]  │  ← Compact header (56px)
├─────────────────────┤
│ Home > Course > ... │  ← Breadcrumbs (truncated)
├─────────────────────┤
│                     │
│   Session Title     │
│   ═══════════════   │
│                     │
│   Content flows     │
│   full width with   │
│   16px padding.     │
│                     │
│   ┌───────────────┐ │
│   │ code block    │→│  ← Horizontal scroll
│   └───────────────┘ │
│                     │
│ [← Prev]   [Next →] │
│                     │
├─────────────────────┤
│ [≡ Show Sessions]   │  ← Toggle sidebar
├─────────────────────┤
│   Comments...       │
└─────────────────────┘

Acceptance Criteria

Scenario: Mobile layout renders correctly
  Given I am viewing on a 375px wide screen
  When I visit any page
  Then content fits within the viewport
  And there is no horizontal scrollbar
  And text is readable without zooming

Scenario: Course grid adapts to screen size
  Given there are 6 courses in the catalog
  When I view on mobile (<768px)
  Then courses display in 1 column
  When I view on tablet (768-1024px)
  Then courses display in 2 columns
  When I view on desktop (>1024px)
  Then courses display in 3 columns

Scenario: Touch targets are accessible
  Given I am on a touch device
  When I try to tap any interactive element
  Then the tap target is at least 44×44 pixels

Technical Notes

  • Use Tailwind responsive prefixes: sm: (640px), md: (768px), lg: (1024px)
  • Test on real devices, not just Chrome DevTools
  • Lighthouse mobile score target: >90
  • Core Web Vitals: LCP <2.5s, CLS <0.1