Build a Responsive Tech Agency Page

Build a multi-section landing page using Flexbox, CSS Grid, and media queries.

Introduction

30 Second Summary

Every website you visit looks different on your phone than it does on your laptop. That layout shift is not magic. It is CSS layout and responsive design working together behind the scenes.

In this project, you will build a tech agency landing page that transforms across mobile, tablet, and desktop screen sizes using Flexbox, CSS Grid, and media queries.

What You'll Build

A multi-section landing page with a navigation bar, hero section, services card grid, contact form, and footer that adapts its layout at different screen widths.

By the end of this project, you'll have:

  • A fully responsive landing page that looks polished from 320px to 1440px+ screen widths.
  • Hands-on experience choosing between Flexbox (one-dimensional) and CSS Grid (two-dimensional) for different layout problems.
  • A working mental model of mobile-first design using min-width media queries that progressively enhance your layout.
  • Secret Mission: Add a team section with an auto-responsive grid that reflows without any media queries.

Are there any prerequisites?

You should be comfortable with basic HTML and CSS concepts like the box model, selectors, typography, and colors. No prior experience with Flexbox, Grid, or responsive design is needed.

Set Up the Project and HTML Structure

Before you write a single line of CSS, you need a solid HTML foundation. Every section of your landing page needs semantic structure that your stylesheet can target later.

In this step, you'll create your project files, write the HTML boilerplate with a critical responsive design meta tag, and build out all the page sections. By the end, you'll have a complete (but unstyled) page ready for layout work.

In this step, get ready to:
  • Create the tech-agency project folder with index.html and style.css.
  • Write the complete HTML structure with all page sections.
  • Preview the page in Chrome DevTools at mobile width.
Create the project folder and files

Every web project starts with a folder to hold your files. You'll create the project on your Desktop so it's easy to find.

  • Open the integrated terminal in Cursor by pressing Cmd+` (macOS) or Ctrl+` (Windows).
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir tech-agency
cd tech-agency
  • Create the two files your project needs by running:
touch index.html style.css
  • Confirm the files exist by running:
ls

You should see index.html and style.css in the output.

Don't see both files?

Make sure you ran the touch command from inside the tech-agency folder. Run pwd to check your current location. It should end with tech-agency.

help me find my project files

  • Open the tech-agency folder in Cursor by clicking File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the tech-agency folder, and click Open.

You should see index.html and style.css listed in the Explorer sidebar on the left.

Write the HTML boilerplate

The boilerplate includes one tag that is essential for responsive design to work on mobile devices. Without it, phones ignore your CSS layout entirely.

  • Click on index.html in the Explorer sidebar to open it in the editor.
  • Write the HTML5 boilerplate with the viewport meta tag by pasting this code:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TechFlow Agency</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

</body>
</html>

What does the viewport meta tag do?

The line <meta name="viewport" content="width=device-width, initial-scale=1.0"> tells mobile browsers to use the actual device width instead of defaulting to a wider viewport (like 980px on iPhones).

Without it, your phone renders the page as if the screen is 980px wide, then zooms out to fit. Your media queries would never trigger because the browser thinks it has plenty of space.

This single tag is what makes responsive design possible on mobile.

  • Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).

You won't see a visual change yet because the body is empty. You'll add page content next.

Write the page structure

Your landing page has five distinct sections: a navigation header, a hero banner, a services grid, a contact form, and a footer. Each section gets its own semantic HTML element with class names that CSS will target later.

  • In index.html, place your cursor between the opening <body> and closing </body> tags.
  • Add the header and hero sections by pasting the following code:
    <header class="header">
        <nav class="nav">
            <div class="nav-logo">TechFlow</div>
            <ul class="nav-links">
                <li><a href="#services">Services</a></li>
                <li><a href="#contact">Contact</a></li>
            </ul>
        </nav>
    </header>

    <section class="hero">
        <div class="hero-content">
            <h1>We Build Digital Experiences</h1>
            <p>From concept to launch, we create websites and apps that help businesses grow in the digital world.</p>
            <a href="#contact" class="btn">Get Started</a>
        </div>
    </section>

What does this code do?

The <header> contains a <nav> with two children: a logo div and an unordered list of navigation links. These sit side by side once you apply Flexbox in a later step.

The .hero section wraps its content in a .hero-content div. This wrapper gives you a target to constrain the text width on large screens without affecting the full-width background color.

  • Save index.html.
  • Open the file in Google Chrome by double-clicking index.html in Finder (macOS) or File Explorer (Windows) from your Desktop's tech-agency folder.

You should see "TechFlow", the nav links, the heading "We Build Digital Experiences", and a "Get Started" link as plain unstyled text.

See a blank page?

Make sure you pasted the code between the <body> and </body> tags. If you pasted it above <body> or below </body>, the browser won't display it.

