Final Project & Career Paths

Reviewed & published by Brayan K

Build your capstone portfolio website, explore front-end career paths, and discover real earning potential with HTML & CSS.

Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

🎉 Welcome to the Final Step

Congratulations! You've reached the end of the LearnCodingFast HTML & CSS course. You now understand:

Now it's time to use all of it to:

What You Can Do with HTML & CSS

Career PathTypical RoleCore SkillsAverage Salary (UK 2025)Global Range (USD)
Front-End DeveloperBuild user interfaces & interactive sitesHTML, CSS, JS, React£35,000 – £65,000$55,000 – $110,000
UI/UX DesignerDesign & prototype web experiencesCSS, Figma, Design Systems£30,000 – £60,000$50,000 – $100,000
Web DesignerCreate visually stunning websitesHTML, CSS, WordPress£25,000 – £50,000$40,000 – $80,000
Email DeveloperBuild responsive email templatesHTML, CSS, Email clients£30,000 – £55,000$45,000 – $90,000
WordPress DeveloperBuild & customize WordPress sitesHTML, CSS, PHP basics£28,000 – £55,000$40,000 – $85,000
FreelancerBuild websites for clientsAll of the above£20 – £75 / hour$25 – $100 / hour

🪙 Business Opportunities You Can Build

Many successful web agencies started with just HTML & CSS skills — you have everything you need to begin.

Your Capstone Project: Portfolio Website

Build a complete, professional portfolio website that showcases your skills. This is your "proof of mastery" — something you can share with employers, clients, or on your GitHub profile.

Project Requirements

Required Pages & Sections

HTML Best Practices

CSS Best Practices

Responsive Design

Accessibility

Starter Template

