100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Tailwind CSS & Modern CSS
80 minbeginner

Capstone — Full Marketing Site with Design System

This is the course capstone: you will build a complete marketing website for a cricket statistics product, backed by a production-grade design system — the full combination of every skill across all six modules. The site has a sticky nav, a hero with fluid typography, a responsive feature grid with animated cards, a pricing section, a form, a dark-mode toggle, and a footer, all driven by a tiered token system that supports three themes and passes accessibility checks throughout.

The design system behind it is the real deliverable: a token architecture, a component library, accessible interaction patterns, production build optimisation, responsive engineering at every breakpoint and container, and conventions that would hold up under team maintenance. The site is what users see; the design system is what a professional team would ship and maintain.

There is no single correct implementation. The rubric measures architecture quality — are tokens tiered, are components semantic, are states accessible, does it re-theme cleanly — rather than pixel perfection. The goal is a codebase a hiring manager could open and recognise as the work of someone who understands Tailwind at a senior level, not just at a user level.

Analogy🏏Cricket
🏏 Think of it like cricket: A design system is like assembling not just talented individuals but a genuine team — players who share a common language, agreed signals, and a unified game plan so they function as one unit rather than eleven soloists. Just as a true team is defined by how well its members combine, not just their individual skill, a design system is defined by how well its components combine, not just their individual polish. Just as the shared playbook and signals are what turn individuals into a team, the shared tokens and variant conventions are what turn components into a system. The insight is that the value is in the coherence of the whole — a championship team and a real design system both succeed through shared language and combination, not isolated brilliance.

Learning Objectives

  • Apply the full token architecture (primitive, semantic, component tiers) with at least two brand themes plus dark mode, all switching without component-code changes.
  • Build a complete page section set (nav, hero, features, pricing, form, footer) that is mobile-first, responsive at every viewport from 360px to 1440px, and fluid between breakpoints with clamp().
  • Animate the interface appropriately — entrance animations, hover states, microinteractions — using compositor-friendly properties, with full motion-reduce coverage and no jank under CPU throttle.
  • Meet WCAG AA accessibility requirements throughout: focus-visible rings on every interactive element, sufficient contrast in every theme and mode, screen-reader labels, and status not reliant on color alone.
  • Package the component set as a small library (Button, Card, Input, Badge, StatTile, Nav, Hero, FeatureCard) with clean variant APIs, accessible defaults, and override-safe className merging.
  • Demonstrate production readiness: accurate content globs, a gzipped build under 20KB, no raw-color token leaks, prettier and eslint tooling enforced, and a Lighthouse accessibility score above 90.

Architecture

The architecture mirrors the course's progression: tokens at the bottom, components in the middle, pages at the top. Tokens live in two CSS files (primitives and themes), the Tailwind config maps semantic tokens to utilities, and the component library is a set of React components that speak only in semantic tokens and expose clean variant APIs. The pages are assembled from those components, with no inline styling that bypasses the token system.

The design system's correctness is verifiable: switch the root class to a brand theme and nothing changes in the component files — every pixel re-themes automatically because every component references semantics. Run the lint rule and zero raw-color violations appear. Tab through the page and every interactive element has a visible ring. Run the production build and the gzipped stylesheet is small. These are the tests that prove the architecture works, not just that the pixels look right.

The responsive architecture uses the two-level approach: viewport breakpoints for page-level structure (the navigation collapses to hamburger at md, the feature grid goes from 1 to 2 to 3 columns from sm to lg) and container queries for components (feature cards adapt to their column width). Fluid typography via clamp() means no discrete text-size jumps between breakpoints — the headline scales continuously from phone to desktop.

