Python · SQL · Web Dev · Java · AI/ML tracks launching soon — your one platform for all of IT
Advanced+300 XP

Building a Complete Responsive Website

The capstone project — a full, real, responsive website built end-to-end using everything from this entire track.

60 min August 2026
// The Major Capstone

Every Technique From This Entire Track, in One Real Build

This module builds a complete, real, responsive marketing site for a fictional studio (Ridgeline Design Co.) end to end — a hero section, a navigation bar that collapses on mobile, a responsive content grid, and a footer. Every technique used is one already covered somewhere earlier in this 42-module track; this module's job is showing how they compose into one genuinely working page, not teaching anything new.

// Part 01 — The HTML Structure

Semantic Structure First, Styling Second

The full page skeleton — Phase 1 semantics
<body>
  <header class="site-header">
    <a href="/" class="logo">Ridgeline Design Co.</a>
    <button class="menu-toggle" aria-label="Toggle menu" aria-expanded="false">☰</button>
    <nav class="main-nav">
      <ul>
        <li><a href="#work">Work</a></li>
        <li><a href="#services">Services</a></li>
        <li><a href="#contact">Contact</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <section class="hero"> ... </section>
    <section id="work" class="project-grid"> ... </section>
    <section id="services" class="services"> ... </section>
    <section id="contact" class="contact"> ... </section>
  </main>

  <footer class="site-footer"> ... </footer>
</body>

The document is planned as landmarks first — header/nav/main/section/footer — exactly the Phase 1 semantic structure approach, before a single CSS rule exists.

// Part 02 — Mobile-First Base Styles

Starting From the Smallest Screen, Per the Mobile-First Module

Base styles — written for mobile first, no media query yet
:root {
  --color-ink: #1a1a1a;
  --color-accent: #ff4757;
  --spacing-unit: 8px;
  --max-width: 1200px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: system-ui, sans-serif;
  color: var(--color-ink);
  line-height: 1.6;
}

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: calc(var(--spacing-unit) * 2);
}

.main-nav {
  display: none;  /* hidden by default on mobile — revealed by menu-toggle */
}

.main-nav.is-open {
  display: block;
}

This uses the custom properties from that module for a real, working design token system (--color-accent, --spacing-unit), and follows box-sizing: border-box from the Box Model module as the very first rule in the reset — the base every later measurement assumes.

// Part 03 — The Hero Section, With Flexbox

Centering and Layout From the Flexbox Modules

The hero — Flexbox centering, fluid typography with clamp()
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: calc(var(--spacing-unit) * 8) calc(var(--spacing-unit) * 3);
}

.hero h1 {
  font-size: clamp(28px, 6vw, 56px);
  max-width: 20ch;
}

.hero p {
  font-size: clamp(16px, 2.5vw, 20px);
  max-width: 60ch;
  color: #666;
  margin-top: var(--spacing-unit);
}

clamp() from the Responsive Design module handles the heading and paragraph's font size scaling smoothly across every viewport width, with no discrete breakpoint jump needed just for typography.

// Part 04 — The Project Grid, With CSS Grid

A Genuinely Responsive Grid With No Media Query at All

auto-fill + minmax — the Grid module's real-layouts technique
.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: calc(var(--spacing-unit) * 3);
  padding: calc(var(--spacing-unit) * 6) calc(var(--spacing-unit) * 3);
  max-width: var(--max-width);
  margin: 0 auto;
}

.project-card {
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  transition: transform 0.2s ease;
}

.project-card:hover {
  transform: translateY(-4px);
}

repeat(auto-fill, minmax(260px, 1fr)) — directly from the CSS Grid in Practice module — reflows the number of columns automatically as the viewport changes, with zero media queries needed for this specific grid at all. The hover lift on .project-card uses transform, not top/margin, following the cheap-vs-expensive property guidance from the Transitions module.

// Part 05 — The Mobile Nav Toggle Breakpoint

Where a Media Query Genuinely Is Needed

The one place this build genuinely needs a breakpoint
@media (min-width: 768px) {
  .menu-toggle {
    display: none;   /* the hamburger button only exists below this width */
  }

  .main-nav {
    display: block;   /* the nav is simply always visible on wider screens */
  }

  .main-nav ul {
    display: flex;
    gap: calc(var(--spacing-unit) * 3);
    list-style: none;
  }
}

