Responsive Design and Publishing
Table of Contents
- One Page, Every Screen
- The Viewport Meta Tag
- Fluid by Default
- Media Queries
- Mobile First
- Responsive Images
- Fluid Type with clamp()
- Testing with DevTools Device Mode
- Responsive Profile Page
- Publishing to GitHub Pages
- Audit with Lighthouse
- Where to Go Next
- 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-widthinstead ofwidth, as inmax-width: 48rem.remand%, not fixed pixels, for sizes and spacing.img { max-width: 100%; height: auto; }so images never overflow.- Flexbox with
flex-wrapand Grid withauto-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:
| Query | True 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 |
print | When 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-widthqueries to undo things for small ones. - Mobile first: write the simple, single-column layout as the base,
then use
min-widthqueries 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.
- Create a repository on GitHub named
dara-profile, public, with no README (your folder already has history). - 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
- On GitHub, open the repository → Settings → Pages. Under Build and deployment, choose Deploy from a branch, branch main, folder / (root), and save.
- 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:
| Category | Looks at |
|---|---|
| Performance | How fast it loads; image sizes, fonts, render-blocking files |
| Accessibility | Contrast, labels, alt text, headings, landmarks |
| Best Practices | HTTPS, no console errors, modern markup |
| SEO | Title, 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
- 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.
- Hunt for overflow. At 320px, is there a horizontal scrollbar? If so, find the element causing it with DevTools.
- Mobile-first. Check that your CSS is written with
min-widthqueries and a simple single-column base. - Real phone test. Open the page on a phone. Check text size, tap targets, and the form with the on-screen keyboard.
- Publish and share. Push to GitHub Pages, run Lighthouse on the live URL, and fix the lowest score.
- Print it. Add a
@media printrule that hides the navigation, then print-preview the page. - 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!