Skip to content

Note

The one line that decides how a phone renders a page

A missing viewport meta tag does not break a page. It does something worse: the phone lays it out at a desktop width and shrinks the result to fit the glass, so a perfectly good responsive stylesheet never matches a single media query.

  • Mobile
  • Front end
  • CSS

Written

What a phone does without it

A mobile browser has two viewports. The visual viewport is the glass. The layout viewport is the width CSS believes it has, and by default the two are not the same thing: faced with a page that says nothing about its own width, a phone assumes it was written for a desktop, lays it out at a fallback width of roughly a thousand CSS pixels, and then scales the whole rendered result down to fit the screen.

The result is the one everybody recognises: a complete, correct desktop layout rendered at about a third of legible size, with pinch-zoom as the only way to read it. Nothing has failed, so nothing is reported.

The second effect is the expensive one. Media queries resolve against the layout viewport, so a min-width breakpoint matches on a phone whose screen is nowhere near it. A responsive stylesheet tested at every breakpoint on a laptop does exactly nothing on the device it was written for, and the defect is one missing line rather than anything in the CSS.

The line

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

Two instructions. width=device-width sets the layout viewport to the device's own width in CSS pixels, so layout happens at the size the page is being read at and media queries start telling the truth. initial-scale=1 sets the opening zoom to one, so the browser does not helpfully scale the result after being told the right width.

It belongs in the head of every page rather than in one template, and on a site built from a single layout it is one edit. That is the whole fix.

What not to add

  • user-scalable=no, and maximum-scale=1. They take away a reader's ability to zoom. The resize-text criterion in WCAG exists for people who need text larger than you chose for them, and removing pinch-zoom is an accessibility failure with no corresponding benefit. Mobile Safari has ignored user-scalable=no for years precisely because it was abused this way, so the line is both wrong and ineffective.
  • A fixed width=1024. That is the original problem, written down deliberately.
  • viewport-fit=cover, unless something needs it. It lets the page draw into the rounded corners and the notch area of a device, and once you have asked for that, keeping content out of those regions with the env(safe-area-inset-*) values is yours to do. Worth it for an edge-to-edge header; not worth it by default.

When the line is there and it is still wrong

One element wider than the viewport — a fixed-width table, an oversized image, a preformatted block that will not wrap, a hardcoded pixel width in an inline style — gives the whole document horizontal scroll, and on a phone that reads as a broken layout rather than as one wide table. Find the offender in the console rather than by inspection:

Array.from(document.querySelectorAll('*'))
  .filter(el => el.getBoundingClientRect().right > window.innerWidth + 1)
  .slice(0, 20)

Then fix it in CSS rather than by removing the viewport line, which is the shortcut that trades one wide element for every phone layout on the site.

Why this is the cheapest item on the list

No build change, no dependency, no deploy risk: one line in the head. It is also the only defect of its kind that every visitor on a phone experiences immediately and silently. They do not report it, they leave. Of everything measurable about a site from outside, this is the shortest distance between a measurement and a fix.

Sources

Where this is written down

Related

Where this comes up in the work

More notes

Other things worth writing down

Next step

Tell us the version, the hardware, and what it has to do.

You will get a written scope and a fixed price against it. If the honest answer is that you do not need us, you will get that instead.