help me fix my blank HTML page

  • Below the closing </section> tag of the hero, add the services section, contact form, and footer by pasting this code:
    <section class="services" id="services">
        <h2>Our Services</h2>
        <div class="services-grid">
            <div class="service-card">
                <h3>Web Design</h3>
                <p>Beautiful, responsive websites that look great on every device and screen size.</p>
            </div>
            <div class="service-card">
                <h3>Development</h3>
                <p>Clean, modern code built with the latest technologies and best practices.</p>
            </div>
            <div class="service-card">
                <h3>SEO Strategy</h3>
                <p>Data-driven strategies to improve your search rankings and drive organic traffic.</p>
            </div>
        </div>
    </section>

    <section class="contact" id="contact">
        <h2>Get In Touch</h2>
        <form class="contact-form">
            <input type="text" placeholder="Your Name" required>
            <input type="email" placeholder="Your Email" required>
            <textarea placeholder="Your Message" rows="5" required></textarea>
            <button type="submit" class="btn">Send Message</button>
        </form>
    </section>

    <footer class="footer">
        <p>2026 TechFlow Agency. All rights reserved.</p>
    </footer>

What does this code do?

The .services section contains a .services-grid wrapper div with three .service-card children. This wrapper becomes a CSS Grid container in Step 4, controlling how the cards arrange into columns.

The .contact-form uses standard form elements (inputs, textarea, button). You'll apply Flexbox in column direction to space them evenly.

The id attributes (id="services", id="contact") match the href values in the nav links, enabling smooth scroll navigation.

  • Save index.html and refresh your Chrome tab.

You should see all your page content flowing top to bottom as plain unstyled text: the nav links, hero heading, three service descriptions, the form fields, and the footer text.

Missing some sections?

Check that you pasted the second code block directly below the hero's closing </section> tag. If sections overlap or nest inside each other, you'll see jumbled content.

Also confirm that the </body> and </html> closing tags are still at the very bottom of your file, after the footer.

help me fix my HTML structure

✔️ Awesome, I've got everything!

Great. Double check you've saved index.html before moving on.

ⓧ I'd like to double check the full code

Here is 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>TechFlow Agency</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header class="header">
        <nav class="nav">
            <div class="nav-logo">TechFlow</div>
            <ul class="nav-links">
                <li><a href="#services">Services</a></li>
                <li><a href="#contact">Contact</a></li>
            </ul>
        </nav>
    </header>

    <section class="hero">
        <div class="hero-content">
            <h1>We Build Digital Experiences</h1>
            <p>From concept to launch, we create websites and apps that help businesses grow in the digital world.</p>
            <a href="#contact" class="btn">Get Started</a>
        </div>
    </section>

    <section class="services" id="services">
        <h2>Our Services</h2>
        <div class="services-grid">
            <div class="service-card">
                <h3>Web Design</h3>
                <p>Beautiful, responsive websites that look great on every device and screen size.</p>
            </div>
            <div class="service-card">
                <h3>Development</h3>
                <p>Clean, modern code built with the latest technologies and best practices.</p>
            </div>
            <div class="service-card">
                <h3>SEO Strategy</h3>
                <p>Data-driven strategies to improve your search rankings and drive organic traffic.</p>
            </div>
        </div>
    </section>

    <section class="contact" id="contact">
        <h2>Get In Touch</h2>
        <form class="contact-form">
            <input type="text" placeholder="Your Name" required>
            <input type="email" placeholder="Your Email" required>
            <textarea placeholder="Your Message" rows="5" required></textarea>
            <button type="submit" class="btn">Send Message</button>
        </form>
    </section>

    <footer class="footer">
        <p>2026 TechFlow Agency. All rights reserved.</p>
    </footer>
</body>
</html>
Preview the page with Chrome Device Toolbar

Chrome DevTools has a Device Toolbar that simulates different screen widths. This is how you'll test responsive layouts throughout the project.

  • In Chrome, right-click anywhere on your page and select Inspect to open DevTools.
  • Toggle the Device Toolbar by pressing Cmd+Shift+M (macOS) or Ctrl+Shift+M (Windows).
  • In the dimensions dropdown at the top of the viewport, select Responsive and set the width to 375.

You should see all your content flowing in a single column from top to bottom. Every section stacks vertically.

Why does this already look like a mobile layout?

Normal document flow gives you single-column layout for free. Block-level elements (<header>, <section>, <footer>) stack vertically by default.

This is the foundation of mobile-first design. You start with what the browser gives you (single column), then add layout techniques for larger screens. Less CSS to write, less CSS to undo.

Don't see the Device Toolbar?

Make sure DevTools is open first (right-click, then Inspect). The Cmd+Shift+M / Ctrl+Shift+M shortcut only works when DevTools has focus.

You can also click the device icon (a phone and tablet) in the top-left corner of the DevTools panel.

help me find the Device Toolbar in Chrome

Your HTML structure is locked in. Next up, you'll add mobile-first base styles that give this page proper typography, colors, and spacing.

Write Mobile-First Base Styles

Your HTML structure is complete. Right now Chrome shows raw, unstyled text flowing down the page.

In this step, you will write foundational CSS that applies at every screen size. The key insight is that mobile-first design means starting with normal document flow. Single-column layout is free. You just need good typography and spacing.

In this step, get ready to:
  • Write a CSS reset and set global typography.
  • Style each page section with background colors and spacing.
  • Verify the page looks clean at mobile width in Chrome DevTools.
Write the CSS reset and element defaults

Every browser applies its own default margins, padding, and styles to HTML elements. A CSS reset removes these inconsistencies so your design looks the same everywhere.

  • Open style.css in Cursor.
  • Add the CSS reset and element defaults by pasting this code:
