Build a Product Landing Page
Build a responsive product landing page with HTML, CSS, and Flexbox.
Introduction
30 Second Summary
Every product you have ever signed up for started with a landing page. It is the single page that grabs your attention, explains the value, and convinces you to click that "Get Started" button.
In this project, you will build a complete product landing page for a fictional app called FocusFlow. You'll write the HTML structure from scratch and style it with modern CSS techniques.
What You'll Build
A professional-looking landing page with a sticky navigation bar, a bold hero section, a three-card features grid, and a clean footer.
By the end of this project, you'll have:
- A complete HTML page with semantic elements like header, nav, section, and footer.
- A CSS file using custom properties, Flexbox, CSS Grid, and a responsive media query.
- A fully responsive website that looks great on both desktop and mobile screens.
- Secret Mission: Add a testimonials section with customer quotes styled using Flexbox.
Are there any prerequisites?
None at all. This guide includes full setup instructions for every tool you need. You do not need any prior coding experience to get started.
Build the HTML Structure
Your tools are set up and your project folder is ready. Now it's time to write the actual content of your landing page.
Every webpage starts with HTML, which defines the structure and content that visitors see. You'll use semantic elements that describe what each section of the page means. Think of it as building the skeleton before adding any visual style.
In this step, get ready to:
- Write the HTML boilerplate and navigation bar.
- Build the hero section with a headline and call-to-action button.
- Add the features grid and footer.
Set up the HTML document and navigation
Every HTML file needs a boilerplate structure that tells the browser what language the page uses, how to display it on different devices, and where to find the stylesheet. On top of that, you'll build the navigation bar that visitors use to jump around the page.
- In the VS Code Explorer sidebar on the left, double-click index.html to open it in the editor.
- Paste the following code into index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FocusFlow - Stay Productive</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="header">
<nav class="nav container">
<a href="#" class="nav-brand">FocusFlow</a>
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="#pricing">Pricing</a></li>
<li><a href="#" class="btn btn-small">Get Started</a></li>
</ul>
</nav>
</header>
</body>
</html>
What does this code do?
The <!DOCTYPE html> declaration tells the browser this is a modern HTML5 document. The <meta name="viewport"> tag ensures the page scales correctly on phones and tablets.
The <link rel="stylesheet" href="style.css"> connects your CSS file so styles will apply once you write them. The <header> and <nav> elements are semantic HTML tags that tell browsers and screen readers this section is the page navigation.
- Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- You won't see a visual change in the browser yet because there's no CSS styling. The next chunk adds more content to the page.
Seeing red squiggly lines?
If VS Code shows a warning or red underline, check that every opening tag has a matching closing tag. Common mistakes include missing the closing </ul> or </nav> tag.
Help me fix a syntax error in my HTML boilerplate
Create the hero section
A hero section is the large, attention-grabbing area at the top of a landing page. It typically contains a bold headline, a short description, and a call-to-action button that invites visitors to take the next step.
- In index.html, find the closing </header> tag you just wrote.
- Add the following code directly below </header> (before </body>):
<section class="hero">
<div class="hero-content container">
<h1>Stay focused. Get more done.</h1>
<p>FocusFlow helps you block distractions, track your time, and build better work habits. Simple, beautiful, and effective.</p>
<a href="#" class="btn">Start Free Trial</a>
</div>
</section>
What does this code do?
The <section> element groups related content together. Inside it, a <div> with the class hero-content wraps the headline, description, and button so you can style them as a unit later.
The <a> link with class btn will be styled to look like a button using CSS in a later step. For now it's just a regular link.
- Save index.html.
- You won't see styled results yet because the CSS file is still empty. The next chunk completes the page content.
Code not saving properly?
Make sure you pasted the hero section code between the closing </header> tag and the closing </body> tag. If it's inside the <header> or outside the <body>, the page structure will break.
Help me figure out where to paste my hero section code
Add the features section and footer
The features section showcases what FocusFlow offers using three cards arranged in a grid. The footer closes out the page with copyright information.
- In index.html, find the closing </section> tag of the hero section you just added.
- Add the following code directly below that </section> tag (before </body>):
<section class="features" id="features">
<div class="container">
<h2 class="section-title">Why teams love FocusFlow</h2>
<div class="features-grid">
<div class="card">
<span class="card-icon">⚡</span>
<h3>Lightning Fast</h3>
<p>No loading screens. No lag. FocusFlow runs instantly so you never lose momentum.</p>
</div>
<div class="card">
<span class="card-icon">🔒</span>
<h3>Distraction Blocker</h3>
<p>Block distracting websites and apps during focus sessions. Stay in the zone.</p>
</div>
<div class="card">
<span class="card-icon">📊</span>
<h3>Smart Analytics</h3>
<p>See where your time goes with beautiful charts. Identify patterns and improve daily.</p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<p>© 2026 FocusFlow. Built with HTML & CSS.</p>
</div>
</footer>
What does this code do?
The features-grid div wraps three card divs. Each card has an emoji icon, a title, and a short description. You'll use CSS Grid later to arrange these cards in a responsive three-column layout.
The id="features" attribute on the section matches the href="#features" link in your navigation. When a visitor clicks "Features" in the nav, the browser scrolls to this section automatically.
The <footer> element uses the special characters © (copyright symbol) and & (ampersand) because these have special meaning in HTML and need to be escaped.
- Save index.html.
- Right-click index.html in the Explorer sidebar and select Open with Five Server.
- Your browser opens and you should see all your page content displayed as plain, unstyled text. You'll see the FocusFlow brand name, navigation links, the hero headline, feature card content, and the footer copyright.
Page is blank or only shows partial content?
Check that every opening tag has a matching closing tag. A missing </div> or </section> can hide everything below it. Count your opening and closing tags carefully for the features section.
Also verify that the hero section and features section are siblings inside <body>, not accidentally nested inside each other.
Help me debug why my HTML page is blank or missing content
✔️ Awesome, I've got everything!
Great. Double check you've saved index.html and that you can see all the content in your browser.
ⓧ I'd like to double check the full code
Here's the complete index.html file. Make sure yours matches exactly:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FocusFlow - Stay Productive</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="header">
<nav class="nav container">
<a href="#" class="nav-brand">FocusFlow</a>
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="#pricing">Pricing</a></li>
<li><a href="#" class="btn btn-small">Get Started</a></li>
</ul>
</nav>
</header>
<section class="hero">
<div class="hero-content container">
<h1>Stay focused. Get more done.</h1>
<p>FocusFlow helps you block distractions, track your time, and build better work habits. Simple, beautiful, and effective.</p>
<a href="#" class="btn">Start Free Trial</a>
</div>
</section>
<section class="features" id="features">
<div class="container">
<h2 class="section-title">Why teams love FocusFlow</h2>
<div class="features-grid">
<div class="card">
<span class="card-icon">⚡</span>
<h3>Lightning Fast</h3>
<p>No loading screens. No lag. FocusFlow runs instantly so you never lose momentum.</p>
</div>
<div class="card">
<span class="card-icon">🔒</span>
<h3>Distraction Blocker</h3>
<p>Block distracting websites and apps during focus sessions. Stay in the zone.</p>
</div>
<div class="card">
<span class="card-icon">📊</span>
<h3>Smart Analytics</h3>
<p>See where your time goes with beautiful charts. Identify patterns and improve daily.</p>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
<p>© 2026 FocusFlow. Built with HTML & CSS.</p>
</div>
</footer>
</body>
</html>
Your page has all its content in place. Next up, you'll bring it to life with CSS styling.
Style the Page Foundation
Your HTML structure is complete. Every section of the FocusFlow landing page exists in the document, but it still looks like a plain text outline.
CSS transforms that raw structure into a polished design. In this step, you will build the visual foundation: resetting browser defaults, defining a reusable color system, and styling the navigation and hero section with Flexbox.
In this step, get ready to:
- Write a CSS reset and define custom properties for your design system.
- Style the navigation bar with Flexbox for horizontal layout.
- Style the hero section and call-to-action button.
Write the CSS reset and custom properties
Browsers apply their own default styles to HTML elements. Headings get margins, lists get padding, and every element uses a sizing model that makes layout math confusing. A CSS reset overrides these defaults so you start from a clean slate.
- Switch to style.css in VS Code (it should be empty) and add the global reset and CSS custom properties by pasting this code:
/* Reset and Custom Properties */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-primary: #6366f1;
--color-primary-dark: #4f46e5;
--color-text: #1e293b;
--color-text-muted: #64748b;
--color-bg: #ffffff;
--color-bg-alt: #f8fafc;
--color-border: #e2e8f0;
--font-family: system-ui, -apple-system, sans-serif;
--radius: 12px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
What does this code do?
The *, *::before, *::after selector targets every element on the page. Setting box-sizing: border-box means padding and borders are included inside an element's width, making layout math predictable.
The :root block defines CSS custom properties (variables) that you can reuse throughout the stylesheet. Instead of repeating #6366f1 everywhere, you write var(--color-primary) once and change it in one place if needed.
- Below the :root block, add the base document styles:
body {
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-bg);
line-height: 1.6;
}
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
What does this code do?
The body rule applies your custom properties to the whole page. The line-height: 1.6 adds breathing room between lines of text.
The .container class centers content with margin: 0 auto and caps it at 1100px wide. This prevents text from stretching edge-to-edge on wide screens.
- Save style.css.
- Open index.html in your browser by double-clicking the file in Finder (macOS) or File Explorer (Windows).
- You should see all text styled in a clean sans-serif font with no gaps around the page edges.
Page still looks unstyled?
- Make sure style.css is saved in the same folder as index.html. The <link> tag in your HTML references style.css by filename, so they must be side by side.
- Check for typos in the filename. The link says href="style.css" so your file must be named exactly style.css (not styles.css or Style.css).
Why isn't my CSS file linking to the HTML?
Style the navigation with Flexbox
The navigation bar needs to display the brand name on the left and the links on the right, in a single horizontal row. Flexbox makes this layout trivial with a single property.
- Below the .container rule in style.css, add the header and nav layout styles:
/* Navigation */
.header {
position: sticky;
top: 0;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
z-index: 100;
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
}
What does this code do?
The position: sticky with top: 0 keeps the header pinned to the top of the viewport as the user scrolls. The z-index: 100 ensures it stays above other content.
On .nav, display: flex arranges children in a row. The justify-content: space-between pushes the brand name to the far left and the links to the far right.
- Below the .nav rule, add the styles for the brand and navigation links:
.nav-brand {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
}
.nav-links {
display: flex;
align-items: center;
gap: 32px;
list-style: none;
}
.nav-links a {
color: var(--color-text-muted);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--color-text);
}
What does this code do?
The .nav-brand rule gives "FocusFlow" a bold purple appearance using your --color-primary variable.
The .nav-links list also uses display: flex to lay its items in a row. The gap: 32px adds even spacing between each link. The transition: color 0.2s creates a smooth fade when hovering over links.
- Save style.css and refresh your browser.
- You should see "FocusFlow" in purple on the left with the navigation links aligned horizontally to the right. The nav bar has a subtle bottom border.
Links not aligned horizontally?
- Confirm your HTML uses class="nav-links" on the <ul> element. The display: flex rule targets that class name exactly.
- Check that the .nav rule is not nested inside another selector. It should be at the top level of your CSS.
Why are my Flexbox items not laying out in a row?
Style the hero section
The hero section is the first thing visitors see below the nav. It needs a bold heading, centered layout, and a call-to-action button that invites clicks.
- Below the .nav-links a:hover rule, add the button component styles:
/* Buttons */
.btn {
display: inline-block;
background: var(--color-primary);
color: #ffffff;
padding: 12px 28px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.2s, transform 0.2s;
}
.btn:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
}
.btn-small {
padding: 8px 20px;
font-size: 0.9rem;
}
What does this code do?
The .btn class turns a plain <a> link into a styled button with a purple background and rounded corners. The transition property animates color and position changes smoothly.
On hover, transform: translateY(-2px) lifts the button up slightly, giving a tactile "press me" feel. The .btn-small variation reduces padding for the smaller "Get Started" button in the nav.
- Below the .btn-small rule, add the hero section layout:
/* Hero Section */
.hero {
padding: 80px 0;
text-align: center;
background: var(--color-bg-alt);
}
.hero-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 700px;
margin: 0 auto;
}
.hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.2;
color: var(--color-text);
}
.hero p {
font-size: 1.15rem;
color: var(--color-text-muted);
max-width: 540px;
}
What does this code do?
The .hero-content uses Flexbox with flex-direction: column to stack the heading, paragraph, and button vertically. The align-items: center centers everything horizontally within that column.
The clamp(2rem, 5vw, 3.5rem) function makes the heading fluid. It scales smoothly between 2rem on small screens and 3.5rem on large screens, so you get responsive typography without a media query.
- Save style.css and refresh your browser.
- You should see the hero section with centered text on a light gray background, a large heading, a muted paragraph, and a purple "Start Free Trial" button. Hover over the button to see it lift up.
Hero text not centered?
- Make sure your HTML has class="hero-content container" on the <div> inside the hero section. Both classes are needed for centering to work.
- Verify that flex-direction: column is spelled correctly. Without it, Flexbox defaults to a row layout and your content will sit side by side.
Why is my hero section not centering properly?
✔️ Awesome, I've got everything!
Great. Double check you have saved style.css before moving on.
ⓧ I'd like to double check the full code
Here is the complete style.css so far. Compare yours against it:
/* Reset and Custom Properties */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-primary: #6366f1;
--color-primary-dark: #4f46e5;
--color-text: #1e293b;
--color-text-muted: #64748b;
--color-bg: #ffffff;
--color-bg-alt: #f8fafc;
--color-border: #e2e8f0;
--font-family: system-ui, -apple-system, sans-serif;
--radius: 12px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
body {
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-bg);
line-height: 1.6;
}
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
/* Navigation */
.header {
position: sticky;
top: 0;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
z-index: 100;
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
}
.nav-brand {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
}
.nav-links {
display: flex;
align-items: center;
gap: 32px;
list-style: none;
}
.nav-links a {
color: var(--color-text-muted);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--color-text);
}
/* Buttons */
.btn {
display: inline-block;
background: var(--color-primary);
color: #ffffff;
padding: 12px 28px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.2s, transform 0.2s;
}
.btn:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
}
.btn-small {
padding: 8px 20px;
font-size: 0.9rem;
}
/* Hero Section */
.hero {
padding: 80px 0;
text-align: center;
background: var(--color-bg-alt);
}
.hero-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 700px;
margin: 0 auto;
}
.hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.2;
color: var(--color-text);
}
.hero p {
font-size: 1.15rem;
color: var(--color-text-muted);
max-width: 540px;
}
Your page already looks like a real product site. Next up, you will build a responsive features grid that showcases FocusFlow's three key selling points.
Build the Features Grid
Your hero section looks polished. Now it's time to tackle the most interesting layout challenge on the page: the features grid.
The features section needs to display three cards side-by-side on wide screens, then automatically stack into fewer columns as the screen gets narrower. CSS Grid makes this possible with a single line of code.
In this step, get ready to:
- Create a responsive grid layout that adapts to any screen size.
- Style interactive feature cards with hover effects.
- Style the footer and preview your page live in the browser.
Create the grid layout
Your features section HTML already has three cards inside a .features-grid container. Right now they stack vertically because div elements are block-level by default.
CSS Grid lets you turn that container into a multi-column layout. The auto-fit keyword combined with minmax() tells the browser to fit as many columns as possible while keeping each card at least 280px wide.
- In style.css, add the following CSS below your hero styles:
/* Features Section */
.features {
padding: 80px 0;
}
.section-title {
text-align: center;
font-size: 2rem;
margin-bottom: 48px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 32px;
}
What does this code do?
.features adds vertical padding to give the section breathing room.
.section-title centers the heading text and pushes it away from the cards below.
.features-grid is where the magic happens. display: grid turns the container into a grid. repeat(auto-fit, minmax(280px, 1fr)) creates as many columns as will fit, with each column at least 280px wide. On wider screens you get three columns. On narrow screens the cards automatically wrap into fewer columns.
- Save style.css.
- Refresh your browser. You should see the three feature cards arranged side-by-side in a row instead of stacked vertically.
Cards still stacking vertically?
Make sure you typed .features-grid (with the hyphen) to match the class="features-grid" in your HTML.
Check that display: grid and grid-template-columns are both inside the .features-grid selector block, not accidentally placed in .features.
Why aren't my feature cards arranging into columns?
Style the feature cards
The cards are in a grid now, but they look plain. Each card needs a border, padding, and a subtle hover effect that makes it feel interactive when the user moves their mouse over it.
- Add the following CSS below your .features-grid styles:
.card {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 32px;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.card-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 16px;
}
.card h3 {
font-size: 1.25rem;
margin-bottom: 12px;
}
.card p {
color: var(--color-text-muted);
font-size: 0.95rem;
}
What does this code do?
.card uses the CSS custom properties you defined earlier (--color-border, --radius) to give each card a rounded border and padding. The transition property makes the hover animation smooth instead of instant.
.card:hover lifts the card up by 4 pixels and adds a shadow when the user hovers over it. This gives the page a polished, interactive feel.
The remaining selectors style the emoji icon, heading, and description text inside each card.
- Save style.css and refresh your browser.
- You should see three neat cards with borders and rounded corners. Hover over a card to see it lift up slightly with a shadow appearing beneath it.
Hover effect not working?
Make sure .card:hover is directly below your .card rule and uses a colon between card and hover (not a period or space).
Check that the transition line is inside .card, not .card:hover. The transition must be on the base state so the animation works in both directions.
Why isn't my card hover animation appearing?
Style the footer and preview your page
The footer is the final unstyled section. A dark background with light text creates a clear visual endpoint for the page.
- Add the following CSS below your card styles:
/* Footer */
.footer {
background: var(--color-text);
color: var(--color-bg-alt);
text-align: center;
padding: 32px 0;
}
What does this code do?
The footer uses --color-text (a dark navy) as its background and --color-bg-alt (a light off-white) for the text. This inverted color scheme creates a strong visual anchor at the bottom of the page.
- Save style.css.
- Preview your page by right-clicking index.html in the VS Code Explorer sidebar and selecting Open with Five Server.
- Your browser opens with the full page. You should see a dark footer at the bottom with light copyright text.
What is Five Server doing?
Five Server (the Live Server extension you installed) opens your page in the browser and automatically reloads it every time you save a file. From now on, you can just save your CSS and see changes instantly.
Don't see Open with Five Server?
Make sure you right-clicked the index.html file in the Explorer panel on the left side of VS Code, not inside the code editor.
If the option doesn't appear, confirm the Live Server (Five Server) extension is installed and enabled. Open the Extensions panel with Ctrl+Shift+X (Windows) or Cmd+Shift+X (macOS) and search for "Five Server" to verify.
Why can't I see the Five Server option in VS Code?
✔️ Awesome, I've got everything!
Great. Double check you've saved style.css before moving on.
ⓧ I'd like to double check the full code
Here is the complete style.css file up to this point:
/* Reset and Custom Properties */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-primary: #6366f1;
--color-primary-dark: #4f46e5;
--color-text: #1e293b;
--color-text-muted: #64748b;
--color-bg: #ffffff;
--color-bg-alt: #f8fafc;
--color-border: #e2e8f0;
--font-family: system-ui, -apple-system, sans-serif;
--radius: 12px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
body {
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-bg);
line-height: 1.6;
}
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
/* Navigation */
.header {
position: sticky;
top: 0;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
z-index: 100;
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
}
.nav-brand {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
}
.nav-links {
display: flex;
align-items: center;
gap: 32px;
list-style: none;
}
.nav-links a {
color: var(--color-text-muted);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--color-text);
}
/* Buttons */
.btn {
display: inline-block;
background: var(--color-primary);
color: #ffffff;
padding: 12px 28px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.2s, transform 0.2s;
}
.btn:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
}
.btn-small {
padding: 8px 20px;
font-size: 0.9rem;
}
/* Hero Section */
.hero {
padding: 80px 0;
text-align: center;
background: var(--color-bg-alt);
}
.hero-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 700px;
margin: 0 auto;
}
.hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.2;
color: var(--color-text);
}
.hero p {
font-size: 1.15rem;
color: var(--color-text-muted);
max-width: 540px;
}
/* Features Section */
.features {
padding: 80px 0;
}
.section-title {
text-align: center;
font-size: 2rem;
margin-bottom: 48px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 32px;
}
.card {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 32px;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.card-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 16px;
}
.card h3 {
font-size: 1.25rem;
margin-bottom: 12px;
}
.card p {
color: var(--color-text-muted);
font-size: 0.95rem;
}
/* Footer */
.footer {
background: var(--color-text);
color: var(--color-bg-alt);
text-align: center;
padding: 32px 0;
}
Your page now has a fully styled features grid and footer. Next up, you'll make everything responsive so it looks great on mobile devices too.
Make It Responsive
Your landing page looks great on a full-size desktop browser. However, most people browse the web on their phones.
A media query lets you write CSS rules that only apply when the browser window is a certain size. In this step, you will add a media query that adjusts your layout for mobile screens.
In this step, get ready to:
- Add a media query that hides the navigation links and adjusts spacing on mobile.
- Reduce typography sizes so text fits comfortably on small screens.
- Test your page at different screen sizes using Chrome DevTools.
Add a media query for mobile
A media query wraps CSS rules that only kick in when the screen meets a condition. You will target screens narrower than 768px, which covers most phones and small tablets.
- In style.css, scroll to the very bottom of the file (below the .footer rule).
- Add the following media query by pasting this code at the end of the file:
/* Responsive */
@media (max-width: 768px) {
.nav-links {
display: none;
}
.hero {
padding: 48px 0;
}
}
What does this code do?
Everything inside the @media (max-width: 768px) block only applies when the browser window is 768 pixels wide or narrower.
- display: none on .nav-links hides the navigation links entirely on mobile. On a real production site you would replace these with a hamburger menu, but hiding them keeps this project focused on layout fundamentals.
- The hero padding drops from 80px to 48px so the section does not take up too much vertical space on a small screen.
- Save style.css.
- Resize your browser window to be very narrow (about the width of a phone). The navigation links should disappear, leaving only the FocusFlow brand name.
Navigation links still showing?
- Make sure the @media block is placed after all your other CSS rules. If it appears above the .nav-links rule, the later rule overrides it.
- Check that your media query opening brace { and closing brace } are both present. A missing brace breaks all styles inside the block.
Why are my nav links still visible on a narrow screen?
Adjust typography and spacing
The hero heading and features section still use their desktop sizes on mobile. Reducing them prevents the text from feeling oversized and the sections from having too much empty space.
- In style.css, find the closing brace } of the .hero rule inside your media query.
- Add the following rules below it, but still inside the @media block (before its final closing brace):
.hero h1 {
font-size: clamp(1.75rem, 6vw, 2.5rem);
}
.features {
padding: 48px 0;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 32px;
}
What does this code do?
- The .hero h1 rule uses a smaller clamp() range so the heading scales down gracefully on narrow screens instead of overflowing.
- The .features padding drops from 80px to 48px to reduce white space on mobile.
- The .section-title shrinks from 2rem to 1.5rem and its bottom margin tightens to keep the card grid closer to the heading.
- Save style.css.
- With your browser still narrow, the hero heading should look noticeably smaller and the features section should feel less spaced out.
Styles not changing on mobile?
- Make sure the new rules are inside the @media (max-width: 768px) { ... } block, not outside it. Code placed after the media query's closing brace runs at all screen sizes.
- Count your curly braces. The media query itself has one opening and one closing brace. Each selector inside also has its own pair.
Help me figure out why my mobile styles are not applying inside my media query.
Test responsiveness in Chrome DevTools
Manually resizing your browser works, but Chrome DevTools has a dedicated device emulator that lets you preview exact phone and tablet sizes.
- In Chrome, press F12 to open DevTools.
- Click the device toggle toolbar icon in the top-left corner of the DevTools panel (or press Ctrl+Shift+M on Windows / Cmd+Shift+M on macOS).
- Select a device like iPhone 14 Pro from the dropdown at the top, or drag the viewport handles to try different widths.
- Verify that the features grid stacks into a single column on narrow screens.
Why does the grid stack without a media query rule?
Your .features-grid uses grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)). This tells the browser each column must be at least 280px wide. When the screen is too narrow to fit two 280px columns side by side, the grid automatically wraps them into a single column.
This is called an intrinsically responsive pattern. The layout adapts without needing an explicit media query rule for the grid.
- Try a few different screen sizes (phone, tablet, wide desktop) and confirm your page looks good at each one.
- Close DevTools by pressing F12 again.
Page looks zoomed in or overflows?
- Open index.html and confirm you have the viewport meta tag: <meta name="viewport" content="width=device-width, initial-scale=1.0">. Without this tag, mobile browsers render the page at desktop width and zoom out.
- Make sure your .container class has padding: 0 24px. This prevents content from touching the screen edges.
My page overflows or looks wrong in Chrome DevTools device mode. Help me fix it.
✔️ Awesome, I've got everything!
Great. Double check you have saved style.css and your page responds correctly at different widths.
ⓧ I'd like to double check the full code
Here is the complete style.css file at the end of this step:
/* Reset and Custom Properties */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-primary: #6366f1;
--color-primary-dark: #4f46e5;
--color-text: #1e293b;
--color-text-muted: #64748b;
--color-bg: #ffffff;
--color-bg-alt: #f8fafc;
--color-border: #e2e8f0;
--font-family: system-ui, -apple-system, sans-serif;
--radius: 12px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
body {
font-family: var(--font-family);
color: var(--color-text);
background: var(--color-bg);
line-height: 1.6;
}
.container {
max-width: 1100px;
margin: 0 auto;
padding: 0 24px;
}
/* Navigation */
.header {
position: sticky;
top: 0;
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
z-index: 100;
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
height: 64px;
}
.nav-brand {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-primary);
text-decoration: none;
}
.nav-links {
display: flex;
align-items: center;
gap: 32px;
list-style: none;
}
.nav-links a {
color: var(--color-text-muted);
text-decoration: none;
font-size: 0.95rem;
transition: color 0.2s;
}
.nav-links a:hover {
color: var(--color-text);
}
/* Buttons */
.btn {
display: inline-block;
background: var(--color-primary);
color: #ffffff;
padding: 12px 28px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background 0.2s, transform 0.2s;
}
.btn:hover {
background: var(--color-primary-dark);
transform: translateY(-2px);
}
.btn-small {
padding: 8px 20px;
font-size: 0.9rem;
}
/* Hero Section */
.hero {
padding: 80px 0;
text-align: center;
background: var(--color-bg-alt);
}
.hero-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
max-width: 700px;
margin: 0 auto;
}
.hero h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
line-height: 1.2;
color: var(--color-text);
}
.hero p {
font-size: 1.15rem;
color: var(--color-text-muted);
max-width: 540px;
}
/* Features Section */
.features {
padding: 80px 0;
}
.section-title {
text-align: center;
font-size: 2rem;
margin-bottom: 48px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 32px;
}
.card {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 32px;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow);
}
.card-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 16px;
}
.card h3 {
font-size: 1.25rem;
margin-bottom: 12px;
}
.card p {
color: var(--color-text-muted);
font-size: 0.95rem;
}
/* Footer */
.footer {
background: var(--color-text);
color: var(--color-bg-alt);
text-align: center;
padding: 32px 0;
}
/* Responsive */
@media (max-width: 768px) {
.nav-links {
display: none;
}
.hero {
padding: 48px 0;
}
.hero h1 {
font-size: clamp(1.75rem, 6vw, 2.5rem);
}
.features {
padding: 48px 0;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 32px;
}
}
Your index.html file is unchanged in this step.
Your landing page now adapts gracefully from wide desktops down to phone-sized screens. You have built a complete, responsive product landing page from scratch using only HTML and CSS.
Secret mission
Add a Testimonials Section
Your landing page looks great, but real products need social proof. Can you add a testimonials section that shows what users think of FocusFlow? You will use Flexbox to lay out quote cards in a row that stacks on mobile.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- The focusflow-landing project folder on your Desktop.
- Live Server (Five Server) VS Code extension.
- Visual Studio Code.
Keep everything running
No action needed. Choose this if you are still actively building or want to experiment more with your landing page.
- Your project files stay on your Desktop ready to open any time.
- VS Code and the Live Server extension remain installed for future web projects.
Pause - I'll come back to this later
Stop the live server to free up the port, but keep your files and tools installed.
- If Five Server is still running, click the Go Live button in the VS Code status bar to stop it.
- Close the browser tab showing your landing page.
- Your project files remain on your Desktop. Open the folder in VS Code whenever you want to continue.
Delete - I don't want to use this again
Remove all project files and extensions installed for this project.
- Delete the project folder. On macOS/Linux, run:
rm -rf ~/Desktop/focusflow-landing
- On Windows, run:
rmdir /s /q %USERPROFILE%\Desktop\focusflow-landing
- To remove the Live Server extension, press Ctrl+Shift+X to open the Extensions panel in VS Code. Find Live Server (Five Server) in your installed list and click Uninstall.
- To uninstall VS Code on Windows, open Settings, select Apps, then Apps & features. Find Visual Studio Code in the list and click Uninstall.
- To uninstall VS Code on macOS, open Finder, go to Applications, and drag Visual Studio Code to the Trash.
That's a Wrap!
That's a Wrap!
Nice work! You just built a complete, responsive product landing page from scratch using only HTML and CSS.
You've learned how to:
- Structured a full webpage with semantic HTML elements like header, nav, section, and footer.
- Built a consistent design system with CSS custom properties and laid out navigation using Flexbox.
- Created a responsive card grid with CSS Grid and made the page mobile-friendly with a media query and interactive hover transitions.
- Secret Mission: Added a testimonials section with Flexbox layout that stacks responsively on mobile.
Ready to quiz yourself?