Best Way to Programmatically Zoom a Web Application
Website accessibility has always been important, but nowadays, when we have clear standards and regulations from governments in most countries, it’s become even more crucial to support those standards and make our projects as accessible as they can be.
The W3C recommendation provides 3 level of conformance: A
, AA
and AAA
. To be at the AA
level, among other requirements, we have to provide a way to increase the site’s font size:
1.4.4 Resize text: Except for captions and images of text, text can be resized without assistive technology up to 200 percent without loss of content or functionality. (Level AA)
Let’s look at solutions for this and try to find the best one we can.
Incomplete Solution : CSS zoom
The first word which comes up when we talk about size changing is zoom. CSS has a zoom
property and it does exactly what we want — increases size.
Let’s take a look at a common design pattern (that we’ll use for the rest of this article): a horizontal bar of navigation that turns into a menu icon at a certain breakpoint:

The GIF below shows what we get with zoom
approach applied to the menu element. I created a switcher which allows selecting different sizes and applies an appropriate zoom level:

The menu goes outside visible area because we cannot programmatically increase viewport width with zoom nor we can wrap the menu because of the requirement. The menu icon doesn’t appear either, because the screen size hasn’t actually changed, it’s the same as before we clicked the switcher.
All these problems, plus, zoom
is not supported by Firefox at all anyway.
Wrong Solution: Scale Transforms
We can get largely the same effect with transform: scale
as we got with zoom
. Except, transform
is more widely supported by browsers. Still, we get the exact same problems as we got with zoom
: the menu doesn’t fit into the visible area, and worse, it goes beyond the vertical visible area as well because page layout is calculated based on an initial 1-factor scale.
See the Pen Font-switcher–wrong-scale by Mikhail Romanov (@romanovma) on CodePen.
Another Incomplete Solution : rem
and html
font-size
Instead of zooming or scaling, we could use rem
as the sizing unit for all elements on the page. We can then change their size by altering html
element’s font-size
property, because by its definition 1rem
equals to html
‘s font-size
value.
This is a fairly good solution, but not quite perfect. As you can see in the following demo, it has the same issues as previous examples: at one point it doesn’t fit horizontally because required space is increased but the viewport width stays intact.
See the Pen Font-switcher–wrong-rem by Mikhail Romanov (@romanovma) on CodePen.
The trouble, in a sense, is that the media queries don’t adjust to the change in size. When we go up in size, the media queries should adjust accordingly so that the effect at the same place would happen before the size change, relative to the content.
Working Solution: Emulate Browser Zoom with Sass mixin
To find inspiration, let’s see how the native browser zoom feature handles the problem:

Wow! Chrome understands that zooming actually does change the viewport. The larger the zoom, the narrower the viewport. Meaning that our media queries will actually take effect like we expect and need them to.
One way to achieve this (without relying on native zoom, because there is no way for us to access that for our on on-page controls as required by AA) is to somehow update the media query values every time we switch the font size.
For example, say we have a media query breakpoint at 1024px
and we perform a 200%
zoom. We should update that breakpoint to 2048px
to compensate for the new size.
Shouldn’t this be easy? Can’t we just set the media queries with rem
units so that when we increase the font-size
the media queries automatically adjust? Sadly no, that approach doesn’t work. Try to update media query from px
to rem
in this Pen and see that nothing changes. The layout doesn’t switch breakpoints after increasing the size. That is because, according to standards, both rem
and em
units in media queries are calculated based on the initial value of html
element font-size
which is normally 16px
(and can vary).
Relative units in media queries are based on the initial value, which means that units are never based on results of declarations. For example, in HTML, the
em
unit is relative to the initial value of ‘font-size
.
We can use power of Sass mixin
s to get around this though! Here is how we’ll do it:
- we’ll use a special class on
html
element for each size(font-size--s
,font-size
--
m
,font-size--l
,font-size
--
xl
, etc.) - we’ll use a special
mixin
, which creates a media query rule for every combination of breakpoint and size and which takes into account both screen width and modifier class applied tohtml
element - we’ll wrap code with this
mixin
everywhere we want to apply a media-query.
Here is how this mixin
looks:
$desktop: 640px;
$m: 1.5;
$l: 2;
$xl: 4;
// the main trick is here. We increase the min-width if we increase the font-size
@mixin media-desktop {
html.font-size--s & {
@media (min-width: $desktop) {
@content;
}
}
html.font-size--m & {
@media (min-width: $desktop * $m) {
@content;
}
}
html.font-size--l & {
@media (min-width: $desktop * $l) {
@content;
}
}
html.font-size--xl & {
@media (min-width: $desktop * $xl) {
@content;
}
}
}
.menu {
@include media-desktop {
&__mobile {
display: none;
}
}
}
And an example of the CSS it generates:
@media (min-width: 640px) {
html.font-size--s .menu__mobile {
display: none;
}
}
@media (min-width: 960px) {
html.font-size--m .menu__mobile {
display: none;
}
}
@media (min-width: 1280px) {
html.font-size--l .menu__mobile {
display: none;
}
}
@media (min-width: 2560px) {
html.font-size--xl .menu__mobile {
display: none;
}
}
So if we have n breakpoints and m sizes, we will generate n times m media query rules, and that will cover all possible cases and will give us desired ability to use increased media queries when the font size is increased.
Check out the Pen below to see how it works:
See the Pen Font-switcher–right by Mikhail Romanov (@romanovma) on CodePen.
Drawbacks
There are some drawbacks though. Let’s see how we can handle them.
Increased specificity on media-query selectors.
All code inside the media query gets additional level of specificity because it goes inside html.font-size — x
selector. So if we go with the mobile first approach and use, for example, .no-margin
modifier on an element then desktop normal style can win over the modifier and desktop margins will be applied.
To avoid this we can create the same mixin
for mobile and wrap with our mixins
not only desktop but also mobile CSS code. That will balance specificity.
Other ways are to handle every special case with an individual approach by artificially increasing specificity, or creating mixin
with desired functionality(no margin in our example) and putting it not for mobile only but also into every breakpoint code.
Increased amount of generated CSS.
Amount of generated CSS will be higher because we generate same CSS code for every size.
This shouldn’t be an issue if files are compressed with gzip (and that is usually the case) because it handles repeated code very well.
Some front-end frameworks like Zurb Foundation use built-in breakpoints in JavaScript utilities and CSS media queries.
That is a tough one. Personally, I try to avoid the features of a framework which depends on the screen width. The main one which can be often missed is a grid system, but with the rise of flexbox and grid, I do not see it to be an issue anymore. Check out this great article for more details on how to build your own grid system.
But if a project depends on a framework like this, or we don’t want to fight the specificity problem but still want to go with AA, then I would consider getting rid of fixed height elements and using rems
together with altering the html
element font-size
to update the layout and text dimensions accordingly.
Thank you for reading! Please let me know if this helps and what other issues you faced conforming to the 1.4.4 resize text W3C Accessibility requirement.
Best Way to Programmatically Zoom a Web Application is a post from CSS-Tricks
LEAVE A REPLY
You must be logged in to post a comment.