/* ================================
   BASE STYLES (Mobile-First)
   Normal document flow = single column.
   No layout techniques needed here.
   ================================ */

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

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

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
}

What does this code do?

The universal selector * removes all default margins and padding from every element. box-sizing: border-box makes padding and borders count inside an element's total width instead of adding to it.

The system-ui font uses the operating system's native font without loading any external files. The img { max-width: 100% } rule prevents images from overflowing their container on small screens.

  • Save style.css.
  • Refresh your page in Chrome. The text should now look tighter with no gaps between elements. Default bullet points and link underlines are gone.

Page looks unchanged?

  • Make sure you saved style.css. Unsaved changes do not apply.
  • Check that your index.html has <link rel="stylesheet" href="style.css"> in the <head>. A missing or misspelled link means the browser cannot find your stylesheet.

Why isn't my CSS reset applying to the page?

Now add the typography scales and a reusable button class below your reset code.

  • Add the typography and button styles below the img rule by pasting this code:
/* ================================
   TYPOGRAPHY & COLORS
   ================================ */

h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
}

h2 {
    font-size: 1.75rem;
    margin-bottom: 2rem;
    text-align: center;
}

h3 {
    font-size: 1.25rem;
    margin-bottom: 0.5rem;
}

.btn {
    display: inline-block;
    background-color: #2563eb;
    color: #fff;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    border: none;
    font-size: 1rem;
    cursor: pointer;
}

.btn:hover {
    background-color: #1d4ed8;
}

What does this code do?

  • The heading sizes use rem units, which scale relative to the root font size. This keeps your text proportional across devices.
  • The .btn class creates a reusable blue button. Both the "Get Started" link and the form's "Send Message" button use this same class.
  • The .btn:hover rule darkens the button when a user hovers over it, giving visual feedback.
  • Save style.css and refresh your browser.
  • You should see a blue "Get Started" button in the hero area and properly sized headings throughout the page.

Button not showing as blue?

  • Check that your HTML uses class="btn" on both the <a> tag in the hero and the <button> tag in the form. The class name is case-sensitive.
  • Make sure there are no typos in #2563eb. A missing character in the hex code means the browser ignores the color entirely.

Why isn't my .btn class styling the button?

Style each section with colors and spacing

Each section of the landing page needs its own background color and padding to create visual separation. The dark header and hero contrast with the lighter content sections below.

  • Add the section styles below the .btn:hover rule by pasting this code:
/* ================================
   HEADER & NAVIGATION
   ================================ */

.header {
    background-color: #1e293b;
    padding: 1rem;
}

/* ================================
   HERO SECTION
   ================================ */

.hero {
    background-color: #0f172a;
    color: #fff;
    padding: 4rem 1rem;
}