Analogy🏏Cricket
🏏 Think of it like cricket: A World Cup campaign is built on a clear hierarchy — the national cricket board's philosophy (token architecture), the coaching staff and squad system (component library), and the match day execution (page assembly). Just as the match day draws on the squad without modifying the coaching philosophy, the page assembly draws on the components without modifying the tokens. Just as the squad is verified to be fit, accessible to all roles, and tested under pressure, the components are verified to be semantic, accessible, and tested across themes. The insight is that clean layer separation, verifiable by testing each layer independently, is what makes the whole campaign — and the whole codebase — reliable.
bash
# Project structure — layers explicit in the directory layout
# cricstat-marketing/
# ├─ tailwind.config.js           <- maps semantic tokens to utilities
# ├─ src/
# │  ├─ tokens/
# │  │  ├─ primitives.css         <- raw palette, scale values
# │  │  └─ themes.css             <- semantic tokens: :root, .dark, .theme-emerald, .theme-rose
# │  ├─ lib/                      <- the component layer
# │  │  ├─ cn.js                  <- clsx + tailwind-merge
# │  │  ├─ Button.jsx             <- primary/secondary/danger, sm/md/lg, accessible
# │  │  ├─ Card.jsx               <- surface token, elevated variant
# │  │  ├─ Input.jsx              <- labelled, focus-visible, native invalid state
# │  │  ├─ Badge.jsx              <- semantic tone variants
# │  │  ├─ StatTile.jsx           <- composes Card + tokens + type scale
# │  │  └─ Nav.jsx                <- sticky, responsive, frosted-glass, focus-visible
# │  ├─ sections/                 <- page sections assembled from lib/
# │  │  ├─ Hero.jsx               <- fluid type (clamp), two-column lg, animated entrance
# │  │  ├─ Features.jsx           <- responsive grid, container-adaptive FeatureCard
# │  │  ├─ Pricing.jsx            <- comparison cards, highlighted plan
# │  │  ├─ SignupForm.jsx         <- accessible form, native validation, sr-only labels
# │  │  └─ Footer.jsx             <- flex wrap columns, semantic tokens
# │  └─ App.jsx                   <- page composition + theme switcher
# └─ .eslintrc.js + prettier.config.js + tailwind.config.js

Phase 1 — Token System and Core Components

Phase 1 builds the foundation: the three-tier token architecture plus the Button, Card, and Input components. Define primitives, then semantic mappings for light, dark, and the two brand themes, then map to Tailwind utilities in the config. Build Button with cva and the cn helper (primary, secondary, danger, three sizes, all states, accessible); build Card consuming only surface and body tokens; build Input with associated label, focus-visible ring, and native invalid state. Validate by switching the root theme class and confirming all three components re-theme instantly.

Analogy🏏Cricket
🏏 Think of it like cricket: Phase 1 is selecting and confirming the squad's core — the three must-have positions (keeper, strike bowler, reliable bat) that every game plan depends on. Just as the core trio must be rock-solid before the selection is complete, the token architecture and core components must be solid before the page is assembled. Just as the core positions are tested in warm-up matches before the campaign opens, the token system and core components are tested by theme-switching before the sections are built. The insight is that the load-bearing core must be verified before the dependent structure is raised on top of it.
javascript
/* src/tokens/themes.css  three-tier token system + three themes */
:root {   /* SEMANTIC layer, light theme defaults */
  --color-primary: var(--blue-700);
  --color-surface: var(--slate-50);
  --color-body: var(--slate-900);
  --color-muted: var(--slate-500);
  --color-border: var(--slate-200);
}
.dark {
  --color-primary: var(--blue-400);
  --color-surface: var(--slate-900);
  --color-body: var(--slate-50);
  --color-border: var(--slate-700);
}
.theme-emerald                { --color-primary: var(--emerald-600); }
.theme-emerald.dark           { --color-primary: var(--emerald-400); }
.theme-rose                   { --color-primary: var(--rose-600); }
.theme-rose.dark              { --color-primary: var(--rose-400); }