This is the Flexbox vs Grid module's decision framework applied for real: the overall page uses Grid for its two-dimensional project layout, Flexbox for the one-dimensional horizontal nav-link row — each reached for specifically where it fits, not out of habit. The 768px value itself was chosen the way the Responsive Design module recommends: by resizing this specific nav bar's actual content and finding where it starts to feel cramped as a horizontal row, not copied from a framework default.

// Part 06 — The Contact Form and Accessibility Pass

Bringing Back Phase 1's Forms, Styled

A real contact form, correctly labeled, with a visible focus state
<section id="contact" class="contact">
  <h2>Get in Touch</h2>
  <form action="/submit-contact" method="POST">
    <label for="name">Name</label>
    <input type="text" id="name" name="name" required>

    <label for="email">Email</label>
    <input type="email" id="email" name="email" required>

    <button type="submit">Send</button>
  </form>
</section>
Accessible focus states — from the CSS Accessibility module
input:focus-visible,
button:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .project-card {
    transition: none;
  }
}
🎯 Pro Tip
Every input keeps its real, associated label from Module 8 — never replaced with placeholder text — and the prefers-reduced-motion query from the Accessibility module disables the hover-lift transition for users who have indicated they prefer reduced motion at the OS level.
// Part 07 — Final Responsive Pass

Testing Across the Full Range, Not Just a Few Presets

The last step, matching the Responsive Design module's own advice, is dragging the browser window slowly across the entire width range — not just checking a fixed list of device presets — watching specifically for the moments the hero text wraps awkwardly, the project grid's column count changes, and the nav toggle switches between its mobile and desktop states, confirming each transition looks intentional rather than abrupt or broken.

⚠️ Important
A genuinely common real gap: testing only at exact breakpoint values, never the space just before or after one. A layout can look perfect at exactly 768px and 767px individually while still having an awkward, cramped moment at 750px that neither preset check would ever catch — continuous resizing is what catches this class of bug.
// Real World
💼 What This Looks Like at Work

A Freelancer's First Full Client Delivery, Built From Exactly This Pattern

Scenario — Freelance developer, Seattle · First paid client project

A newly freelance developer delivers a small studio's marketing site — structurally almost identical to this module's build — and the client comes back a week later specifically praising how well it "just works" on their phone, without ever having asked for mobile support explicitly.

What actually earned that reaction

Nothing exotic — a mobile-first base, a single well-chosen breakpoint for the nav, Grid's auto-fill/minmax() handling the project layout's column count automatically at every width, and real semantic HTML that search engines and screen readers alike could parse confidently. The developer's own reflection: "the client had no idea any of these specific techniques existed — they just experienced a site that behaved correctly everywhere, which is the actual point of everything in this whole track."

// Misconceptions

Four Misconceptions About Building a Real Responsive Site

"A responsive site needs a media query for every single component"
auto-fill combined with minmax() made the project grid genuinely responsive with zero media queries dedicated to it — reach for a breakpoint specifically where a component actually needs a structural change, like the nav collapsing into a hamburger menu, not everywhere by default.
"Testing at the standard preset device widths in DevTools is sufficient responsive QA"
A layout can look correct at 768px and 767px individually while still having a cramped, awkward moment at 750px that neither preset catches — continuous resizing across the full range is what actually catches this class of bug.
"Choosing Grid for the whole page is simpler than mixing Grid and Flexbox"
This build deliberately uses Grid for the two-dimensional project layout and Flexbox for the one-dimensional nav row — using only one tool everywhere often means fighting it for the layout it is not suited to, rather than reaching for whichever one actually fits each specific piece.
"Accessibility features like focus-visible and prefers-reduced-motion are optional polish for a real client site"
They cost very little to add and directly affect real users — a visible focus state and respecting a user's reduced-motion preference are part of a genuinely complete, professional build, not optional extras.
// Interview Prep

4 Interview Questions — With Complete Answers

In this build, why is Grid used for the project layout but Flexbox for the nav bar?
The project layout is genuinely two-dimensional (rows and columns of cards need to align in both directions), which is exactly what Grid is built for. The nav bar is a single row of links — a one-dimensional layout problem, which is exactly what Flexbox is built for. The choice follows the Flexbox vs Grid module's decision framework applied to real content, not personal preference.
How does repeat(auto-fill, minmax(260px, 1fr)) make the project grid responsive without a dedicated media query?
auto-fill tells Grid to fit as many 260px-minimum columns as the container width allows, and 1fr lets each column grow to fill any remaining space — the browser recalculates the column count continuously as the viewport changes, with no explicit breakpoint needed for this specific layout.
Why choose a mobile-first approach for this build rather than desktop-first?
Base styles target the smallest screen with no media query needed at all, and a single min-width query at 768px progressively adds the desktop nav layout — this tends to produce leaner CSS overall than starting from a full desktop layout and overriding it down for smaller screens.
Why does the final testing step involve continuously resizing the browser rather than only checking fixed device presets?
A layout can pass every fixed preset check while still having an awkward, cramped moment at some width in between two presets that were never explicitly tested — continuous resizing is the only way to catch that class of bug before a real user encounters it.
// Common Mistakes

Mistakes Beginners Make Building a Full Responsive Site

Reaching for a media query breakpoint before checking if a CSS-native technique already solves it
The project grid needed zero dedicated media queries thanks to auto-fill/minmax() — always check whether Grid's own responsive capabilities, or clamp() for fluid typography, already solve the problem before reaching for a breakpoint.
Copying a breakpoint value from a different project without testing this specific layout's content
The 768px nav breakpoint in this build was chosen by testing THIS nav bar's actual content at a range of widths, not copied from a framework default — a borrowed breakpoint has no guarantee of fitting different content.
Adding hover-based interactions without considering touch devices
A hover-only interaction (like the project card lift) needs to also work reasonably on touch devices that have no true hover state — combined with respecting prefers-reduced-motion, this keeps the interaction genuinely accessible.
Treating the responsive pass as a final step done once, rather than testing throughout the build
Checking responsiveness only after the whole page is built makes it harder to isolate which specific section introduced a given layout bug — testing each section's responsiveness as it's built catches problems earlier and more cheaply.
// Error Library

Issues You Will Hit Building a Full Responsive Site — And Exactly Why

The mobile nav toggle button remains visible even on a wide desktop viewport
Cause: The @media (min-width: 768px) block that hides .menu-toggle either has a typo in the selector or was placed before a later rule that re-shows it, letting a later, more specific or later-declared rule win.
Fix: Confirm the media query selector exactly matches the toggle button's class, and that no later CSS rule outside the media query re-overrides its display property.
The project grid shows only one column even on a wide screen
Cause: A parent container has a fixed, narrow max-width or width set inline, constraining the grid's available space regardless of the viewport's actual width — auto-fill responds to the GRID CONTAINER's width, not the raw viewport width.
Fix: Check every ancestor of .project-grid for an unintentional width constraint, using DevTools' box model inspector to see the grid container's actual rendered width.
Focus outlines appear on mouse clicks as well as keyboard navigation, which some designers find visually noisy
Cause: Using :focus instead of :focus-visible — :focus applies on both mouse and keyboard interaction, while :focus-visible applies specifically when the browser determines a visible focus indicator is actually needed (primarily keyboard navigation).
Fix: Use :focus-visible instead of :focus for interactive elements where a focus ring is meant to serve keyboard users specifically.

🎯 Key Takeaways

  • A real responsive build starts from semantic HTML structure — landmarks and sections planned before any CSS exists, exactly as Phase 1 established.
  • Mobile-first base styles, then progressive enhancement via min-width media queries, produces leaner CSS than a desktop-first, override-heavy approach.
  • Grid and Flexbox are used for what each is actually suited to in the same page — Grid for the two-dimensional project layout, Flexbox for the one-dimensional nav row — not chosen out of habit.
  • auto-fill combined with minmax() can make a grid genuinely responsive with zero additional media queries for that specific layout.
  • Every form field keeps a real associated label, and focus-visible states plus prefers-reduced-motion respect real accessibility needs, not just visual polish.
  • Final testing means continuously resizing across the full viewport range, not just checking a handful of fixed device presets — the gaps between presets are exactly where real bugs hide.

🏗️ The Capstone Project

Two modules remain — best practices, then the full interview prep synthesis.

This build pulled together structure, layout, responsiveness, and accessibility from across the entire track into one real, working site. Module 41 closes out with the conventions and common mistakes that separate maintainable CSS from a stylesheet nobody wants to touch, and Module 42 — the capstone — synthesizes everything into interview-ready form.

Module 41 → CSS Best Practices & Common Mistakes
Share

Discussion

0

Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.

Continue with GitHub
Loading...