.hero-content {
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   SERVICES SECTION
   ================================ */

.services {
    padding: 4rem 1rem;
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   CONTACT SECTION
   ================================ */

.contact {
    background-color: #f1f5f9;
    padding: 4rem 1rem;
}

/* ================================
   FOOTER
   ================================ */

.footer {
    background-color: #1e293b;
    color: #e2e8f0;
    text-align: center;
    padding: 2rem 1rem;
}

What does this code do?

  • The header and footer share the same dark background (#1e293b) to bookend the page. The hero uses an even darker shade (#0f172a) for extra contrast.
  • The max-width: 1200px with margin: 0 auto on content-heavy sections constrains text width on large screens. On mobile, the content naturally fills the viewport.
  • Every section uses padding: 4rem 1rem for generous vertical breathing room and a small horizontal buffer on mobile.
  • Save style.css and refresh your browser.
  • You should see distinct colored bands for each section. The header and hero are dark with white text, the services area is white, the contact section is light gray, and the footer is dark again.

Hero text invisible?

  • If you cannot see the hero heading and paragraph, check that .hero has color: #fff. Without it, the dark text sits on a dark background.
  • Make sure the class name hero in your CSS matches class="hero" in your HTML exactly.

Help me debug why my hero section text is not visible.

Verify the mobile layout in Chrome DevTools

Your page already has a complete mobile layout without any Flexbox or CSS Grid. Normal document flow stacks every element in a single column. That is exactly what mobile needs.

  • In Chrome, open the Device Toolbar by pressing Ctrl+Shift+M (Windows) or Cmd+Shift+M (macOS).
  • Set the viewport width to 375 pixels using the width field at the top of the viewport.
  • Scroll through the page from top to bottom.

You should see a clean, single-column page with proper spacing, distinct section colors, and readable text. Every section stacks vertically. This IS your mobile layout.

Why is this already a mobile layout?

HTML elements are block-level by default. They naturally stack in a single column from top to bottom. On a phone screen, that is exactly the layout you want.

Mobile-first means your base CSS handles the simplest case (single column with good typography). You only add layout complexity through media queries when the screen is wide enough to benefit from multiple columns.

✔️ Awesome, I've got everything!

Great. Double check that you have saved style.css before moving on.

ⓧ I'd like to double check the full code

Here is the full style.css file at this point. Compare yours against it:

/* ================================
   BASE STYLES (Mobile-First)
   Normal document flow = single column.
   No layout techniques needed here.
   ================================ */

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

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

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
}

/* ================================
   TYPOGRAPHY & COLORS
   ================================ */

h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
}

h2 {
    font-size: 1.75rem;
    margin-bottom: 2rem;
    text-align: center;
}

h3 {
    font-size: 1.25rem;
    margin-bottom: 0.5rem;
}

.btn {
    display: inline-block;
    background-color: #2563eb;
    color: #fff;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    border: none;
    font-size: 1rem;
    cursor: pointer;
}

.btn:hover {
    background-color: #1d4ed8;
}

/* ================================
   HEADER & NAVIGATION
   ================================ */

.header {
    background-color: #1e293b;
    padding: 1rem;
}

/* ================================
   HERO SECTION
   ================================ */

.hero {
    background-color: #0f172a;
    color: #fff;
    padding: 4rem 1rem;
}

.hero-content {
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   SERVICES SECTION
   ================================ */

.services {
    padding: 4rem 1rem;
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   CONTACT SECTION
   ================================ */

.contact {
    background-color: #f1f5f9;
    padding: 4rem 1rem;
}

/* ================================
   FOOTER
   ================================ */

.footer {
    background-color: #1e293b;
    color: #e2e8f0;
    text-align: center;
    padding: 2rem 1rem;
}

Your mobile foundation is solid. Every section has proper colors, spacing, and typography. Next up, you will use Flexbox to arrange the navigation bar and contact form into purposeful layouts.

Apply Flexbox Layouts

Your mobile base styles are looking great. The page is readable and well-spaced at small widths. Now it's time to introduce your first layout technique: Flexbox.

Right now, every element stacks vertically because that's how normal document flow works. But a navigation bar needs items side by side, and a form needs consistent spacing between its inputs. Flexbox solves both of these one-dimensional alignment problems.

In this step, get ready to:
  • Apply Flexbox to the navigation bar for horizontal alignment.
  • Apply Flexbox to the contact form for vertical spacing.
  • Test both layouts at different viewport widths in Chrome DevTools.
Add Flexbox to the navigation bar

The navigation bar has two child elements: the logo and the links list. You want the logo on the left and the links on the right, all vertically centered. This is a one-dimensional alignment problem, which is exactly what Flexbox is built for.

Why Flexbox instead of Grid here?

Flexbox distributes items along a single axis (a row or a column). Grid controls items across two axes (rows AND columns). The nav only needs items pushed to opposite ends of one row, so Flexbox is the right fit.

A good rule of thumb from MDN: if you find yourself setting widths on flex items to line them up with items above, switch to Grid instead.

  • In style.css, find the HEADER & NAVIGATION section comment.
  • Update the comment to say HEADER & NAVIGATION (Flexbox) so you remember which layout technique this section uses.
  • Add the following code below the existing .header rule to turn the nav into a flex container:
/* Turn nav into a horizontal flex container */
.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: 1200px;
    margin: 0 auto;
}

What does this code do?

  • display: flex activates Flexbox on the .nav element, making its children (logo and links) flex items.
  • justify-content: space-between pushes the first child (logo) to the left and the last child (links) to the right.
  • align-items: center vertically centers both items within the nav bar.
  • Now add the styles for the flex items (logo and links) directly below the .nav rule:
/* Logo styling */
.nav-logo {
    font-size: 1.5rem;
    font-weight: 700;
    color: #fff;
}

/* Horizontal link list using Flexbox */
.nav-links {
    display: flex;
    gap: 1.5rem;
}

.nav-links a {
    color: #e2e8f0;
}

.nav-links a:hover {
    color: #fff;
}

What does this code do?

  • .nav-logo makes the brand name large, bold, and white so it stands out against the dark header.
  • .nav-links gets its own display: flex so the list items sit in a horizontal row. The gap: 1.5rem property adds consistent spacing between each link without needing margins.
  • Save style.css and refresh your browser.
  • You should see the logo ("TechFlow") on the left and the two navigation links on the right, all in a single horizontal row against the dark header.

Links still stacking vertically?

  • Make sure you added display: flex to both .nav and .nav-links. The nav needs flex to position logo vs links, and the links list needs flex to lay out its items horizontally.
  • Check that your class names in CSS match the HTML exactly. The nav uses class="nav" and the links use class="nav-links".

Help me debug my Flexbox navigation layout

Add Flexbox to the contact form

The contact form needs its inputs stacked vertically with even spacing between them. This is still a one-dimensional problem (items flowing in one direction), but this time the axis is vertical instead of horizontal.

  • In style.css, find the CONTACT SECTION comment.
  • Update the comment to say CONTACT SECTION (Flexbox column).
  • Add the following code below the existing .contact rule to make the form a vertical flex container:
/* Flex column layout for stacked form inputs */
.contact-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 600px;
    margin: 0 auto;
}

What does this code do?

  • flex-direction: column switches the flex axis from horizontal (the default) to vertical. Items stack top to bottom.
  • gap: 1rem creates consistent 1rem spacing between each input. No need to add margins to individual elements.
  • max-width: 600px with margin: 0 auto constrains the form width and centers it horizontally, keeping it readable on wider screens.
  • Now add the input and textarea styles directly below the .contact-form rule:
/* Form input base styles */
.contact-form input,
.contact-form textarea {
    padding: 0.75rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
    font-size: 1rem;
    font-family: inherit;
}

/* Highlight the active input with brand color */
.contact-form input:focus,
.contact-form textarea:focus {
    outline: none;
    border-color: #2563eb;
}

What does this code do?

  • The base styles give each input consistent padding, a light border, and rounded corners. font-family: inherit ensures the textarea matches the body font instead of using the browser's default monospace.
  • The :focus state removes the browser's default outline and replaces it with a blue border. This gives clear visual feedback when the learner clicks into a field.
  • Save style.css and refresh your browser.
  • Scroll down to the contact section. You should see three evenly-spaced form inputs stacked vertically with a blue "Send Message" button at the bottom.
  • Click into one of the inputs. The border should turn blue to show it's focused.

Form inputs not evenly spaced?

  • Check that you targeted .contact-form (the form element), not .contact (the section wrapper). The flex properties need to go on the direct parent of the inputs.
  • Make sure flex-direction: column is included. Without it, Flexbox defaults to row and your inputs would appear side by side.

Help me fix my Flexbox contact form layout

Test the Flexbox layouts at different widths

Flexbox is inherently flexible. Items compress and expand based on available space. Let's verify that both layouts hold up at any viewport width.

  • In Chrome DevTools, toggle the Device Toolbar by pressing Ctrl+Shift+M (Windows/Linux) or Cmd+Shift+M (macOS).
  • Drag the viewport width from 375px to around 1200px and watch both the nav and the form.
  • Right-click the navigation bar and select Inspect.
  • In the Elements panel, look for the flex badge next to the .nav element. Click it to visualize the flex container.

Notice that the nav works at all sizes without needing any media queries. The items simply compress or expand naturally. This is the power of Flexbox for simple one-directional layouts.

What is the flex badge in DevTools?

Chrome DevTools adds a small "flex" label next to any element that has display: flex. Clicking it overlays colored lines showing the flex container boundaries, item sizes, and gaps. This is invaluable for debugging alignment issues.

✔️ 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 full style.css file up to this point. Compare yours against it:

/* ================================
   BASE STYLES (Mobile-First)
   Normal document flow = single column.
   No layout techniques needed here.
   ================================ */

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

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

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
}

