Meta viewport

Back to the index.

The meta viewport was originally invented by Apple for the iPhone. Since web developers started to use it a lot, other mobile browsers also implemented it, including the parts that don’t make sense.

Meanwhile W3C has reverse-engineered a specification.

Although I’d love to study it more and compare it to my findings, I’m afraid it’s so densely written that I do not understand it — even now that I understand the meta viewport itself.

For instance, I think that W3C’s extend-to-zoom concept is the same as my ideal viewport, but I’m not totally sure.

So I hope that this page will serve as an English translation of the specification, but I’m not sure it is.

My notes for this research. Not necessarily comprehensible to anyone else.

This page contains the preliminary tests for the various directives in the meta viewport tag.

The tests are preliminary because I ran them in only eight browsers, just to get an idea of the lay of the land and the right questions to ask.

The @viewport construct is not part of this research. I’ll phase it in later when I know how it should behave.

Last major update on 21 October 2013.

The meta viewport tag

The meta viewport tag contains instructions to the browser in the matter of viewports and zooming. In particular, it allows web developers to set the width of the layout viewport relative to which CSS declarations such as width: 20% are calculated.

The meta viewport has the following syntax:

<meta name="viewport" content="name=value,name=value">

Directives

Every name/value pair is a directive. (The word is my own invention.) There are six of them in total:

The current research does not address the last two directives at all, and does only light testing of the minimum- and maximum-scale ones. It concentrates on width and initial-scale.

device-width value

There is one special value: device-width for the width directive. It sets the layout viewport width to the ideal viewport width.

In theory there’s a similar device-height value, but it doesn’t seem to work.

The browsers

I did these tests in a subset of my regular mobile test array consisting of eight browsers. I selected the browsers for a healthy mix of rendering engines and device dimensions.

Special mobile browser test array, a subset of 1.1.1; October 2013

iPhone
Default browser on iPhone 4S with iOS 6.1.3
iPad
Default browser on iPad 2 with iOS 7.0
Android Samsung
Default browser on Samsung Galaxy Note I, Android 4.0.4
Android HTC
Default browser on HTC One X, Android 4.2.2
Chrome
Chrome 29 on Nexus 7, Android 4.3
Opera Presto
12.10 on Samsung Galaxy Pocket, Android 2.3.6
BlackBerry
Default browser on BlackBerry Z10 (BB OS 10.1)
IE
IE10 on Nokia Lumia 820, Windows Phone 8.0.

I wanted to test Firefox, too, but it still does not support window.innerWidth/Height correctly, so I can’t do the zoom tests. Whatever.

In general the browsers consist of three groups:

  1. Android WebKit and IE have terrible bugs throughout. Sometimes their bugs are even more-or-less similar.
  2. Safari has one very annoying bug where it uses the portrait width in landscape mode, too; for instance with width=device-width.
  3. Chrome, Opera, and BlackBerry support most features well, though they aren’t entirely bug-free.

The three viewports

Ages ago I reported that mobile browsers have two viewports: the visual one and the layout one. Re-read this article if necessary; I assume knowledge of these two viewports in what is to follow.

The ideal viewport

It turns out that there’s a third viewport, which I decided to call the ideal viewport. It gives the ideal size of a web page on the device. Thus, the dimensions of the ideal viewport differ per device.

On old or cheap devices with non-retina screens the ideal viewport is equal to the physical number of pixels, but that’s not a requirement. Newer devices with higher physical pixel densities may well retain the old ideal viewport, because it’s ideally suited to the device.

Up to and including the 4S, the iPhone ideal viewport is 320x480, regardless of whether it has a retine screen or not. That’s because 320x480 is the ideal size for web pages on these iPhones.

There are two important things about the ideal viewport:

  1. The layout viewport can be set to ideal viewport values. The width=device-width and initial-scale=1 directives do so.
  2. All scale directives are relative to the ideal viewport, and not to whatever width the layout viewport has. So maximum-scale=3 means the maximum zoom is 300% of the ideal viewport.

