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.
Update(09/25/17): It turns out that WCAG doesn’t require a text resizing custom solution in addition to what user-agents provide. We just have to make sure a website looks OK as a result of resizing. Yet if we still want to scale elements for any reason, below is a thorough analysis of different methods for doing that.
zoom
Incomplete Solution : CSS 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.
rem
and html
font-size
Another Incomplete Solution : 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.
mixin
Working Solution: Emulate Browser Zoom with Sass 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.
I’ve done a good amount of work recently trying to programmatically detect if the user has zoomed using their browser controls (key commands or by the menu). This was affecting the way a WYSIWYG application was rendering text. Unfortunately, because of retina screens and other mobile devices, there’s no way to determine if the browser has been zoomed right now. Most answers will reference
window.devicePixelRatio
but this will fail depending on the device and the zoom level. It looks like this ratio is set near arbitrarily by the device manufacturer in order to shove more pixels into a device.Having implemented an on-page text resize widget I would be very cautious about suggesting it, given the significant complexities in getting the layout to scale correctly with the text.
According to the WCAG guidelines a custom text-resize widget is also not required to meet this criteria (unless supporting old browsers)
(https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html
So with this in mind I would focus on using ems/ rems correctly, and building a good responsive site to give browser’s native zoom the best possible chance.
Um.. I don’t think the WCAG is proposing that every website implement their own custom zoom controls – they just mean that you should test your site at various browser zoom levels.
Please provide the link to the specification when citing it:
https://www.w3.org/TR/WCAG20/#visual-audio-contrast-scale
Done, thanks for pointing out.
You really should try setting font size to something like “1.13vw” in a media query of min-width 50em
Hi, Eric. Unfortunately, I don’t get your point. Could you please add some more details like where I should do it and what result is expected?
As an aside, according to the spec’s example (https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html) this feature is only required if it must be supported on browsers that don’t support user agent zoom (IE6 for example, but not IE7+).
However, this is really a very cool solution to the issue.
Wow, good catch! Looks like according to the specs we actually can rely on browser native zoom.
However, in addition to IE6 we have at least:
– iOS Safari(I don’t see any way to zoom its content without using the Reader View which cuts off the embed Pen examples),
– and different web views(like browsing the web from Facebook or Inbox apps) which also don’t provide a zoom support.
It’s important to note that WCAG 2.0 doesn’t ask developers to add any kind of text-resize widget.
It is an interesting analysis of different ways developers can change the size of things, but it is very misleading to say that anyone should use them for the purpose of accessibility and/or WCAG conformance.
Would it be possible to add a note at the top (just below the 1.4.4 quote) that says something like:
“I originally wrote this thinking that it was required by WCAG, but later found that the requirement is for users to be able to change their setting (zoom or text-size), and the site should still work. However, it is still an interesting analysis of different methods of scaling elements” (Or something, you get the idea?)
You might be interested to learn that the next version of WCAG (2.1, due next year) might have an update to this requirement that explicitly asks that browser zoom can work up to a point where the width becomes equivalent to 320px. (Draft available for comment.)
Yeah, I’m definitely going to do that based on all the comments.
I wrote W3C an e-mail for clarification but no response so far.
Thanks for the feedback.
Where did you send it? I’m on the WCAG working group, I didn’t see it, but there are a lot of places these things can go! I think the suitable place would be public-agwg-comments (@w3.org), but we are working on wcag 2.1 at the moment so it might fall by the wayside.
It might be better to check our the updated guideline I linked to above, that changes the requirements anyway…
I sent to [email protected] but doesn’t matter since you are here.
So usage of the browser native zoom feature conforms to AA. That’s good news.
Could you please clarify about iOS Safari and web views like Facebook or Google Inbox iOS apps where we can view the website contents. I don’t see any way to zoom the content there
without requiring scrolling on more than one axis
as stated in the guideline update you provided.(well we can access reader view in Safari sometimes but it removes some content – for example embed Pens).
That’s why it is phrased as zooming to 320px wide, that is the ‘content requirement’, i.e. what the author/developer can control.
There isn’t currently support for zoom with reflow on iOS / Android (except Opera on Android that does allow some reflow), but without better support we can’t ask developers to work-around the issues in user-agents.
There is also a practical issue of it being a small size interface already, where would things go? There is some responsibility on the user for having a device that meets their needs, which for people with low vision, means a bigger screen. There might be some improvements to make here, but that will have to be for the next version.
Isn’t the solution as simple as don’t use pixels as a unit?
Use ems for media queries, and rems on elements.