Zooming and scaling must not be disabled
Many customers with low vision zoom in on their phone to read prices, product details and checkout fields. A viewport setting that blocks zooming (user-scalable=no or maximum-scale=1) leaves small text unreadable and can stop them from completing a purchase.
- Rule id
- meta-viewport
- Source
- axe-core 4.13.0
- Impact
- Moderate
- Fix tier
- AUTO
- on Shopify, Shopware, WooCommerce and Magento
- Status
- runs
What the check does
Ensure <meta name="viewport"> does not disable text scaling and zooming
The rule reads the content attribute of each <meta name="viewport"> element in the top-level document. Viewport tags inside iframes are not tested, and a tag with a missing or empty content passes.
axe-core splits the content at commas and semicolons into key=value pairs, ignores letter case and the spaces around keys and values, and fails the tag in three cases:
user-scalable=no- a numeric
user-scalablevalue greater than -1 and less than 1, such asuser-scalable=0 - a
maximum-scalebelow 2, wheremaximum-scale=yesis read as 1 and a negative value is ignored
Other keys, such as width, initial-scale and minimum-scale, play no part. The failure message names the setting that blocks zooming.
The rule only reads the markup and does not zoom the page, so it cannot tell whether text stays readable at 200 percent. Validiscan’s scanner groups findings by rule, selector and HTML shape, so the same viewport tag on every page of a theme becomes one finding.
How to fix it
Remove user-scalable=no and any maximum-scale value below 2 from the <meta name="viewport"> tag in the layout template; content="width=device-width, initial-scale=1" is enough.
WCAG success criteria
- 1.4.4 Resize Text, level AA
EN 301 549 clauses
| WCAG criterion | Version 3.2.1 | Version 4.1.1 |
|---|---|---|
| 1.4.4 | 9.1.4.4 | 9.1.4.4 |
Where a recipe fixes it
Shopify: recipe meta-viewport-zoom
Show the before and after (1)
layout/checkout.liquid Changes to layout/checkout.liquid: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 6 | 6 | <html class="no-js"> | |
| 7 | 7 | <head> | |
| 8 | 8 | <meta charset="utf-8"> | |
| 9 | Removed | <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> | |
| 9 | Added | <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| 10 | 10 | <meta name="robots" content="noindex"> | |
| 11 | 11 | <link rel="canonical" href="{{ canonical_url }}"> | |
| 12 | 12 | <!-- Checkout assets: //cdn.shopify.com/s/files/1/0712/3456/7890/t/12/assets/ (served locally by the fixture server) --> |
layout/theme.focused.liquid Changes to layout/theme.focused.liquid: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 10 | 10 | <head> | |
| 11 | 11 | <meta charset="utf-8"> | |
| 12 | 12 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| 13 | Removed | <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> | |
| 13 | Added | <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| 14 | 14 | <title></title> | |
| 15 | 15 | <link rel="canonical" href="{{ canonical_url }}"> | |
| 16 | 16 | <!-- Theme assets: //cdn.shopify.com/s/files/1/0712/3456/7890/t/12/assets/ (served locally by the fixture server) --> |
layout/theme.liquid Changes to layout/theme.liquid: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 11 | 11 | <head> | |
| 12 | 12 | <meta charset="utf-8"> | |
| 13 | 13 | <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| 14 | Removed | <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> | |
| 14 | Added | <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| 15 | 15 | <meta name="theme-color" content="{{ settings.color_background }}"> | |
| 16 | 16 | <link rel="canonical" href="{{ canonical_url }}"> | |
| 17 | 17 | {%- comment -%} On the live store theme assets load from //cdn.shopify.com/s/files/1/0712/3456/7890/t/12/assets/; the fixture serves them from {{ routes.root_url }}assets/. {%- endcomment %} |
Shopware: recipe meta-viewport-zoom
Show the before and after (1)
Resources/views/storefront/layout/meta.html.twig Changes to Resources/views/storefront/layout/meta.html.twig: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 6 | 6 | ||
| 7 | 7 | {% block layout_head_meta_tags_viewport %} | |
| 8 | 8 | {# Added by the merchant's agency to stop "zoom jumps" on iOS. #} | |
| 9 | Removed | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | |
| 9 | Added | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 10 | 10 | {% endblock %} | |
| 11 | 11 | ||
| 12 | 12 | {% block layout_head_meta_tags_general %} |
WooCommerce: recipe meta-viewport-zoom
Show the before and after (1)
header-checkout.php Changes to header-checkout.php: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 12 | 12 | <head> | |
| 13 | 13 | <meta charset="<?php bloginfo( 'charset' ); ?>"> | |
| 14 | 14 | <?php // VALIDISCAN-SEED meta-viewport: zooming is disabled. ?> | |
| 15 | Removed | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | |
| 15 | Added | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 16 | 16 | <?php wp_head(); ?> | |
| 17 | 17 | </head> | |
| 18 | 18 |
header.php Changes to header.php: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 14 | 14 | <head> | |
| 15 | 15 | <meta charset="<?php bloginfo( 'charset' ); ?>"> | |
| 16 | 16 | <?php // VALIDISCAN-SEED meta-viewport: zooming is disabled ("stops iOS zooming into inputs"). ?> | |
| 17 | Removed | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | |
| 17 | Added | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 18 | 18 | <?php wp_head(); ?> | |
| 19 | 19 | </head> | |
| 20 | 20 |
Magento: recipe meta-viewport-zoom
Show the before and after (1)
app/design/frontend/Validiscan/fixture/Magento_Theme/layout/default_head_blocks.xml Changes to app/design/frontend/Validiscan/fixture/Magento_Theme/layout/default_head_blocks.xml: 1 added, 1 removed
| Line before | Line after | Change | Code |
|---|---|---|---|
| 3 | 3 | <head> | |
| 4 | 4 | <!-- VALIDISCAN-SEEDED meta-viewport: pinch zoom disabled (Luma 2.0-2.3 default kept by the theme). | |
| 5 | 5 | Fixed variant: content="width=device-width, initial-scale=1". --> | |
| 6 | Removed | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"/> | |
| 6 | Added | <meta name="viewport" content="width=device-width, initial-scale=1"/> | |
| 7 | 7 | <meta name="format-detection" content="telephone=no"/> | |
| 8 | 8 | <css src="css/styles-m.css"/> | |
| 9 | 9 | <css src="css/styles-l.css" media="screen and (min-width: 768px)"/> |
Start with a free check of your shop.
Send your shop’s address. Validiscan scans it and replies with every failure the scan can prove, with the page and the code.
Request a free checkOr write to info@validiscan.com.
What the free check covers, and what it leaves out- You send
- your shop’s address
- Pages scanned
- up to 15
- You get
- every failure the scan can prove, with the page and the code
- On your shop
- public pages only; no login, nothing bought, no forms sent
- What it is not
- a full audit