/* ================================
   TYPOGRAPHY & COLORS
   ================================ */

h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
}

h2 {
    font-size: 1.75rem;
    margin-bottom: 2rem;
    text-align: center;
}

h3 {
    font-size: 1.25rem;
    margin-bottom: 0.5rem;
}

.btn {
    display: inline-block;
    background-color: #2563eb;
    color: #fff;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    border: none;
    font-size: 1rem;
    cursor: pointer;
}

.btn:hover {
    background-color: #1d4ed8;
}

/* ================================
   HEADER & NAVIGATION (Flexbox)
   ================================ */

.header {
    background-color: #1e293b;
    padding: 1rem;
}

.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: 1200px;
    margin: 0 auto;
}

.nav-logo {
    font-size: 1.5rem;
    font-weight: 700;
    color: #fff;
}

.nav-links {
    display: flex;
    gap: 1.5rem;
}

.nav-links a {
    color: #e2e8f0;
}

.nav-links a:hover {
    color: #fff;
}

/* ================================
   HERO SECTION
   ================================ */

.hero {
    background-color: #0f172a;
    color: #fff;
    padding: 4rem 1rem;
}

.hero-content {
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   SERVICES SECTION
   ================================ */

.services {
    padding: 4rem 1rem;
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   CONTACT SECTION (Flexbox column)
   ================================ */

.contact {
    background-color: #f1f5f9;
    padding: 4rem 1rem;
}

.contact-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 600px;
    margin: 0 auto;
}

.contact-form input,
.contact-form textarea {
    padding: 0.75rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
    font-size: 1rem;
    font-family: inherit;
}

.contact-form input:focus,
.contact-form textarea:focus {
    outline: none;
    border-color: #2563eb;
}

/* ================================
   FOOTER
   ================================ */

.footer {
    background-color: #1e293b;
    color: #e2e8f0;
    text-align: center;
    padding: 2rem 1rem;
}

Your Flexbox layouts are working across all viewport widths. Next up, you'll use CSS Grid to tackle a two-dimensional layout challenge: arranging the service cards into columns that change based on screen size.

Build the Services Grid with CSS Grid

In the last step, you used Flexbox to align items along a single axis. The navigation spread items horizontally, and the form stacked inputs vertically.

Now you need your three service cards to align in both rows AND columns. This is where CSS Grid shines. Grid gives you control over two dimensions at once, making it the right tool for card layouts.

In this step, get ready to:
  • Style the services grid container and individual cards.
  • Experiment with multi-column layouts to understand why media queries are needed.
  • Use Chrome DevTools Grid inspector to visualize your grid.
Add Grid styles to the services section

Why Grid instead of Flexbox here?

Flexbox distributes items along one axis. If you tried to lay out cards in a row with Flexbox, the items in one row would not automatically align with items in the row above.

Grid controls both rows and columns simultaneously. Cards snap into a structured matrix where every column and row aligns perfectly. Whenever you need items to line up across both axes, Grid is the right choice.

  • In style.css, find the SERVICES SECTION comment and update it to SERVICES SECTION (CSS Grid) so the section is clearly labelled.
  • Below the existing .services rule, add Grid styles for the card container and individual cards by pasting this code:
.services-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

.service-card {
    background-color: #f8fafc;
    padding: 2rem;
    border-radius: 0.5rem;
    border: 1px solid #e2e8f0;
}

What does this code do?

  • display: grid turns .services-grid into a Grid container. All direct children become grid items automatically.
  • grid-template-columns: 1fr creates a single column that takes up all available space. 1fr means "one fraction of the available space."
  • gap: 1.5rem adds consistent spacing between each grid item without needing margin on individual cards.
  • The .service-card styles give each card a light background, padding, rounded corners, and a subtle border.
  • Save style.css.
  • Refresh your browser at mobile width (around 375px in the Device Toolbar). You should see three service cards stacked vertically with even spacing between them.

Cards not showing any styling?

  • Check that .services-grid in your CSS matches class="services-grid" in your HTML exactly. Class names are case-sensitive.
  • Make sure the new rules are placed after the .services rule, not inside it. Each rule block needs its own opening and closing curly braces.

Help me debug why my Grid styles aren't applying to the service cards.

Experiment with a three-column layout

Your single-column grid works perfectly at mobile width. But what happens if you force three columns at every width?

  • In style.css, find the grid-template-columns: 1fr; line inside .services-grid.
  • Find this line:
    grid-template-columns: 1fr;
  • Temporarily change it to:
    grid-template-columns: repeat(3, 1fr);
  • Save style.css and drag the Device Toolbar viewport between 320px and 1200px.

At desktop width, three columns look great. At mobile width, the cards are cramped and unreadable. This is exactly why you need media queries to switch between column counts at different breakpoints.

  • Change the line back to its original value:
    grid-template-columns: 1fr;
  • Save style.css. Your cards should be back to a single stacked column.

Why start with one column and add more later?

This is the mobile-first pattern in action. Your base CSS handles the simplest layout (one column). In the next step, you will add media queries that only activate at wider viewports to create multi-column layouts. This means less CSS overall because you never have to "undo" desktop styles for mobile.

Explore the Grid inspector in Chrome DevTools

Chrome DevTools has a built-in Grid inspector that visualizes grid lines, tracks, and gaps directly on your page.

  • Right-click on one of the service cards in your browser and select Inspect.
  • In the Elements panel, find the <div class="services-grid"> element in the DOM tree.
  • Look for the small grid badge that appears next to the element.
  • Click the grid badge to toggle the grid overlay on your page.

You should see colored lines overlaid on the services section, showing the grid tracks and gap between your cards.

What is the Grid inspector showing me?

The overlay displays columns, rows, their numbers, and gaps. Right now you have a single column, so the overlay shows one vertical track with three rows (one for each card).

This tool becomes essential when you add multi-column layouts in the next step. You can instantly see whether items are landing in the correct tracks.

  • Try resizing the viewport while the grid overlay is active. Watch how the single column stretches and the gap remains consistent.

✔️ Awesome, I've got everything!

Great. Double check you have saved style.css with grid-template-columns: 1fr (the single-column version).

ⓧ I'd like to double check the full code

/* ================================
   BASE STYLES (Mobile-First)
   Normal document flow = single column.
   No layout techniques needed here.
   ================================ */

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

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

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
}

/* ================================
   TYPOGRAPHY & COLORS
   ================================ */

h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
}

h2 {
    font-size: 1.75rem;
    margin-bottom: 2rem;
    text-align: center;
}

h3 {
    font-size: 1.25rem;
    margin-bottom: 0.5rem;
}

.btn {
    display: inline-block;
    background-color: #2563eb;
    color: #fff;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    border: none;
    font-size: 1rem;
    cursor: pointer;
}

.btn:hover {
    background-color: #1d4ed8;
}

/* ================================
   HEADER & NAVIGATION (Flexbox)
   ================================ */

.header {
    background-color: #1e293b;
    padding: 1rem;
}

.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: 1200px;
    margin: 0 auto;
}

.nav-logo {
    font-size: 1.5rem;
    font-weight: 700;
    color: #fff;
}

.nav-links {
    display: flex;
    gap: 1.5rem;
}

.nav-links a {
    color: #e2e8f0;
}

.nav-links a:hover {
    color: #fff;
}

/* ================================
   HERO SECTION
   ================================ */

.hero {
    background-color: #0f172a;
    color: #fff;
    padding: 4rem 1rem;
}

.hero-content {
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   SERVICES SECTION (CSS Grid)
   ================================ */

.services {
    padding: 4rem 1rem;
    max-width: 1200px;
    margin: 0 auto;
}

.services-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

.service-card {
    background-color: #f8fafc;
    padding: 2rem;
    border-radius: 0.5rem;
    border: 1px solid #e2e8f0;
}

/* ================================
   CONTACT SECTION (Flexbox column)
   ================================ */

.contact {
    background-color: #f1f5f9;
    padding: 4rem 1rem;
}

.contact-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 600px;
    margin: 0 auto;
}