Finding the ideal viewport dimensions

It might occasionally be useful to be able to read out the ideal viewport dimensions. Tough luck.

Well, you can do it. Give a page the following meta tag and read out document.documentElement.clientWidth/Height:

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

If that’s not an option you have no way of reading out the ideal viewport dimensions. I hoped screen.width/height might be helpful here, but only BlackBerry gives the correct information. The other browsers resort to various shades of unhelpfulness.

Open question: should screen.width/height give the ideal viewport dimensions?
Pro: the properties would at last contain useful information.
Con: ideal viewport size does not necessarily equate physical pixels on the device.

Compatibility - ideal viewport

Here is all of the above in easy table format.

Test iPhone iPad Android Samsung Android HTC Chrome Opera Presto BlackBerry IE
ideal viewport size
320 x 480 768 x 1024 400 x 640 360 x 640 601 x 962 240 x 320 342 x 570 320 x 480

There is no right or wrong here. The values depend on the device, and all values are reasonable.

Does width=device-width give the layout viewport the width of the ideal viewport? portrait portrait Yes Yes Yes Yes Yes Yes
  • Safari applies the portrait width (320 or 768) to both landscape and portrait modes.
Does initial-scale=1 give the layout viewport the width of the ideal viewport? Yes Yes Yes Yes Yes Yes Yes portrait
  • IE10 applies the portrait width (320) to both landscape and portrait modes.
screen.width/height
Do screen.width/height give the dimensions of the ideal viewport?
Portrait Portrait No No width width Yes No
  • Safari always gives the portrait ideal viewport.
  • Chrome and Opera give the available height (screen height minus toolbars and such). Width is correct, though.
  • Android and IE give the physical number of pixels on the screen.

Layout viewport width

Before rendering the page, the browser needs to know how wide the layout viewport is. This is the viewport relative to which CSS declarations such as width: 20% are calculated.

Without any further instructions the browsers pick a width themselves. In six of the eight tested browsers this is 980px, in BlackBerry and IE10 it’s 1024px. There’s no right or wrong here; this is just the browser vendors making a choice.

When you use width=400 or another numerical value in the meta viewport tag, the layout viewport’s width is set to that value. We already knew that.

However, Android WebKit’s and IE’s minimal viewport is 320px. When you go below 320px they revert to the ideal viewport width.

Then there’s the case when the layout viewport becomes equal to the ideal viewport. This happens when you do width=device-width or initial-scale=1. It’s complicated, since there are bugs in Safari and IE10 and there’s a catch to using initial-scale, but this is the general rule.

Minimum and maximum dimensions

The maximum width of the layout viewport is 10,000 pixels. I don’t entirely trust that number, since the browsers do not allow you to zoom out to this amount. Still, for now I accept this official value.

The minimum width of the layout viewport is about 1/10th of the ideal viewport, which is also the maximum zoom level. (I.e. the layout viewport never becomes smaller than the smallest possible visual viewport.) Exceptions: Android WebKit and IE, which never go below 320px.

Compatibility - layout viewport

Here is all of the above in easy table format.

Test iPhone iPad Android Samsung Android HTC Chrome Opera Presto BlackBerry IE
The default dimensions of the layout viewport without any meta viewport 980 980 980 980 980 980 1024 1024

There is no right or wrong here; any value will do.

In order to find the maximum layout viewport width 10,000 10,000 10,000 10,000 10,000 10,000 10,000 1,024

When a value above 10,000 is applied, all browsers stay at 10,000, except for Android WebKit, which reverts back to the 980px default.

In order to find the minimum layout viewport width 64/96 154/205 Incorrect Incorrect 120/192 48/64 68/114 Incorrect
  • Below a declared width of 320px Android WebKit and IE take the ideal viewport size.
