Focus: Full-Stack Design & Engineering · Learning Platform · 0→1 Role: Sole designer and engineer (end-to-end) Timeline: 2025 Live: partner-certification.vercel.app · github.com/drudog/partner-certification Stack: Next.js 16 · TypeScript · Tailwind CSS · shadcn/ui · Radix UI
Executive Summary
I designed and built a full-stack partner certification platform that delivers structured training across three progressive tiers. Partners read lessons, watch embedded videos, check understanding with inline quizzes, and prove mastery through a gated final assessment, with no backend required. Progress is persisted entirely in localStorage through a pure-function library I wrote to manage all state.
The platform gives Beacon's partner program a single destination that mirrors a real certification experience: read, learn, prove it.
3
Progressive certification tiers: Certified, Gold, Platinum
0
Backend required: all state via localStorage pure-function library
6
Content types per lesson: prose, video, carousels, resources, quizzes, assessments
The Problem
Partner programs typically sprawl across PDFs, slide decks, and scattered wikis. There's no structured learning path, no way to verify knowledge, and no sense of progression. This app gives partners a single destination with a real certification experience, one that enforces sequential mastery rather than letting partners skip to the end.
- No structured path existed for partner onboarding
- Knowledge couldn't be verified; completion was self-reported
- Content scattered across PDFs and slides with no progression logic
- Partners had no sense of achievement or certification to point to
The Strategy
- Mirror a real certification experience: three tiered levels (Certified → Gold → Platinum) with progressive unlock gates enforced at the route level
- Enforce sequential mastery: lessons unlock in order; assessments lock until every preceding lesson is complete
- No backend dependency: ship fast, stay maintainable; all progress state in a pure-function localStorage library
- Rich content without JavaScript bloat: Markdown prose processed server-side via
remark+remark-html; no JS shipped for reading
Key Design Decisions
Progressive tier gating
Each certification tier is locked until the previous one is fully completed and its final assessment passed. This is enforced at the route level via a client-side access guard that silently redirects if a partner tries to skip ahead; the lock isn't just visual, it's structural.
Linear lesson progression
The app computes a flat ordered list of all lessons across all modules and checks whether every preceding lesson is marked complete before granting access. If not, the user is redirected to the first incomplete lesson. Partners can't self-report completion or navigate out of order.
State without a backend
All progress lives in localStorage under a single JSON key. A pure-function library (lib/progress.ts) handles every read, write, and derived state: getLevelSummary, isLevelUnlocked, getNextLesson. A useProgress hook wraps this in useState + useEffect for persistence on every change. Zero API routes, zero database.
Server-side content pipeline
Lesson content is written in Markdown and processed at request time through a remark → rehype → remark-html pipeline. HTML is injected via dangerouslySetInnerHTML on a server component, with no JavaScript shipped for prose rendering. This replaced next-mdx-remote which bundled an incompatible React 18 copy against Next.js 16's React 19.
shadcn/ui design system
The full component set (Button, Card, Badge, Progress) is built on Radix UI primitives through shadcn/ui, styled with Tailwind CSS custom properties mapped to the Beacon design palette. The CSS variable layer (--primary, --border, --radius) keeps the design system coherent while ensuring every component is accessible out of the box.
Key Screens






Challenges Solved
Next.js 15+ async params
All dynamic route params became a Promise in Next.js 15. Server components now await params; client components use React.use(params). Missing this causes all dynamic routes to 404 silently, a breaking change with no obvious error message.
React version conflict
next-mdx-remote v6 bundled its own React 18 copy, throwing "React Element from an older version of React" errors against Next.js 16's React 19. Replaced the entire MDX pipeline with a lightweight remark + remark-html chain, with no JSX emitted, no version conflict, smaller bundle.
Reserved variable naming
Next.js's ESLint config treats module as a reserved identifier (@next/next/no-assign-module-variable). Renamed all destructured course module variables to mod to unblock Vercel's CI pipeline.
Outcomes & Impact
- Delivered a fully functional three-tier certification platform with zero backend infrastructure
- Partners can progress through structured learning paths with real mastery gates, not self-reported completion
- Content authors add new lessons by writing a Markdown file; no code changes required
- Deployed to Vercel; live at partner-certification.vercel.app
Leadership
- End-to-end ownership: architecture decisions, design system, engineering, and deployment
- Identified and resolved three non-obvious framework-level breaking changes (async params, React version conflict, ESLint peer deps)
- Designed for content-author handoff; adding lessons requires no engineering involvement
- Built a stateful learning experience with zero backend cost or operational overhead