// src/lib/Button.jsx — cva variant API, semantic tokens, fully accessible
import { cva } from 'class-variance-authority';
import { cn } from './cn';
const btn = cva(
  'inline-flex items-center justify-center font-semibold rounded-card transition-colors ' +
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ' +
  'disabled:opacity-50 disabled:pointer-events-none',
  { variants: {
      variant: {
        primary:   'bg-primary text-surface hover:opacity-90 focus-visible:ring-primary',
        secondary: 'bg-surface text-body border border-border hover:bg-muted/10 focus-visible:ring-muted',
        danger:    'bg-danger text-surface hover:opacity-90 focus-visible:ring-danger'
      },
      size: { sm:'px-3 py-1.5 text-sm', md:'px-4 py-2', lg:'px-6 py-3 text-lg' }
    },
    defaultVariants: { variant: 'primary', size: 'md' }
  }
);
export function Button({ variant, size, className, ...p }) {
  return <button {...p} className={cn(btn({ variant, size }), className)} />;
}

Phase 2 — Page Sections

Build the Nav, Hero, Features, Pricing, Form, and Footer sections. The Nav is sticky with a frosted-glass backdrop-blur effect (small, stable surface), a mobile-first hamburger toggle, and a focus-visible ring on every link. The Hero uses clamp() for fluid headings, a two-column layout from lg, and an animated entrance (fill-mode forwards, motion-reduce aware). The Features section uses a responsive grid with container queries on each FeatureCard so cards adapt to their column width. The Form is fully accessible — labelled inputs, native validation feedback, no display:none-hidden controls.

Analogy🏏Cricket
🏏 Think of it like cricket: Phase 2 is filling the whole eleven — every position that covers every phase of the game, from the opening over to the death, assembled and coordinated. Just as a balanced eleven covers all phases, the page sections cover all visitor journeys — navigation, pitch, features, conversion, contact, departure. Just as each player must fit the system and support the others, each section must use the token system and compose with the others. The insight is that completion means every position filled and every player playing the system — the full eleven and the full page both require coherence, not just individual quality.
javascript
// src/sections/Hero.jsx — fluid type + entrance animation + two-column lg
// tailwind.config.js: keyframes: { 'fade-up': { '0%':{opacity:'0',transform:'translateY(1rem)'}, '100%':{opacity:'1',transform:'translateY(0)'} }},
//                    animation: { 'fade-up': 'fade-up 600ms ease-out forwards' }
export function Hero() {
  return (
    <section className="mx-auto max-w-6xl px-4 py-16 lg:py-24">
      <div className="flex flex-col lg:flex-row lg:items-center gap-10">
        <div className="flex-1 text-center lg:text-left motion-safe:animate-fade-up">
          <h1
            className="font-extrabold text-body leading-tight"
            style={{ fontSize: 'clamp(2rem, 1.5rem + 3vw, 4.5rem)' }}>
            Every ball. Every stat. <span className="text-primary">One app.</span>
          </h1>
          <p className="mt-4 text-muted text-lg max-w-prose mx-auto lg:mx-0">
            Live scores, deep player analytics, and historic scorecards  all in your pocket.
          </p>
          <div className="mt-6 flex flex-wrap gap-3 justify-center lg:justify-start">
            <Button size="lg">Get started</Button>
            <Button variant="secondary" size="lg">Watch demo</Button>
          </div>
        </div>
        <div className="flex-1 grid grid-cols-2 gap-4 motion-safe:animate-fade-up [animation-delay:150ms]">
          <StatTile value="264" label="Highest ODI" />
          <StatTile value="4.6" label="Bumrah econ" />
          <StatTile value="50" label="Kohli tons" className="col-span-2" />
        </div>
      </div>
    </section>
  );
}

Phase 3 — Polish, Theming and Production Hardening

Phase 3 connects the theme switcher, adds the final microinteractions, and hardens everything for production. The theme switcher toggles the root class, persists the choice, and sets the initial theme before first paint via an inline head script to prevent flash. Feature cards get hover animations (scale + shadow, compositor-friendly). The pricing section highlights the recommended plan with a primary-color accent. The form's submit button shows a loading spinner (animate-spin on an SVG) during submission.