width=x
Some normal width values:
width=200
width=300
width=400
width=1200
Yes Yes Incorrect below 320 Incorrect below 320 Yes Yes Yes Incorrect below 320

The browser should make the layout viewport as wide as the width directive prescribes.

  • The iPad makes the width=200 205px wide in landscape. This is consistent with its minimum layout viewport width.
  • Below 320px Android WebKit and IE take the ideal viewport size as their minimum layout viewport width.

Zoom

Zoom is tricky. In theory it sounds simple: determine the zoom factors that the user can zoom in or out to. The problem is two-fold:

  1. We cannot directly read out the zoom factor. Instead, we have to read out the width of the visual viewport, which has an inverse relation to the zoom factor. The larger the zoom factor, the smaller the visual viewport width.
    So the minimum zoom factor determines the maximum visual viewport width, and vice versa.
  2. It turns out that all zoom factors are relative to the ideal viewport, no matter what the current size of the layout viewport is.

Then there is the matter of the name. In Apple-speak, zoom is scale, and the meta viewport directives are thus called initial-scale, minimum-scale, and maximum-scale. The other browsers were forced to comply in order to retain compatibility with iPhone-specific websites.

The three directives expect a zoom factor, where for instance 2 means “zoom to 200% of the ideal viewport width.”

Formulas

Let’s define the formulas first:

visual viewport width = ideal viewport width / zoom factor
zoom factor = ideal viewport width / visual viewport width

Thus, with a ideal viewport width of 320px and a zoom factor of 2 we get a visual viewport width of 160px. The width of the layout viewport plays no part in this calculation.

Minimum and maximum zoom factors

I did some extra tests on the Huawei C8813, Android 4.1.1, because it has a landscape ideal viewport width of 569.

Here, too, the minimum and maximum zoom factors were 0.25 and 4. So these powers of 4 are actually a general Android WebKit rule, and not just an artifact of the specific 640px width of the Samsung and HTC test phones.

The maximum visual viewport width here is 2277px, which is about 4 x 569.

What are the minimum and maximum zoom factors the browsers support?

First, a restriction. The visual viewport can never become wider than the layout viewport, so in most practical cases the minimum zoom factor is ideal viewport width / layout viewport width.

Still, in these tests I can use absurd layout viewport widths such as 5,000. I did so, and the results are:

There are slight differences in these factors; see the table below.

So in theory the iPhone’s visual viewport width can be anything between 32px (zoom factor 10) and 3200px (zoom factor 0.1).

Compatibility - zoom

Here is all of the above in easy table format.

Test iPhone iPad Android Samsung Android HTC Chrome Opera Presto BlackBerry IE
The width of the visual viewport at maximum zoom (zoomed in), without any scale directives.

Try also width=device-width
p: 65
l: 97
p: 154
l: 206
p: 100
l: 160
p: 90
l: 160
p: 121
l: 193
p: 48
l: 64
p: 70
l: 116
p: 48
l: 80
  • Without any meta directive Opera’s values are 24 and 32.
  • Without any meta directive BlackBerry’s values are 192 and 320.
Maximum zoom factor without directives
ideal viewport size / mimimum visual viewport size
5 5 4 4 about 5 5 about 5 about 6

Opera and BlackBerry have two factors: one with and one without width=device-width.

  • Chrome and BlackBerry use slightly different values for portrait and landscape
  • IE uses 6 for landscape and 6 and 2/3 for portrait
Minimum zoom factor without directives
width=5000 without any other instructions. Thus we can zoom out until we’ve reached the minimum factor.
0.25 0.25 0.25 of landscape 0.25 of landscape 0.25 0.2 about 0.25 untestable
  • Android WebKit has a maximum visual viewport width of 2560px, which is 4x640 (landscape). It also uses this value for portrait mode.
  • IE has a maximum visual viewport width of 1024px because that’s the maximum width of the layout viewport.
