Responsive Design and Publishing

Table of Contents

  1. One Page, Every Screen
  2. The Viewport Meta Tag
  3. Fluid by Default
  4. Media Queries
  5. Mobile First
  6. Responsive Images
  7. Fluid Type with clamp()
  8. Testing with DevTools Device Mode
  9. Responsive Profile Page
  10. Publishing to GitHub Pages
  11. Audit with Lighthouse
  12. Where to Go Next
  13. Try It Yourself

1. One Page, Every Screen

Your page will be viewed on a 6-inch phone, a laptop, and a 27-inch monitor, in portrait and landscape, with large fonts and small. In Cambodia and much of the world, most visitors are on a phone. You can't build one fixed layout for all of them, and you shouldn't build separate sites either.

Responsive design is one HTML page, with CSS that adapts to the space available. It rests on three ideas: a viewport tag, flexible layouts, and media queries.

2. The Viewport Meta Tag

Phones have a trick. When a page doesn't say otherwise, they pretend to be a ~980px-wide desktop screen and then shrink the page to fit, which leaves everything tiny. This tag turns it off:

<meta name="viewport" content="width=device-width, initial-scale=1">

It says "use the real width of the device, at normal zoom." You added it in chapter 2. Check every page has it — without it, none of the CSS in this chapter works on a real phone.

Don't add user-scalable=no or maximum-scale=1; they stop people from zooming in, which is an accessibility failure.

3. Fluid by Default

You've already written most of a responsive page without realizing it. The core techniques are:

  • Block elements fill their container, so they shrink with it.
  • max-width instead of width, as in max-width: 48rem.
  • rem and %, not fixed pixels, for sizes and spacing.
  • img { max-width: 100%; height: auto; } so images never overflow.
  • Flexbox with flex-wrap and Grid with auto-fit, which add and remove columns by themselves.

Test any page by dragging the browser window narrower. If content overflows sideways, causing a horizontal scrollbar, something has a fixed width that's too wide. That's the bug to find.

4. Media Queries

Sometimes fluid isn't enough, and the layout should truly change: a menu that collapses, a one-column page that goes to two. A media query applies CSS only when a condition is true:

@media (min-width: 40rem) {
  .about {
    grid-template-columns: 12rem 1fr;
  }
}

Read it as: "when the viewport is at least 40rem wide, apply these rules." You can test other things too:

QueryTrue when
(min-width: 40rem)The viewport is at least 40rem wide
(max-width: 40rem)The viewport is at most 40rem wide
(orientation: landscape)Wider than tall
(prefers-color-scheme: dark)The user prefers dark mode (chapter 8)
(prefers-reduced-motion: reduce)The user wants less animation
printWhen printing the page

Choose breakpoints, the widths where the layout changes, from your content, not from specific devices: drag the window wider until the layout looks stretched, and put a breakpoint there. Common starting points in rem are 40rem (~640px), 48rem (~768px), and 64rem (~1024px). Use rem rather than px so the layout also respects bigger text sizes.

5. Mobile First

There are two ways to order your CSS:

  • Desktop first: write for large screens, then use max-width queries to undo things for small ones.
  • Mobile first: write the simple, single-column layout as the base, then use min-width queries to add complexity as space appears.

Mobile first is the better habit. The base CSS is the simplest — one column, stacked — and phones, which have the slowest connections and smallest screens, get the least code to process. Larger screens layer on what they need:

/* Base: phones. Single column. */
.gallery {
  display: grid;
  gap: 1rem;
}

/* 40rem and up: two columns */
@media (min-width: 40rem) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 64rem and up: three columns */
@media (min-width: 64rem) {
  .gallery {
    grid-template-columns: repeat(3, 1fr);
  }
}