.contact-form input,
.contact-form textarea {
    padding: 0.75rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
    font-size: 1rem;
    font-family: inherit;
}

.contact-form input:focus,
.contact-form textarea:focus {
    outline: none;
    border-color: #2563eb;
}

/* ================================
   FOOTER
   ================================ */

.footer {
    background-color: #1e293b;
    color: #e2e8f0;
    text-align: center;
    padding: 2rem 1rem;
}

Your Grid foundation is in place. Right now it looks the same as plain document flow because you only have one column. In the next step, you will add media queries that unlock multi-column layouts at wider viewports, and everything will click together.

Add Responsive Media Queries

Your page has proper layout structure now. The navigation uses Flexbox for horizontal alignment, and the services section uses CSS Grid for card placement.

There is one problem: the cards are locked in a single column at every screen size. Media queries let you apply different styles at different viewport widths, so your layout can adapt as the screen grows.

In this step, get ready to:
  • Add a tablet media query at 768px.
  • Add a desktop media query at 1024px.
  • Test the responsive layout across viewport widths in Chrome DevTools.
Add a tablet breakpoint at 768px

A media query is a CSS rule that only activates when the viewport meets a certain condition. Using min-width means the styles inside only apply when the viewport is that wide or wider.

  • In style.css, scroll to the very bottom of the file (after the .footer block).
  • Add the tablet media query by pasting this code at the bottom:
/* ================================
   RESPONSIVE: TABLET (768px+)
   ================================ */

@media (min-width: 768px) {
    h1 {
        font-size: 2.5rem;
    }

    .services-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .hero {
        padding: 6rem 2rem;
    }
}

What does this code do?

  • @media (min-width: 768px) means everything inside this block only applies when the viewport is 768px or wider. Below that width, your mobile base styles remain active.
  • h1 { font-size: 2.5rem; } bumps up the heading size now that more screen space is available.
  • grid-template-columns: repeat(2, 1fr) switches the services grid from one column to two equal columns at tablet width.
  • The hero padding increases from 4rem 1rem to 6rem 2rem so the section breathes more on wider screens.
  • Save style.css and refresh your browser.
  • In Chrome DevTools, drag the viewport width above 768px. You should see the service cards jump from a single column to two columns side by side.

Cards still in a single column above 768px?

  • Make sure the media query is placed AFTER the .footer block, not inside another rule.
  • Check that you have the opening { after @media (min-width: 768px) and a matching closing } at the end of the block.
  • Confirm you are targeting .services-grid (with the dot) inside the media query.

Why aren't my media queries working?

Add a desktop breakpoint at 1024px

The tablet query handles medium screens. Now you need a second query for even wider desktop viewports where three columns fit comfortably.

  • In style.css, directly below the tablet media query you just added, paste the desktop media query:
/* ================================
   RESPONSIVE: DESKTOP (1024px+)
   ================================ */

@media (min-width: 1024px) {
    h1 {
        font-size: 3rem;
    }

    .services-grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .hero {
        padding: 8rem 2rem;
    }

    .hero-content {
        max-width: 800px;
    }
}

What does this code do?

  • @media (min-width: 1024px) activates these styles only when the viewport is 1024px or wider.
  • grid-template-columns: repeat(3, 1fr) expands the services grid to three equal columns on desktop.
  • .hero-content { max-width: 800px; } constrains the hero text so lines stay readable on very wide monitors.
  • Save style.css and refresh your browser.
  • Drag the DevTools viewport above 1024px. You should see the service cards jump to three columns.

Still seeing two columns at desktop width?

  • Make sure the desktop media query is placed below the tablet one. CSS reads top to bottom, so the 1024px query must come after 768px.
  • Check that the closing } of the tablet query is not wrapping the desktop query inside it. Each media query must be its own independent block.

Help me figure out why my desktop media query is not overriding the tablet one.

Test the responsive layout in Chrome DevTools

Now that both media queries are in place, you can see the full mobile-first pattern in action. Base styles handle mobile, and media queries progressively enhance for larger screens.

  • In Chrome DevTools, set the viewport width to 375px. Confirm the service cards display in a single column.
  • Drag the viewport to 800px. Confirm the cards switch to two columns.
  • Drag the viewport to 1100px. Confirm the cards switch to three columns.
  • At each width, check that the navigation bar and contact form still look correct. They adapt fluidly at all sizes without needing any media query rules because Flexbox handles them automatically.

Why do the nav and form work without media queries?

Flexbox is inherently flexible. Items compress and expand naturally based on available space. The navigation links shrink their gap at narrow widths, and the form stacks vertically at every size because you set flex-direction: column.

CSS Grid with a fixed column count (like repeat(3, 1fr)) needs media queries because it forces a specific number of columns regardless of space. That is the tradeoff: Grid gives you precise two-dimensional control, but you handle the responsive transitions yourself.

✔️ Awesome, I've got everything!

Great. Double check you have saved style.css and your page responds correctly at all three widths.

ⓧ I'd like to double check the full code

Here is the complete style.css file with both media queries added at the bottom.

/* ================================
   BASE STYLES (Mobile-First)
   Normal document flow = single column.
   No layout techniques needed here.
   ================================ */

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

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

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
}

/* ================================
   TYPOGRAPHY & COLORS
   ================================ */