Maximum zoom factor with directives
maximum-scale=20 without any other instructions. Browsers stay well below the 20.
10 10 4 4 10 10 10 about 6
  • IE uses 6 for landscape and 6 and 2/3 for portrait
Minimum zoom factor with directives
width=10000,minimum-scale=0.01In order to find the absolute minimum zoom factor the browser supports. Browsers stay well above the 0.01.
0.1 0.14 0.25 of landscape 0.25 of landscape 0.1 0.1 0.111 untestable
  • Android WebKit has a maximum visual viewport width of 2560px, which is 4x640 (landscape). It also uses this value for portrait mode.
  • IE has a maximum visual viewport width of 1024px because that’s the maximum width of the layout viewport.

initial-scale

Setting he initial-scale directive actually does two things:

  1. It sets the initial zoom factor of the page to the defined value, calculated relative to the ideal viewport. Thus it generates a visual viewport width.
  2. It sets the layout viewport width to the visual viewport width it just calculated.

So let’s say we have an iPhone in portrait mode and give it an initial-scale=2 without any further instructions. By now, it shouldn’t surprise you that this sets the visual viewport width to 160px (= 320 / 2). That’s how the scaling directives work.

However, it also sets the width of the layout viewport to 160px. So we now have an 160px wide page in minimum zoom. (The visual viewport cannot become larger than the layout viewport, so zooming out is not possible.)

And no, this doesn’t make the slightest bit of sense. If asked for my candid opinion I’d probably mumble something like “completely fucking batshit insane.” Still, there’s no doubt that browsers behave like this.

Browser bugs

Except for Android WebKit. Obviously. Android WebKit allows initial-scale to set the layout viewport width only if the value is 1 AND there is no width directive. So only initial-scale=1 without any other directives works.

As to IE, it applies the wrong ideal viewport (320x320 instead of 320x480), and also pretends any value is 1. So the value you give to the initial-scale doesn’t matter in IE.

Conflicting width directives

Since initial-scale sets the layout viewport width, you can now create conflicting directives:

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

What happens now? The browser gets conflicting orders. Let’s return to the iPhone 4S once more:

  1. initial-scale=1 tells it to set the layout viewport width to 320px portrait and 480px landscape.
  2. width=400 tells it to set the layout viewport width to 400px in both portrait and landscape.

The browser solves the problem by obeying the largest width in portrait or landscape. In our example the portrait layout viewport width becomes 400px (the larger of 320 and 400), and the landscape layout viewport width 480px (the larger of 480 and 400).

Makes sense? Actually it doesn’t, but browsers do it anyway.

In any case, what we have here is a min-width for the layout viewport. The meta tag above sets the min-width to 400px, but allows the browser to grow the layout viewport beyond that if device size and orientation require it.

I’m not sure if there’s any practical use for a min-width for the layout viewport, but if you need one, hey, it’s there!

Browser bugs

Android WebKit does not follow these rules. If the width equals device-width or is smaller than 320, Android WebKit always applies the ideal viewport width to the layout viewport. Above 320 it always obeys the width directive.

IE does not follow these rules above width=480, when it sets the layout viewport width to 1024px.

Compatibility - initial scale and width

Here is all of the above in easy table format.

Test iPhone iPad Android Samsung Android HTC Chrome Opera Presto BlackBerry IE
initial-scale
Without any other instructions
initial-scale=1
initial-scale=0.5
initial-scale=2
Yes Yes Incomplete Incomplete Yes Yes Yes Buggy

When you set initial-scale to any value, the browser scales the ideal viewport to that value, and then resizes the layout viewport to those values.

So initial-scale=2 on an iPhone gives a landscape width of 240 (480/2), which becomes the width of the layout viewport.

  • Android WebKit obeys the rule only with initial-scale=1. For any other value it applies the correct zoom level, but leaves the layout viewport untouched (i.e. it uses the default).
  • IE10 applies the same width (320) to landscape and portrait. Also, it pretends any value other than 1 is 1. (In other words, the 0.5 and 2 tests give exactly the same result as the 1 test.)
