For a while I've been talking about how I am not a fan of the Derekstrap solution for text scaling (using vw), but have not been clear about what is wrong with it, or how I would propose fixing it. I have organized my thoughts and a few references on what is wrong with using vw units for the base font size of a website.
The w3 defines 2 categories of length units:
- relative lengths, of which there are 2 sub-categories:
- font-relative lengths (eg.
em
,rem
) - viewport-relative lengths (eg.
vh
,vw
,vmin
)
- font-relative lengths (eg.
- absolute lengths (eg.
px
,cm
,in
)
*The percentage dimension is not a length and has different values depending where it's used. More on that later.
These units each behave differently and have different base calculations. Some are obvious and some are less obvious.
Font-relative lengths are the only type of length defined as being based on the user's chosen font size configuration. These sizes will respond properly to system font size and browser zoom, and have a predictable size based on that defined by the user (for most users, who have not customized their font size, this is just the system size). As far as I know, all browsers know how to deal with font-relative lengths properly, though there are some browser bugs around using em
in pseudo-elements (however, rem
does not have these issues).
Viewport relative lengths are tied directly to the user's viewport size and do not normally respond to user configuration or browser zoom, and thus are a problem for users who have configured a larger font size - their preference may not be reflected. When using vw units, the user's chosen font size configuration is normally entirely ignored, and zoom behaviour is unpredictable, being implemented differently across browsers.
Absolute lengths are complicated.
Originally the px
unit was as straightforward as it seems (1px
= one pixel on your device, right?), but over time it has evolved along with our devices to be more complex. In the CSS3 spec, the px
unit is called the visual angle unit
. Browser and device manufacturers may implement px
either based on physical measurements where 1px = 1/96th of 1in
, or by using the reference pixel which is the visual angle of one pixel on a device with a pixel density of 96dpi.
Complicated, right?!? If you think a pixel is a pixel is a pixel, that probably hasn't been true in 20 years or so (source needed!!).
I thought this section from the w3 spec on absolute lengths was enlightening:
All of the absolute length units are compatible, and px is their canonical unit.
For a CSS device, these dimensions are anchored either
- by relating the physical units to their physical measurements, or
- by relating the pixel unit to the reference pixel.
For print media at typical viewing distances, the anchor unit should be one of the standard physical units (inches, centimeters, etc). For screen media (including high-resolution devices), low-resolution devices, and devices with unusual viewing distances, it is recommended instead that the anchor unit be the pixel unit. For such devices it is recommended that the pixel unit refer to the whole number of device pixels that best approximates the reference pixel.
The percent unit (%
) behaves differently depending on where they are used, and so are a versatile unit. font-size
defined in percent units behaves like em
, but width
defined in percent units uses the width
of the parent and is not related to any font-size
. Contrast this with a width
defined in rem
units, which will correspond to the current base font size and does not have awareness of the parent's width
.
Using font-relative lengths (rem
) for base font size is sensible, predictable, and good for accessibility.
If you consistently use font-relative lengths (rem
), your text will always be readable at the size that the user or device manufacturer intended, and zoom behaviour will be predictable.
Using viewport-relative lengths (vw
) for base font size is sensible and predictable, but bad for accessibility (font size cannot be customized by the user).
If you consistently use viewport-relative lengths, some browsers will not properly respond to zoom behaviour, and you will completely clobber the user/device's preferred text size.
In decorative settings this can be good (eg. setting an element with a decorative background image to width: 50vw
so that it always fills half the screen), but as a base font size, you have deliberately chosen to ignore the user/device's text size or scaling preferences. This is a terrible accessibility choice and causes unpredictable behaviour across browsers.
Absolute lengths (px
), in my opinion, are confusing and far more complicated than most developers realize. Many developers know that, on mobile devices and high-DPI displays, 1px
on a webpage may have a different definition than exactly one hardware pixel, but think that it's as simple as dpi or device pixel ratio. Most developers are unaware that px
may have a different definition, for example, on non-traditional devices such as televisions and projectors. On those devices, sometimes the visual angle may be used.
If you consistently use absolute lengths (px
), everything will probably be fine, but figuring out what this size actually means for different devices can be confusing. 1px
is not a hardware pixel and you cannot rely on this measurement.
You should use font-relative lengths for font sizes and containers holding text (you can roughly predict and limit line length this way!). The base font size should always be 1rem
. If you set it to something different, you may be clobbering the user's configured font size, and if the user is depending on this in order to read text, you may have made your webpage unusable for them.
You can use viewport-relative lengths with caution and deliberately, but you should never set the base font size to a viewport-relative length. This destroys accessibility and device/user font size/scaling.
You can use absolute lengths, but please remember that 1px
on a webpage is not a device pixel, and the 16px = 1rem
rule isn't always true. The definition of a visual pixel on web pages is complicated and I think that you should be using rem
instead especially if you do not feel like you really understand px
.
Repeating what I said in Slack: