Real-World Project
Plan and build a complete responsive landing page combining the grid system, navbar, cards, and a contact form.
Introduction
This final lesson pulls together everything from the course into one project: a responsive marketing landing page for a fictional product called "TaskFlow." You will plan the page section by section, then build it with a navbar, hero, feature grid, pricing cards, and a contact form — the same structure used by countless real product sites.
- A responsive, collapsible navbar with a call-to-action button.
- A hero section with a headline, subtext, and buttons.
- A three-column feature grid using cards.
- A three-tier pricing section, again using cards.
- A validated contact form using form controls and grid columns.
- A simple multi-column footer.
Planning the Page
Before writing markup, sketch the page as a stack of full-width sections, then decide how each section's content breaks into columns at different widths. TaskFlow's landing page will use this structure, top to bottom: navbar, hero, features (3 columns → 1 on mobile), pricing (3 columns → 1 on mobile), contact form (2 columns → 1 on mobile), footer (4 columns → 1 on mobile).
| Section | Desktop Layout | Mobile Layout |
|---|---|---|
| Navbar | Horizontal links + CTA button | Collapsed hamburger menu |
| Hero | Centered text, two buttons side by side | Stacked, full-width buttons |
| Features | 3 cards per row (col-md-4) | 1 card per row (col-12) |
| Pricing | 3 cards per row (col-lg-4) | 1 card per row (col-12) |
| Contact Form | 2 fields per row (col-md-6) | 1 field per row (col-12) |
| Footer | 4 columns (col-md-3) | Stacked columns |
Project Setup
Start from a standard Bootstrap HTML skeleton with the CDN CSS in the head and the JS bundle before the closing body tag.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TaskFlow — Organize Your Team's Work</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"></head><body>
<!-- sections go here -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script></body></html>Building the Navbar
A responsive navbar with a collapsible menu on mobile and a highlighted call-to-action button on the right.
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top"> <div class="container"> <a class="navbar-brand fw-bold" href="#">TaskFlow</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navContent" aria-controls="navContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navContent"> <ul class="navbar-nav ms-auto mb-2 mb-lg-0 align-items-lg-center"> <li class="nav-item"><a class="nav-link" href="#features">Features</a></li> <li class="nav-item"><a class="nav-link" href="#pricing">Pricing</a></li> <li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li> <li class="nav-item ms-lg-3"> <a class="btn btn-primary" href="#contact">Get Started</a> </li> </ul> </div> </div></nav>Building the Hero Section
A centered hero using utility classes for spacing, typography, and a two-button call-to-action row that stacks on mobile.
<header class="bg-primary bg-gradient text-white text-center py-5"> <div class="container py-5"> <h1 class="display-4 fw-bold">Organize Your Team's Work in One Place</h1> <p class="lead col-lg-8 mx-auto"> TaskFlow brings tasks, docs, and conversations together so your team always knows what's next. </p> <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mt-4"> <a href="#pricing" class="btn btn-light btn-lg px-4">View Pricing</a> <a href="#contact" class="btn btn-outline-light btn-lg px-4">Request a Demo</a> </div> </div></header>Building the Features Grid
Three feature cards laid out with the grid system, one card per column on mobile and three across from the md breakpoint up.
<section id="features" class="py-5"> <div class="container"> <h2 class="text-center mb-5">Why Teams Choose TaskFlow</h2> <div class="row g-4"> <div class="col-12 col-md-4"> <div class="card h-100 border-0 shadow-sm"> <div class="card-body text-center"> <i class="bi bi-kanban fs-1 text-primary mb-3"></i> <h5 class="card-title">Visual Boards</h5> <p class="card-text">Track work at a glance with drag-and-drop kanban boards.</p> </div> </div> </div> <div class="col-12 col-md-4"> <div class="card h-100 border-0 shadow-sm"> <div class="card-body text-center"> <i class="bi bi-people fs-1 text-primary mb-3"></i> <h5 class="card-title">Team Collaboration</h5> <p class="card-text">Comment, assign, and share files without leaving the task.</p> </div> </div> </div> <div class="col-12 col-md-4"> <div class="card h-100 border-0 shadow-sm"> <div class="card-body text-center"> <i class="bi bi-graph-up fs-1 text-primary mb-3"></i> <h5 class="card-title">Progress Reports</h5> <p class="card-text">Automatic dashboards show exactly where every project stands.</p> </div> </div> </div> </div> </div></section>Building the Pricing Cards
Three pricing tiers, with the middle "recommended" plan visually emphasized using a border and badge — a common real-world pattern combining cards, badges, and list groups.
<section id="pricing" class="py-5 bg-body-tertiary"> <div class="container"> <h2 class="text-center mb-5">Simple, Transparent Pricing</h2> <div class="row g-4 justify-content-center"> <div class="col-12 col-lg-4"> <div class="card h-100"> <div class="card-body text-center"> <h5 class="card-title">Starter</h5> <p class="display-6">$9<small class="fs-6 text-body-secondary">/mo</small></p> <ul class="list-group list-group-flush text-start mb-3"> <li class="list-group-item">Up to 5 team members</li> <li class="list-group-item">3 active projects</li> </ul> <a href="#contact" class="btn btn-outline-primary w-100">Choose Starter</a> </div> </div> </div> <div class="col-12 col-lg-4"> <div class="card h-100 border-primary border-2 shadow"> <div class="card-body text-center"> <span class="badge text-bg-primary mb-2">Most Popular</span> <h5 class="card-title">Team</h5> <p class="display-6">$29<small class="fs-6 text-body-secondary">/mo</small></p> <ul class="list-group list-group-flush text-start mb-3"> <li class="list-group-item">Up to 25 team members</li> <li class="list-group-item">Unlimited projects</li> </ul> <a href="#contact" class="btn btn-primary w-100">Choose Team</a> </div> </div> </div> <div class="col-12 col-lg-4"> <div class="card h-100"> <div class="card-body text-center"> <h5 class="card-title">Enterprise</h5> <p class="display-6">Custom</p> <ul class="list-group list-group-flush text-start mb-3"> <li class="list-group-item">Unlimited members</li> <li class="list-group-item">Dedicated support</li> </ul> <a href="#contact" class="btn btn-outline-primary w-100">Contact Sales</a> </div> </div> </div> </div> </div></section>Building the Contact Form
A contact form using paired grid columns for name/email, full-width fields for the message, and Bootstrap's validation classes.
<section id="contact" class="py-5"> <div class="container"> <h2 class="text-center mb-5">Request a Demo</h2> <form class="row g-3 col-lg-8 mx-auto needs-validation" novalidate> <div class="col-12 col-md-6"> <label for="fullName" class="form-label">Full Name</label> <input type="text" class="form-control" id="fullName" required> <div class="invalid-feedback">Please enter your name.</div> </div> <div class="col-12 col-md-6"> <label for="workEmail" class="form-label">Work Email</label> <input type="email" class="form-control" id="workEmail" required> <div class="invalid-feedback">Please enter a valid email.</div> </div> <div class="col-12"> <label for="message" class="form-label">What are you hoping to solve?</label> <textarea class="form-control" id="message" rows="4" required></textarea> <div class="invalid-feedback">Please add a short message.</div> </div> <div class="col-12 text-center"> <button class="btn btn-primary btn-lg px-5" type="submit">Request Demo</button> </div> </form> </div></section>// Bootstrap's standard client-side validation trigger(() => { document.querySelectorAll('.needs-validation').forEach(form => { form.addEventListener('submit', event => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } form.classList.add('was-validated'); }, false); });})();Building the Footer
A dark, four-column footer that stacks to a single column on mobile, wrapping up the page.
<footer class="bg-dark text-white py-5"> <div class="container"> <div class="row g-4"> <div class="col-12 col-md-3"> <h5>TaskFlow</h5> <p class="text-body-secondary">Work management for growing teams.</p> </div> <div class="col-6 col-md-3"> <h6>Product</h6> <ul class="list-unstyled"> <li><a class="link-light link-underline-opacity-0" href="#features">Features</a></li> <li><a class="link-light link-underline-opacity-0" href="#pricing">Pricing</a></li> </ul> </div> <div class="col-6 col-md-3"> <h6>Company</h6> <ul class="list-unstyled"> <li><a class="link-light link-underline-opacity-0" href="#">About</a></li> <li><a class="link-light link-underline-opacity-0" href="#">Careers</a></li> </ul> </div> <div class="col-12 col-md-3"> <h6>Stay Updated</h6> <form class="d-flex" role="search"> <input class="form-control me-2" type="email" placeholder="Email address" aria-label="Email address"> <button class="btn btn-outline-light" type="submit">Join</button> </form> </div> </div> <hr class="my-4"> <p class="text-center text-body-secondary mb-0">© 2026 TaskFlow. All rights reserved.</p> </div></footer>Testing Responsiveness
Before calling the page done, walk through it at each breakpoint using your browser's device toolbar: confirm the navbar collapses correctly below lg, the feature and pricing cards stack to one column below md/lg, the contact form fields stack below md, and the footer columns stack below md. Also confirm the sticky-top navbar does not overlap the hero heading on short mobile viewports.
Common Mistakes
- Building the whole page at desktop width first and only checking mobile at the very end, when fixes are more expensive.
- Forgetting novalidate plus the was-validated JS trigger, so the browser's default validation bubbles fight Bootstrap's styled feedback.
- Nesting a full <div class="container"> inside a section that is already inside another container, doubling up padding.
- Leaving placeholder link href="#" on primary navigation without real destinations before shipping.
- Missing aria-label on icon-only or icon-first controls, like the newsletter form's email input.
Best Practices
- Plan each section's column breakdown before writing markup, as done in the planning table above.
- Reuse the same card pattern (h-100, shadow-sm, card-body) across features and pricing for visual consistency.
- Keep one <container> per section rather than nesting containers.
- Use semantic landmarks — <header>, <nav>, <section id="...">, <footer> — so the page has a real document outline.
- Test keyboard navigation end to end: tab through the navbar, form fields, and buttons to confirm focus order and visibility.
Frequently Asked Questions
Yes — use one .container (or .container-fluid for edge-to-edge sections) per <section>, directly inside it, rather than nesting multiple containers, which would compound padding unexpectedly.
You can add a slight scale transform via a small custom CSS rule, or increase the shadow/border utilities, as done above with border-primary border-2 shadow — both are legitimate uses of custom CSS since Bootstrap has no built-in "featured card" utility.
No — this entire landing page is built with plain HTML, Bootstrap's CSS classes, and the small validation script shown above; a framework like React only becomes useful once the page needs real interactivity beyond forms and toggles.
Key Takeaways
- A real landing page is just a stack of sections, each using the grid system to break its content into responsive columns.
- The navbar, cards, list groups, badges, forms, and icons from earlier lessons combine naturally in a single page.
- Client-side form validation needs both the novalidate attribute and the was-validated JavaScript trigger.
- Always verify the finished page across every breakpoint, not just the width you built it at.