HTML2 min read

The viewport meta tag that breaks pinch to zoom

user-scalable=no and maximum-scale=1 are copied into projects every day. They fail an accessibility audit, and the bug they were added to fix has another cause.

This line is in a lot of templates:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />

The last two values stop people zooming. For anyone with low vision, that is the difference between using your site and closing it. It is also a direct failure of WCAG success criterion 1.4.4, which requires text to scale to 200%.

Use this instead:

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

That is the whole tag. Two values, nothing else, for almost every site.

What each part does

width=device-width tells the browser to make the layout viewport as wide as the screen. Without it, mobile browsers assume a 980px desktop page and shrink it, which is why an unstyled page looks tiny on a phone.

initial-scale=1 sets the starting zoom to 100%, so one CSS pixel maps to one density independent pixel.

maximum-scale and user-scalable cap or block zooming. Safari on iOS has ignored them since iOS 10 for exactly this reason. Chrome on Android still honours them, so the harm is real.

The bug people are actually fixing

user-scalable=no usually gets added for one of two reasons.

Reason one: iOS zooms in when you focus an input. Safari does this when the input font size is below 16px. That is the entire rule. Fix the font size, not the zoom:

input,
select,
textarea {
  font-size: 16px; /* Safari will not zoom at 16px or larger */
}

You can keep the visual size you wanted by adjusting padding and line height instead.

Reason two: a double tap zooms when they meant to tap twice. Modern browsers removed the 300ms delay for pages that declare width=device-width, and the double tap behaviour is standard on the web. Leave it alone.

The rest of the head that goes with it

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Page name, site name</title>
<meta name="description" content="One sentence about this page." />
<link rel="canonical" href="https://example.com/page" />

charset must be within the first 1024 bytes of the document, so it goes first. Otherwise the browser may start parsing with the wrong encoding and then restart.

Notches and safe areas

If your layout goes edge to edge on a phone with a notch, add viewport-fit=cover and then pad your content back in with the environment variables:

<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
.app-bar {
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

Without viewport-fit=cover those env() values are zero, which is a fine default. Only opt in when you actually paint under the system UI.

interactive-widget controls what happens to the viewport when the on screen keyboard opens:

<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content" />

resizes-content shrinks the layout viewport so a fixed bottom bar sits above the keyboard rather than behind it. Useful for chat interfaces.

And theme-color paints the browser chrome, with support for both schemes:

<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#08080b" media="(prefers-color-scheme: dark)" />

The 20 second test

Open the site on a phone, or in device mode, and pinch. If nothing happens, find the viewport tag and delete everything after initial-scale=1. Then check whether an input still zooms on focus, and if it does, set its font size to 16px.

Written by Rahul Mourya, full stack engineer. Spotted a mistake or want the follow-up post? Email me.

All posts
  • HTML3 min read

    The autocomplete tokens that fix mobile checkout

    autocomplete is not on or off. It is a list of specific tokens that let phones fill an address in one tap. Getting them right is the cheapest conversion win in a form.

    • forms
    • mobile
    • autofill
  • HTML2 min read

    A data table a screen reader can actually read

    Four attributes turn a grid of numbers into a table that announces which row and column each cell belongs to. Plus how to make a wide table scroll without breaking it.

    • tables
    • accessibility
    • data
  • HTML2 min read

    The dialog element gives you a focus trap for free

    Hand rolled modals leak focus to the page behind them. The dialog element handles focus, Escape, the backdrop, and the top layer. Here is the small amount you still write.

    • dialog
    • accessibility
    • modal