initial-scale
With width=device-width or a width of 320 or smaller

width=device-width
initial-scale=1
initial-scale=0.5
initial-scale=2

width=300
initial-scale=1
initial-scale=0.5
initial-scale=2
Yes Yes dips dips Yes Yes Yes No scale

Rule: the browser compares the width directive and the outcome of the initial-scale directive, and applies the highest resulting width to the layout viewport. This is done independently for portrait and landscape.

  • Android WebKit uses the ideal viewport as layout viewport.
  • IE ignores the scale factor; i.e. always pretends it’s 1.
initial-scale
With a width between 321 and 480

width=400
initial-scale=1
initial-scale=0.5
initial-scale=2
Yes Yes width width Yes Yes Yes No scale

Rule: the browser compares the width directive and the outcome of the initial-scale directive, and applies the highest resulting width to the layout viewport. This is done independently for portrait and landscape.

  • Android WebKit obeys the width.
  • IE ignores the scale factor; i.e. always pretends it’s 1.
initial-scale
With a width of 481 or higher

width=750
initial-scale=1
initial-scale=0.5
initial-scale=2

width=1200
initial-scale=1
initial-scale=0.5
initial-scale=2
Yes Yes width width Yes Yes Yes no scale; 1024

Rule: the browser compares the width directive and the outcome of the initial-scale directive, and applies the highest resulting width to the layout viewport. This is done independently for portrait and landscape.

  • Android WebKit obeys the width.
  • IE ignores the scale factor; i.e. always pretends it’s 1. It also makes the layout viewport width 1024px.

Minor iPhone bug

I found a minor bug in the iPhone, but not the iPad:

  1. If a combination of width and initial-scale causes the browser to automatically zoom in in landscape mode (i.e. visual viewport is smaller than layout viewport)
  2. AND the user zooms out in landscape mode and then switches to portrait mode
  3. the minimum-scale for portrait equals the minimum-scale for landscape (i.e. the viewport width) times the portrait/landscape ratio. (So a landscape viewport width of 400 causes a portrait minimum-scale of 268.)
  4. Solution: zoom in as much as possible in portrait. The bug disappears.

Try it here. Hold your iPhone in landscape, go to the page, and follow the instructions above.

minimum- and maximum-scale

I did only one small series of tests for minimum-scale and maximum-scale. They generally seem to work quite well, except on Android WebKit, which doesn’t support minimum-scale, and IE, which makes a horrific mess of things — so badly, in fact, that I’ve given up trying to understand what’s going on.

What’s supposed to happen in the tests below is that the layout viewport width is calculated as described above, and after that zooming is restricted to between 50% and 200%, i.e. the visual viewport can become from twice as large to twice as small as the ideal viewport.

One exception: the visual viewport can never become smaller than the layout viewport.

Test iPhone iPad Android Samsung Android HTC Chrome Opera Presto BlackBerry IE
minimum-scale=0.5,maximum-scale=2
no other instructions
width=device-width
width=400
width=750
width=400,initial-scale=1
width=400,initial-scale=2

minimum-scale=1,maximum-scale=2
no other instructions
width=750
Yes Yes Only max Only max Almost Yes Yes No! Buggy! Terrible!

Expected result: the layout viewport width is calculated as above, and zooming is restricted to between 50% and 200% of the ideal viewport width.

Exception: zooming out is only possible until the width of the layout viewport.

  • Android WebKit does not support minimum-scale.
  • Chrome allows zooming up to 975px when the layout viewport is 962px wide.
  • IE:
    • applies the 320x320 device-width layout viewport in the absence of a width or initial-scale directive.
    • does not pay the minimum- and maximum-scale any attention
    • uses a scale factor of 10 on all pages where the layout viewport becomes 400, but I have no fucking clue why and I give up.