h1 {
    font-size: 2rem;
    margin-bottom: 1rem;
}

h2 {
    font-size: 1.75rem;
    margin-bottom: 2rem;
    text-align: center;
}

h3 {
    font-size: 1.25rem;
    margin-bottom: 0.5rem;
}

.btn {
    display: inline-block;
    background-color: #2563eb;
    color: #fff;
    padding: 0.75rem 1.5rem;
    border-radius: 0.5rem;
    border: none;
    font-size: 1rem;
    cursor: pointer;
}

.btn:hover {
    background-color: #1d4ed8;
}

/* ================================
   HEADER & NAVIGATION (Flexbox)
   ================================ */

.header {
    background-color: #1e293b;
    padding: 1rem;
}

.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    max-width: 1200px;
    margin: 0 auto;
}

.nav-logo {
    font-size: 1.5rem;
    font-weight: 700;
    color: #fff;
}

.nav-links {
    display: flex;
    gap: 1.5rem;
}

.nav-links a {
    color: #e2e8f0;
}

.nav-links a:hover {
    color: #fff;
}

/* ================================
   HERO SECTION
   ================================ */

.hero {
    background-color: #0f172a;
    color: #fff;
    padding: 4rem 1rem;
}

.hero-content {
    max-width: 1200px;
    margin: 0 auto;
}

/* ================================
   SERVICES SECTION (CSS Grid)
   ================================ */

.services {
    padding: 4rem 1rem;
    max-width: 1200px;
    margin: 0 auto;
}

.services-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

.service-card {
    background-color: #f8fafc;
    padding: 2rem;
    border-radius: 0.5rem;
    border: 1px solid #e2e8f0;
}

/* ================================
   CONTACT SECTION (Flexbox column)
   ================================ */

.contact {
    background-color: #f1f5f9;
    padding: 4rem 1rem;
}

.contact-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 600px;
    margin: 0 auto;
}

.contact-form input,
.contact-form textarea {
    padding: 0.75rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
    font-size: 1rem;
    font-family: inherit;
}

.contact-form input:focus,
.contact-form textarea:focus {
    outline: none;
    border-color: #2563eb;
}

/* ================================
   FOOTER
   ================================ */

.footer {
    background-color: #1e293b;
    color: #e2e8f0;
    text-align: center;
    padding: 2rem 1rem;
}

/* ================================
   RESPONSIVE: TABLET (768px+)
   ================================ */

@media (min-width: 768px) {
    h1 {
        font-size: 2.5rem;
    }

    .services-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .hero {
        padding: 6rem 2rem;
    }
}

/* ================================
   RESPONSIVE: DESKTOP (1024px+)
   ================================ */

@media (min-width: 1024px) {
    h1 {
        font-size: 3rem;
    }

    .services-grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .hero {
        padding: 8rem 2rem;
    }

    .hero-content {
        max-width: 800px;
    }
}

Your landing page is fully responsive. It adapts from a single column on phones, to two columns on tablets, to three columns on desktops. Next up, you will take on a bonus challenge that removes media queries entirely for a section using an advanced Grid technique.

Secret mission

Add a Self-Responsive Team Section

Your services grid uses media queries to switch between 1, 2, and 3 columns. But what if the browser could figure out the column count on its own? In this mission, you'll use an advanced CSS Grid pattern to build a team section that reflows automatically without a single media query.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files or delete them.

Resources you used:

  • The tech-agency folder on your Desktop containing index.html and style.css.

Keep everything running

No action needed. Choose this if you want to keep building on your responsive landing page or use it as a template for future projects.

  • Your tech-agency folder contains a complete responsive landing page with Flexbox navigation, CSS Grid service cards, and a self-responsive team section.
  • You can continue adding new sections, experimenting with breakpoints, or using the page as a starter template.

Pause - I'll come back to this later

There are no running processes or services to pause. Your files sit on disk and take up minimal space.

  • Your project is just two static files. They use negligible disk space and consume no resources while sitting idle.
  • Come back anytime by opening the tech-agency folder in Cursor.

Delete - I don't want to use this again

Remove all project files from your machine by deleting the project folder.

  • Delete the tech-agency folder by running the command for your operating system below.

macOS

  • Delete the project folder by running this command:
rm -rf ~/Desktop/tech-agency

Windows

  • Delete the project folder by running this command:
rmdir /s /q %USERPROFILE%\Desktop\tech-agency
  • Confirm the folder is gone by running:
ls ~/Desktop

You should no longer see tech-agency in the output.

That's a Wrap!

That's a Wrap!

Nice work! You just built a fully responsive tech agency landing page that adapts beautifully from a 320px phone screen all the way up to a widescreen desktop.

You've learned how to:

  • Applied Flexbox for one-dimensional layouts (navigation bar, contact form) and CSS Grid for two-dimensional layouts (service cards).
  • Implemented mobile-first responsive design with media queries at 768px and 1024px breakpoints that progressively enhance your layout for larger screens.
  • Used Chrome DevTools Device Toolbar and Grid inspector to test and debug layouts at every viewport width.
  • Secret Mission: Created a self-responsive team grid using repeat(auto-fit, minmax()) that reflows from four columns to one without any media queries.

Ready to quiz yourself?