Arkendia Classrooms
Lesson 14 of 21 · 18 min

Responsive design: one site for every screen

Your visitors will open your portfolio on phones, tablets, laptops and wide monitors, in portrait or landscape, sometimes with enlarged text. A site that works only on the screen you built it on is a site that fails many people. Responsive design makes your pages adapt gracefully. In this lesson you will learn the viewport tag, the mobile-first approach, media queries, fluid images and the clamp() function.

What responsive means

A responsive site has a single codebase that adapts its layout to the space available. It is not about specific device models, as there are far too many sizes. Instead, you let content flow naturally and make adjustments at the points where the design starts to break.

Three ingredients make up the classic recipe: a flexible layout (percentages, Flexbox, Grid), flexible media (images that never overflow) and media queries (rules that apply only under certain conditions).

The viewport meta tag

Without help, a phone browser pretends the page is on a wide desktop screen and shrinks everything to fit, producing tiny text. The following tag in your head (you added it in your very first page) fixes that:

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

It tells the browser to use the real width of the device as the layout width and not to zoom out. Without it, none of your media queries would behave as expected on a phone. Never forget it, and avoid options that block the user from zooming, which harms accessibility.

Mobile first

Mobile first means writing the styles for the smallest screens first, as the default, then adding enhancements for larger screens. This has practical advantages:

  • A small screen forces you to focus on what is essential.
  • The simplest layout, a single column, is also the default behavior of HTML, so there is less CSS to write.
  • Phones load only the rules they need and are not burdened with overrides.

Concretely, your base CSS describes the single-column layout, and media queries enhance it as the screen widens.

Media queries

A media query applies a block of CSS only when a condition is met. The most common condition is the width of the viewport:

/* Base: small screens */
.gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

/* From 40em upward */
@media (min-width: 40em) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* From 64em upward */
@media (min-width: 64em) {
  .gallery {
    grid-template-columns: repeat(3, 1fr);
  }
}

min-width means “this width or more”, which is the natural fit for mobile-first. You may also meet max-width (“this width or less”) in older, desktop-first code. Using em units in queries respects the user’s font size settings.

Choosing breakpoints

A breakpoint is the width at which your layout changes. Do not choose them from a list of device sizes. Instead, open your page, slowly narrow or widen the window, and add a breakpoint where the design starts to look bad: lines getting too long, items getting cramped. Two or three breakpoints are usually enough for a portfolio.

Other media features

Media queries can test more than width:

@media (orientation: landscape) { }
@media (prefers-color-scheme: dark) { }
@media (prefers-reduced-motion: reduce) { }
@media print { }

You can adapt to the visitor’s preferences, such as a dark mode or reduced motion. The last example styles the page for printing, such as hiding the menu on paper. We will use prefers-reduced-motion in the accessibility lesson.

Flexible media

Images and videos have their own intrinsic sizes, and a wide image in a narrow column overflows. The standard guard, which you met earlier, is:

img, video {
  max-width: 100%;
  height: auto;
}

For many images at different resolutions, HTML provides srcset and sizes so that the browser downloads the version that suits the screen. We will touch on it in the performance lesson.

Fluid typography with clamp()

Instead of jumping from one size to another at a breakpoint, the clamp() function lets a value scale smoothly between a minimum and a maximum:

h1 {
  font-size: clamp(2rem, 1.2rem + 3vw, 3.5rem);
}
.section {
  padding: clamp(1.5rem, 5vw, 4rem);
}

The three arguments are the minimum, the ideal value (which grows with the viewport width, using vw) and the maximum. The title never gets smaller than 2rem nor larger than 3.5rem, and scales in between. Adding a rem portion to the ideal value keeps the text responsive to user font settings. The same function works for spacing. Other helpers, min() and max(), pick the smaller or larger of several values.

A responsive header

Back to our Flexbox header. On a small screen, a row with a logo and four links may not fit. A simple, robust solution that needs no JavaScript is to let it wrap:

.site-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem 1rem;
}
.menu {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

On narrow screens the menu drops under the logo and its links wrap if necessary. Complex “hamburger” menus need JavaScript and careful accessibility work; for a portfolio with a few links, wrapping is perfectly good.

A responsive hero section

.hero {
  display: grid;
  gap: 2rem;
  padding: clamp(2rem, 6vw, 5rem) 1.5rem;
}
.hero img {
  width: 100%;
  border-radius: 0.75rem;
}
@media (min-width: 50em) {
  .hero {
    grid-template-columns: 1.2fr 1fr;
    align-items: center;
  }
}

On a phone, the text and the image stack. From 50em and up, they sit side by side. Short and readable.

Testing your site

  • Resize the browser window slowly and look for overflow, horizontal scrollbars and cramped text.
  • Use the device toolbar of the developer tools to simulate phone sizes and orientations.
  • Test on a real phone when you can. Touch targets such as links and buttons should be comfortable to tap, with enough size and space around them.
  • Increase the browser’s default text size and make sure nothing breaks.

Warning. A horizontal scrollbar on a phone is a bug. The usual culprits are a fixed width in pixels, a wide image without max-width: 100%, or a long unbroken word or URL. Use the Elements panel to find which element is wider than the screen.

Tip. Browser simulation is a good approximation but not a perfect copy of a real phone. Treat it as a quick check, not as the final test.

Your turn

  1. Check that every page of your portfolio contains the viewport meta tag.
  2. Rewrite the gallery so it uses one column by default, two columns from 40em and three from 64em.
  3. Use clamp() for the size of your h1 and for the vertical padding of your hero section.
  4. Make the header wrap gracefully on narrow screens.
  5. Test with the device toolbar and find one thing to improve.
See a solution
.gallery {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
}
@media (min-width: 40em) {
  .gallery { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 64em) {
  .gallery { grid-template-columns: repeat(3, 1fr); }
}
h1 {
  font-size: clamp(2rem, 1.2rem + 3vw, 3.5rem);
}
.hero {
  padding-block: clamp(2rem, 6vw, 5rem);
}

Note that padding-block sets the top and bottom padding in one declaration. The result: a layout that grows by steps for columns, and smoothly for type and spacing.

Key takeaways

  • Include <meta name="viewport" content="width=device-width, initial-scale=1"> on every page.
  • Go mobile first: simple single-column base styles, then enhancements with @media (min-width: ...).
  • Choose breakpoints where your design breaks, not from lists of device sizes.
  • Keep media fluid with max-width: 100% and height: auto, and use clamp() for fluid type and spacing.
  • Test by resizing, with the device toolbar, on a real phone and with larger text.
Advertisement
Log in to save your progress. Log in