Responsive Design Starts in the Markup

The viewport tag, flexible images, relative units and the mobile first habit. Half of responsive design is decided before a single media query is written.

Concept

Responsive design means one document that adapts to any screen, rather than separate mobile and desktop sites. Most of the work is CSS, but several decisions live in the markup and no stylesheet can rescue them.

The three foundations

1. The viewport meta tag

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

Without it a mobile browser pretends the screen is about nine hundred and eighty pixels wide and scales the whole page down. Every media query is then evaluated against that fiction and none of them fire. This one line is the difference between a responsive site and a shrunken desktop site.

2. Flexible images

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

Without max-width, a wide image forces the page wider than the screen and the whole document scrolls sideways. Without height: auto, the image is squashed rather than scaled.

The width and height attributes stay on the element regardless - they reserve the correct space and prevent layout shift while the file downloads.

3. Relative units

/* brittle */
.card { width: 400px; font-size: 14px; padding: 20px; }

/* adapts */
.card { max-width: 25rem; font-size: 1rem; padding: 1.25rem; }
UnitRelative toGood for
remThe root font sizeAlmost everything
emThe element font sizeSpacing that scales with its own text
%The parentWidths within a container
vw, vhThe viewportFull screen sections
chCharacter widthLine length
pxNothingBorders and hairlines

Setting font sizes in pixels overrides the browser font size a reader has deliberately chosen. Using rem respects it.

Mobile first

/* base: the narrowest screen, no media query */
.layout {
  display: grid;
  gap: 1.5rem;
}

/* then add complexity as space allows */
@media (min-width: 50rem) {
  .layout { grid-template-columns: 2fr 1fr; }
}

@media (min-width: 70rem) {
  .layout { gap: 2.5rem; }
}

Writing the small screen styles first and adding with min-width queries produces simpler CSS than the reverse, because a narrow layout is usually a single column - the simplest case - and each query adds rather than undoes.

Choose breakpoints where the design starts to look wrong, not at device names. Device sizes change; the point at which your line length becomes uncomfortable does not.

Markup decisions that matter

Responsive images

<img src="/images/hall-800.jpg"
     srcset="/images/hall-400.jpg 400w,
             /images/hall-800.jpg 800w,
             /images/hall-1600.jpg 1600w"
     sizes="(max-width: 50rem) 100vw, 50vw"
     alt="An empty lecture hall"
     width="1600" height="900">

Sending a 1600 pixel image to a 360 pixel phone wastes most of the download on a connection that can least afford it.

Wide content needs a container

<div class="table-scroll" tabindex="0" role="region" aria-label="Fee structure">
  <table>...</table>
</div>

Tables, code blocks and preformatted text do not reflow. Give each its own scroll container so the page body never scrolls sideways.

.table-scroll, pre { overflow-x: auto; }

Source order is mobile order

On a narrow screen a multi column layout collapses into a single column in source order. So the markup order should already be the priority order, which is another reason to put the main content before the sidebar.

Touch targets

a, button, input, select, summary {
  min-height: 44px;
  min-width: 44px;
}

Roughly forty four pixels square is the common guidance for a comfortable tap target. Links in body text are an exception - they inherit the line height - but standalone controls should meet it.

Form fields at sixteen pixels

input, select, textarea { font-size: 16px; }

Below sixteen pixels, iOS zooms in when a field is focused and does not zoom back out. The correct fix is the font size, not disabling zoom.

Testing properly

  • 320 pixels wide - the narrowest screen still in common use. If it works there it works nearly everywhere.
  • Zoom to 200 percent - required by accessibility guidelines and a good test of relative units.
  • A real device - a resized desktop window does not reproduce touch, keyboards, or the browser interface eating vertical space.
  • Landscape on a phone - very short viewports break fixed height designs.
  • A throttled connection - which is the real experience for a large share of readers.

Useful media queries beyond width

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

@media (prefers-color-scheme: dark) { }
@media (prefers-contrast: more) { }
@media (hover: none) { }          /* touch device */
@media (pointer: coarse) { }      /* finger sized pointer */
@media print { }

The reduced motion query is the one to add first. It costs three lines and it matters a great deal to readers who experience motion sickness.

Important rules

  • No viewport tag means no responsive behaviour, whatever the CSS says.
  • Never disable zoom.
  • Images need max-width: 100% and height: auto in CSS, plus width and height attributes in the markup.
  • Source order is the order a narrow screen shows.
  • Breakpoints belong to the design, not to device names.
  • Tables and code blocks need their own scroll containers.

Common mistakes

  • Omitting the viewport tag.
  • Fixed pixel widths that overflow narrow screens.
  • Font sizes in pixels, ignoring the reader browser setting.
  • Testing only by resizing a desktop window.
  • Tap targets too small or too close together.
  • Sending desktop sized images to phones.
  • Disabling zoom to work around the iOS form field zoom.

Best practices

  • Viewport tag on every page.
  • Mobile first CSS with min-width queries.
  • rem for type and spacing, px only for hairlines.
  • Responsive images with srcset and sizes.
  • Scroll containers for anything that cannot reflow.
  • Honour prefers-reduced-motion and prefers-color-scheme.
  • Test at 320 pixels and at 200 percent zoom before shipping.

Practice

  1. Remove the viewport tag from a responsive page, open it on a phone, and describe what happens to the media queries.
  2. Set a container to 800 pixels wide and view it at 320 pixels. Fix it with max-width.
  3. Convert a stylesheet from pixel font sizes to rem, then change the browser default size and observe.
  4. Add a reduced motion query and confirm animations stop when the system setting is on.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.