Copy this starter code into a file called index.html and start customizing it:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your Name — Portfolio</title>
  <style>
    /* CSS Reset & Variables */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    
    :root {
      --primary: #2563eb;
      --text: #1e293b;
      --text-light: #64748b;
      --bg: #ffffff;
      --card-bg: #f8fafc;
      --border: #e2e8f0;
    }

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

    /* Navigation */
    nav {
      position: sticky;
      top: 0;
      background: var(--bg);
      border-bottom: 1px solid var(--border);
      padding: 1rem 2rem;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 100;
    }
    nav .logo { font-weight: 700; font-size: 1.25rem; }
    nav .links { display: flex; gap: 1.5rem; }
    nav a { text-decoration: none; color: var(--text); transition: color 0.2s; }
    nav a:hover { color: var(--primary); }

    /* Hero */
    .hero {
      text-align: center;
      padding: 6rem 2rem;
      background: linear-gradient(135deg, #eff6ff, #dbeafe);
    }
    .hero h1 { font-size: 2.5rem; margin-bottom: 1rem; }
    .hero p { font-size: 1.2rem; color: var(--text-light); max-width: 600px; margin: 0 auto; }

    /* Sections */
    section { padding: 4rem 2rem; max-width: 900px; margin: 0 auto; }
    section h2 { font-size: 2rem; margin-bottom: 1.5rem; text-align: center; }

    /* Projects Grid */
    .projects-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
      gap: 1.5rem;
    }
    .project-card {
      border: 1px solid var(--border);
      border-radius: 12px;
      overflow: hidden;
      transition: transform 0.3s, box-shadow 0.3s;
    }
    .project-card:hover { transform: translateY(-4px); box-shadow: 0 10px 25px rgba(0,0,0,0.1); }
    .project-card img { width: 100%; height: 180px; object-fit: cover; }
    .project-card .info { padding: 1rem; }
    .project-card h3 { margin-bottom: 0.5rem; }

    /* Skills */
    .skills-list {
      display: flex;
      flex-wrap: wrap;
      gap: 0.75rem;
      justify-content: center;
    }
    .skill-tag {
      background: var(--card-bg);
      border: 1px solid var(--border);
      padding: 0.5rem 1rem;
      border-radius: 999px;
      font-size: 0.9rem;
      transition: background 0.2s;
    }
    .skill-tag:hover { background: #dbeafe; }

    /* Footer */
    footer {
      text-align: center;
      padding: 2rem;
      border-top: 1px solid var(--border);
      color: var(--text-light);
      font-size: 0.9rem;
    }
  </style>
</head>
<body>
  <nav>
    <span class="logo">Your Name</span>
    <div class="links">
      <a href="#about">About</a>
      <a href="#projects">Projects</a>
      <a href="#skills">Skills</a>
      <a href="#contact">Contact</a>
    </div>
  </nav>

  <header class="hero">
    <h1>Hi, I'm Your Name 👋</h1>
    <p>A front-end developer who loves building beautiful, accessible websites.</p>
  </header>

  <section id="about">
    <h2>About Me</h2>
    <p style="text-align:center; max-width:600px; margin:0 auto;">
      I'm a passionate web developer with skills in HTML, CSS, and responsive design.
      I love creating clean, user-friendly interfaces.
    </p>
  </section>

  <section id="projects">
    <h2>My Projects</h2>
    <div class="projects-grid">
      <div class="project-card">
        <img src="https://placehold.co/400x180/dbeafe/1e293b?text=Project+1" alt="Project 1">
        <div class="info">
          <h3>Landing Page</h3>
          <p>A responsive landing page with modern CSS Grid layout.</p>
        </div>
      </div>
      <div class="project-card">
        <img src="https://placehold.co/400x180/dcfce7/1e293b?text=Project+2" alt="Project 2">
        <div class="info">
          <h3>Blog Layout</h3>
          <p>A clean blog design with Flexbox and custom typography.</p>
        </div>
      </div>
      <div class="project-card">
        <img src="https://placehold.co/400x180/fef3c7/1e293b?text=Project+3" alt="Project 3">
        <div class="info">
          <h3>Dashboard UI</h3>
          <p>An admin dashboard with CSS Grid and dark mode support.</p>
        </div>
      </div>
    </div>
  </section>

  <section id="skills">
    <h2>My Skills</h2>
    <div class="skills-list">
      <span class="skill-tag">HTML5</span>
      <span class="skill-tag">CSS3</span>
      <span class="skill-tag">Flexbox</span>
      <span class="skill-tag">CSS Grid</span>
      <span class="skill-tag">Responsive Design</span>
      <span class="skill-tag">Accessibility</span>
      <span class="skill-tag">Animations</span>
      <span class="skill-tag">Git</span>
    </div>
  </section>

  <section id="contact">
    <h2>Get In Touch</h2>
    <p style="text-align:center;">Email me at <a href="mailto:[email protected]">[email protected]</a></p>
  </section>

  <footer>
    <p>&copy; 2025 Your Name. All rights reserved.</p>
  </footer>
  <!-- ✅ Expected result, measured in a real browser:
     nav -> display: flex
     nav .links -> display: flex
     nav a -> text-decoration-line: none
     .projects-grid -> display: grid
     .skill-tag -> count: 8
  -->
</body>
</html>

Worked Example: The One Line That Makes the Projects Grid Responsive

The starter template above contains a line most beginners copy without understanding: grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)). It re-flows your project cards from three columns to two to one with no media query at all. Here is that grid on its own, with every part explained.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Responsive Projects Grid</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    body { font-family: system-ui, sans-serif; color: #1e293b; line-height: 1.6; }

    /* The section is capped at 900px and centred, with 2rem (32px) of padding
       each side. So the grid itself is at most 900 - 64 = 836px wide. */
    section {
      padding: 4rem 2rem;
      max-width: 900px;
      margin: 0 auto;
    }

    .projects-grid {
      display: grid;

      /* Read this as: "fit as many columns as you can. Each one must be at
         least 250px, and any leftover space is shared out equally (1fr)."

         repeat(auto-fit, ...) = the browser decides the column COUNT
         minmax(250px, 1fr)    = never narrower than 250px, otherwise stretch

         That is the whole responsive behaviour. No media query needed. */
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

      gap: 1.5rem;              /* 24px between cards, rows and columns */
    }

    .project-card {
      border: 1px solid #e2e8f0;
      border-radius: 12px;
      overflow: hidden;         /* keeps the image corners rounded */
      transition: transform 0.3s, box-shadow 0.3s;
    }
    .project-card:hover {
      transform: translateY(-4px);
      box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
    }
    .project-card .info { padding: 1rem; }
    .project-card h3 { margin-bottom: 0.5rem; font-size: 1.1rem; }
    .project-card p { font-size: 0.9rem; color: #64748b; }
    h2 { text-align: center; margin-bottom: 1.5rem; }
  </style>
</head>
<body>
  <section>
    <h2>My Projects</h2>
    <div class="projects-grid">
      <div class="project-card">
        <div class="info"><h3>Landing Page</h3><p>Responsive layout with CSS Grid.</p></div>
      </div>
      <div class="project-card">
        <div class="info"><h3>Blog Layout</h3><p>Flexbox and custom typography.</p></div>
      </div>
      <div class="project-card">
        <div class="info"><h3>Dashboard UI</h3><p>CSS Grid with dark mode support.</p></div>
      </div>
    </div>
  </section>
  <!-- ✅ Expected result, measured in a real browser:
     .projects-grid -> display: grid
     .project-card -> overflow: hidden
     .project-card -> count: 3
  -->
</body>
</html>
The page this code makes: Drag the preview narrower and watch the columns re-flow
What this code shows in a browser window 720 pixels wide.

Measured in Chrome at four different window widths:

Window widthColumnsEach card measures
900px and up3262.66px
700px2306px
500px1436px
375px (phone)1311px

Why 3 columns at 900px? The grid is 836px wide. Three 250px columns plus two 24px gaps is 798px, which fits. Four columns would need 1,072px, which does not — so the browser settles on three and stretches each to 262.66px.

🎯 Your Turn: Make the Header Survive a Phone

Here is a real bug in the starter template: at phone width the logo and four nav links will not fit on one row, so the whole page scrolls sideways. Your job is the media query at the bottom — everything above it already works. Fill in the three ___ blanks, then narrow the preview to about 375px.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!-- Without this line, phones pretend to be 980px wide and your media
       query never fires. It is the #1 cause of "my site isn't responsive". -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your Turn: Responsive Header</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --primary: #2563eb;
      --text: #1e293b;
      --text-light: #64748b;
      --bg: #ffffff;
      --border: #e2e8f0;
    }

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

    nav {
      position: sticky;
      top: 0;
      background: var(--bg);
      border-bottom: 1px solid var(--border);
      padding: 1rem 2rem;
      display: flex;                    /* logo on the left, links on the right */
      justify-content: space-between;
      align-items: center;
      z-index: 100;
    }
    nav .logo  { font-weight: 700; font-size: 1.25rem; white-space: nowrap; }
    nav .links { display: flex; gap: 1.5rem; }
    nav a      { text-decoration: none; color: var(--text); white-space: nowrap; }
    nav a:hover { color: var(--primary); }

    .hero {
      text-align: center;
      padding: 6rem 2rem;
      background: linear-gradient(135deg, #eff6ff, #dbeafe);
    }
    .hero h1 { font-size: 2.5rem; margin-bottom: 1rem; }
    .hero p  { font-size: 1.2rem; color: var(--text-light); max-width: 600px; margin: 0 auto; }

    /* 🎯 YOUR TURN — fill in the three ___ below. */

    /* 👉 1) This rule should switch on for phones only.
           A common breakpoint for "small screen" is 640px wide. */
    @media (max-width: ___) {

      nav {
        /* 👉 2) Stack the logo ABOVE the links instead of beside them.
               Flexbox has one property for the main axis direction. */
        flex-direction: ___;
        gap: 0.75rem;
      }

      nav .links { gap: 1rem; font-size: 0.9rem; }

      .hero { padding: 3rem 1rem; }

      .hero h1 {
        /* 👉 3) 2.5rem (40px) is too big on a phone. Use 1.75rem. */
        font-size: ___;
      }
    }

    /* ✅ Expected result, measured in Chrome at a 375px viewport:
       - BEFORE you fill the blanks, the page is 440px wide inside a 375px
         window: 65px of unwanted sideways scrolling.
       - AFTER, the page measures exactly 375px wide - the sideways scroll
         is gone.
       - The nav grows from 65px tall to 100.05px tall, because the links
         have moved onto their own row underneath the logo.
       - The h1 computes to 28px instead of 40px.
       - At 900px wide, nothing changes at all - the media query is asleep. */
  </style>
</head>
<body>
  <nav>
    <span class="logo">Your Name</span>
    <div class="links">
      <a href="#about">About</a>
      <a href="#projects">Projects</a>
      <a href="#skills">Skills</a>
      <a href="#contact">Contact</a>
    </div>
  </nav>

  <header class="hero">
    <h1>Hi, I'm Your Name</h1>
    <p>A front-end developer who loves building beautiful, accessible websites.</p>
  </header>
</body>
</html>
@media (max-width: 640px) {
  nav { flex-direction: column; }
  .hero h1 { font-size: 1.75rem; }
}

Once this works, paste the same media query into your own capstone file. Horizontal scrolling on a phone is the single most common reason a portfolio looks unfinished to a hiring manager.

Features to Showcase Your Skill Level

🟢 Beginner Level

🔵 Intermediate Level

🟣 Expert Level

🏆 Mini-Challenge: Add a Testimonials Section

No blanks and no answers this time — just a brief and an outline. Add a Testimonials section to your portfolio that reuses the design tokens already in :root and re-flows on small screens exactly like the projects grid does. This is the whole course in one exercise: semantic HTML, custom properties, Grid, a transition, and accessible contrast.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mini-Challenge: Testimonials</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    /* These tokens come straight from your starter template. Use them -
       do not hard-code the same colours again further down. */
    :root {
      --primary: #2563eb;
      --text: #1e293b;
      --text-light: #64748b;
      --bg: #ffffff;
      --card-bg: #f8fafc;
      --border: #e2e8f0;
    }

    body { font-family: system-ui, sans-serif; color: var(--text); background: var(--bg); line-height: 1.6; }
    section { padding: 4rem 2rem; max-width: 900px; margin: 0 auto; }
    section h2 { font-size: 2rem; margin-bottom: 1.5rem; text-align: center; }

    /* 🎯 MINI-CHALLENGE — write the rules yourself. No answers below.

       1. .testimonials -> display: grid
                           grid-template-columns using repeat(auto-fit, minmax(...))
                           so it drops from 2 columns to 1 on a phone.
                           Pick your own minimum - 280px is a sensible choice.
                           gap: 1.5rem

       2. .testimonial  -> background var(--card-bg)
                           1px solid var(--border)
                           border-radius 12px
                           padding 1.5rem

       3. .testimonial blockquote -> italic, colour var(--text), margin 0
          .testimonial figcaption   -> smaller text, colour var(--text-light)

       4. Add a hover lift, the same idea as .project-card:
                           transition: transform 0.3s
                           :hover -> transform: translateY(-4px)

       5. Wrap the whole thing in @media (prefers-reduced-motion: reduce)
          and set transition: none for people who do not want movement.

       ✅ Check yourself:
          - Narrow the preview. The cards must go from 2 across to 1 across
            with no media query of your own - auto-fit does that job.
          - Every colour in your new rules should be a var(--...), not a
            hex code. If you typed #f8fafc anywhere, use var(--card-bg).
          - Leave the markup's <figure> / <blockquote> / <figcaption>
            structure alone. The HTML spec says the attribution must sit
            OUTSIDE the <blockquote>, because the blockquote is the quoted
            words only - the "who said it" is not part of the quote.
            <figure> is the wrapper that lets you keep them together.
    */
  </style>
</head>
<body>
  <section id="testimonials">
    <h2>What People Say</h2>

    <div class="testimonials">
      <figure class="testimonial">
        <blockquote><p>Delivered exactly what we asked for, a week early.</p></blockquote>
        <figcaption>— Sam Okonkwo, Project Manager</figcaption>
      </figure>

      <figure class="testimonial">
        <blockquote><p>The site works perfectly on every phone we tested.</p></blockquote>
        <figcaption>— Priya Raman, Small Business Owner</figcaption>
      </figure>

      <figure class="testimonial">
        <blockquote><p>Clean, readable code that our team could pick straight up.</p></blockquote>
        <figcaption>— Alex Dubois, Lead Developer</figcaption>
      </figure>
    </div>
  </section>
  <!-- ✅ Expected result, measured in a real browser:
     section -> max-width: 900px
     section h2 -> font-size: 32px
     .testimonial -> count: 3
  -->
</body>
</html>

When it works, drop the section into your capstone between Projects and Contact, and add a <a href="#testimonials"> link to the nav so it is reachable.

Development Tips

Free Resources & Inspiration

🎨 Design Resources

🚀 Where to Deploy

📘 Quick Reference — Portfolio Checklist

CategoryRequirementWhy It Matters
StructureSemantic HTML5 tagsSEO & accessibility
LayoutFlexbox + GridModern, flexible layouts
ResponsiveMobile-first design50%+ traffic is mobile
TypographyCustom fonts + hierarchyProfessional appearance
InteractivityHover effects & transitionsPolished user experience
AccessibilityContrast, alt text, keyboard navInclusive & professional
PerformanceOptimized images, minimal CSSFast load times

🎉 Congratulations — Course Complete!

You've completed the entire HTML & CSS course! You now have the skills to build beautiful, responsive, and accessible websites from scratch.

Keep building, keep learning, and never stop creating! 🚀

Practice quiz

Which set of elements reflects good semantic structure for a portfolio page?

  • header, nav, main, section, footer
  • div, div, div, div, div
  • table, tr, td only
  • span, b, i, font

Answer: header, nav, main, section, footer. Semantic landmarks (header, nav, main, section, footer) give the page structure for SEO and accessibility.

What does 'mobile-first' responsive design mean?

  • Designing for mobile screens first, then enhancing for larger screens with media queries
  • Only supporting mobile devices
  • Building the desktop layout first and shrinking it
  • Using a separate mobile-only website

Answer: Designing for mobile screens first, then enhancing for larger screens with media queries. Mobile-first starts with the small-screen layout and layers on desktop styles via media queries.

Which CSS feature is recommended for theming a portfolio (e.g. a dark/light toggle)?

  • Inline styles on every element
  • CSS custom properties (variables)
  • Many duplicate stylesheets
  • JavaScript only

Answer: CSS custom properties (variables). CSS custom properties centralize theme values so a toggle can swap them everywhere at once.

What is the recommended minimum touch target size for buttons on mobile?

  • 10x10px
  • 24x24px
  • 44x44px
  • 100x100px

Answer: 44x44px. The lesson recommends touch-friendly buttons of at least 44x44px.

What is the recommended minimum color contrast ratio for body text?

  • 1:1
  • 2:1
  • 4.5:1
  • 10:1

Answer: 4.5:1. A contrast ratio of at least 4.5:1 for text keeps content readable and accessible.

Which declaration creates a responsive projects grid without media queries?

  • grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  • display: block;
  • float: left; width: 33%;
  • grid-template-columns: 250px 250px 250px;

Answer: grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));. auto-fit with minmax lets the project cards reflow automatically as the viewport changes.

Why should every <img> include descriptive alt text?

  • It speeds up image loading
  • It supports accessibility (screen readers) and SEO
  • It is required for the image to display
  • It changes the image size

Answer: It supports accessibility (screen readers) and SEO. alt text helps screen-reader users and search engines understand the image.

Which free hosting options does the lesson suggest for deploying a static portfolio?

  • GitHub Pages, Netlify, or Vercel
  • A paid dedicated server only
  • Email attachments
  • A local file with no hosting

Answer: GitHub Pages, Netlify, or Vercel. The lesson recommends free static hosting like GitHub Pages, Netlify, and Vercel.

Which development workflow tip does the lesson recommend?

  • Write all CSS before any HTML
  • Build the HTML structure first, then add CSS styling
  • Skip testing on real devices
  • Avoid using browser DevTools

Answer: Build the HTML structure first, then add CSS styling. The lesson advises building structure (HTML) first and then styling it with CSS.

Which pair of layout tools is recommended for a modern portfolio's layouts?

  • Tables and frames
  • Flexbox and CSS Grid
  • Only absolute positioning
  • Inline-block hacks only

Answer: Flexbox and CSS Grid. Flexbox (one-dimensional) and Grid (two-dimensional) are the modern layout tools to combine.

Continue this course