(As chapter 10 showed, repeat(auto-fit, minmax(…)) often needs no queries at all. Use queries for changes it can't express.)

6. Responsive Images

Making an image shrink is easy; making it light on a phone takes a little more. Two HTML tools help.

srcset gives the browser several sizes and lets it pick:

<img
  src="images/dara-800.jpg"
  srcset="images/dara-400.jpg 400w,
          images/dara-800.jpg 800w,
          images/dara-1600.jpg 1600w"
  sizes="(min-width: 40rem) 12rem, 100vw"
  alt="Dara Sok smiling at a laptop"
  width="800" height="600">

The w numbers are each file's real width; sizes tells the browser how wide the image will display, so it can choose before downloading. A phone gets the 400px file, saving data.

<picture> lets you offer different formats or crops:

<picture>
  <source srcset="images/dara.webp" type="image/webp">
  <img src="images/dara.jpg" alt="Dara Sok smiling at a laptop"
       width="800" height="600">
</picture>

Browsers that understand WebP use it, and others fall back to the JPEG in the <img>. For a profile page, one good-sized image with max-width: 100% is fine, and you can skip both until you need them.

7. Fluid Type with clamp()

Instead of a media query for every heading size, the clamp() function picks a value between a minimum and maximum, growing with the viewport:

h1 {
  font-size: clamp(1.75rem, 1.2rem + 3vw, 3rem);
}

Its three arguments are clamp(minimum, preferred, maximum). The heading is never smaller than 1.75rem and never bigger than 3rem, and in between it scales smoothly with the screen width (3vw). The rem in the preferred value keeps it responding to the user's font settings. clamp() also works for padding, gaps, and widths.

8. Testing with DevTools Device Mode

You don't need ten phones to test. In Chrome or Edge DevTools, click the device toggle icon (or press Ctrl+Shift+M / Cmd+Shift+M).

  • Choose a device preset (iPhone, Pixel, iPad), or Responsive and drag the edges to any width.
  • Rotate between portrait and landscape.
  • The bar above the page shows your media-query breakpoints as colored strips; click one to jump to that width.
  • Throttle the network to Slow 4G to feel the weight of your images.

Firefox has a similar Responsive Design Mode (Ctrl+Shift+M). Device mode is very good but not perfect: before you ship, open the page on at least one real phone. With Live Server running, the page is available to other devices on the same Wi-Fi at your computer's address and port.

Check the things that usually break: text readable without zooming, no sideways scrolling, tap targets (links and buttons) at least about 44px tall with space between them, and forms usable with the on-screen keyboard.

9. Responsive Profile Page

Add these to the end of style.css:

/* Skip link: hidden until focused */
.skip-link {
  position: absolute;
  left: -999px;
}

.skip-link:focus {
  left: 1rem;
  top: 1rem;
  padding: 0.5rem 1rem;
  background: var(--color-bg);
  z-index: 10;
}

/* Fluid heading */
h1 {
  font-size: clamp(1.75rem, 1.2rem + 3vw, 3rem);
  line-height: 1.2;
}

/* Base: phone. Stack the navbar links */
.nav-list {
  flex-direction: column;
  gap: 0.5rem;
}

.about {
  display: grid;
  gap: 1.5rem;
}

/* Tablet and up */
@media (min-width: 40rem) {
  .nav-list {
    flex-direction: row;
    gap: 1.5rem;
  }

  .about {
    grid-template-columns: 12rem 1fr;
    align-items: start;
  }
}

Add class="skip-link" to the link you made in chapter 4. Then test in device mode at 360px, 768px, and 1280px. Commit your work.

10. Publishing to GitHub Pages

Your page is a static site — plain files, no server code (see Static Sites, Web Apps, and APIs). GitHub can host it for free. If you went through Git & GitHub for Beginners, you've done this once.

  1. Create a repository on GitHub named dara-profile, public, with no README (your folder already has history).
  2. Connect and push from the project folder:
git branch -M main
git remote add origin https://github.com/YOUR-USERNAME/dara-profile.git
git push -u origin main
  1. On GitHub, open the repository → Settings → Pages. Under Build and deployment, choose Deploy from a branch, branch main, folder / (root), and save.
  2. Wait a minute or two. The page gives you the address: https://YOUR-USERNAME.github.io/dara-profile/.

Open it on your phone. If CSS or images are missing, check the paths. Your site lives under /dara-profile/, so use relative paths (style.css, images/dara.jpg) and not /style.css, as covered in URLs, Piece by Piece. Also, file names are case-sensitive on the server even if your computer isn't: Dara.JPG and dara.jpg are different files.

To update the site, edit, commit, and push — it redeploys on its own:

git add .
git commit -m "Improve mobile layout"
git push

11. Audit with Lighthouse

Now measure your work. In Chrome DevTools, open Lighthouse, choose Mobile, tick all four categories, and click Analyze page load on your published URL. It scores:

CategoryLooks at
PerformanceHow fast it loads; image sizes, fonts, render-blocking files
AccessibilityContrast, labels, alt text, headings, landmarks
Best PracticesHTTPS, no console errors, modern markup
SEOTitle, description, viewport, crawlability

A page like yours should score close to 100 in each. Read what's flagged and fix it, then run again. Most fixes, such as missing alt, low contrast, and oversized images, are things you have learned about in this series.

12. Where to Go Next

You can now build a complete, accessible, responsive page from nothing. Some things beyond this series:

  • JavaScript. Make the page react — menus that open, forms that validate, content that loads. That's the next series on the roadmap: Start Here.
  • CSS you can look up as needed: animations and @keyframes, position: sticky, aspect-ratio, container queries, :has().
  • Frameworks such as Tailwind CSS. They save you typing, but they speed you up only once you understand the CSS underneath, which you now do.
  • A real project. Make a page for a friend, a small business, or a club. Nothing teaches CSS like building something for someone else.

The best reference is MDN Web Docs. When you forget a property, search for it there.

13. Try It Yourself

  1. Find the breakpoint. Open your page in device mode at 320px, then drag it wider. At what width does it start to look stretched or cramped? Put a media query there.
  2. Hunt for overflow. At 320px, is there a horizontal scrollbar? If so, find the element causing it with DevTools.
  3. Mobile-first. Check that your CSS is written with min-width queries and a simple single-column base.
  4. Real phone test. Open the page on a phone. Check text size, tap targets, and the form with the on-screen keyboard.
  5. Publish and share. Push to GitHub Pages, run Lighthouse on the live URL, and fix the lowest score.
  6. Print it. Add a @media print rule that hides the navigation, then print-preview the page.
  7. Show someone. Send the link to a friend. Ask them what they couldn't find. That's the best test of all.

You started with a blank folder and ended with a live site you built yourself. Keep building.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!