Hardening means running the full checklist: prettier and eslint pass with zero errors, lint finds no raw-color violations, the production build is under 20KB gzipped, Lighthouse accessibility is above 90, and the whole site is keyboard-navigable with a visible focus ring on every interactive element in every theme and mode. These are the gates that distinguish a finished production deliverable from a finished-looking prototype.

Analogy🏏Cricket
🏏 Think of it like cricket: Phase 3 is the final nets session and the pre-match checks — the captain reviewing every set-piece, the physio clearing every player fit, the analysts confirming every opposition vulnerability is covered. Just as the pre-match checks ensure nothing is left to chance on match day, the production hardening ensures nothing is left to chance on launch day. Just as a team that skips the pre-match checks carries hidden risks into the match, a product that skips the production checklist carries hidden accessibility and performance failures into launch. The insight is that hardening is not extra work — it is the verification that the preparation is complete and the team is ready to perform.
javascript
// src/App.jsx — theme switcher, pre-paint, assembly
import { useState, useEffect } from 'react';
// (other section imports)

const THEMES = ['', 'theme-emerald', 'theme-rose'];

// Inline script to add to <head> to prevent theme flash:
// <script>
//   var saved = localStorage.getItem('theme');
//   var pref  = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : '';
//   document.documentElement.className = (saved || pref);
// </script>

export function App() {
  const [brand, setBrand] = useState('');
  const [dark, setDark]   = useState(() => matchMedia('(prefers-color-scheme:dark)').matches);

  useEffect(() => {
    const cls = [brand, dark ? 'dark' : ''].filter(Boolean).join(' ');
    document.documentElement.className = cls;
    localStorage.setItem('theme', cls);
  }, [brand, dark]);

  return (
    <div className="min-h-screen bg-surface text-body">
      <Nav brand={brand} setBrand={setBrand} THEMES={THEMES} dark={dark} setDark={setDark} />
      <Hero />
      <Features />
      <Pricing />
      <SignupForm />
      <Footer />
    </div>
  );
}

Evaluation Rubric

  • Token architecture: three themes switch by root-class change alone, zero component edits; lint confirms zero raw-color violations in component files; config maps only semantic tokens.
  • Responsive engineering: mobile-first at 360px, all sections adapt correctly to 768px and 1440px; feature cards use container queries; hero heading scales with clamp() between breakpoints.
  • Animation quality: all motion uses compositor-friendly properties (transform/opacity); motion-reduce disables or calms every animation; no jank under 4x CPU throttle in DevTools.
  • Accessibility: Lighthouse accessibility score above 90; every interactive element has a visible focus-visible ring in every theme and mode; all status and error states use multi-channel cues (not color alone); icon buttons have sr-only labels.
  • Production build: gzipped stylesheet under 20KB; Lighthouse performance score above 80; no render-blocking CSS (or a preload hint added); Prettier and ESLint pass with zero errors.
  • Component library quality: each of the eight components (Button, Card, Input, Badge, StatTile, Nav, Hero, FeatureCard) exposes a clean variant API, spreads props for behaviour pass-through, and merges className last via tailwind-merge.
  • Code quality: no @apply on markup you control; extraction follows the three-uses threshold; class ordering is automated; the token system is tiered with no primitive-bypass in components.

Extension Challenges: (1) Generate the primitive and semantic tokens from a Figma export or tokens.json, and write a build script that updates the CSS token files automatically from the design source. (2) Add a fifth section — a testimonials carousel using native scroll snap — replacing a JavaScript carousel library. (3) Publish the component library as an npm package and consume it in a second app (a dashboard), proving the system scales across products. (4) Add a high-contrast accessibility theme as a fourth theme option that raises all text contrast ratios above 7:1 for WCAG AAA.

Submit your capstone project

Checking submission status…
Final Exam unlocks when all 35 lessons are complete (35 left)
Lesson 35 of 35
0% complete