There’s no such thing as a desktop screen

Take the responsiveness to the next level

Hajime Yamasaki Vukelic
Bootcamp

--

A photograph of a pair of glasses in front of a vision testing chart
Photo by David Travis on Unsplash

My father, an old dude well into his 80’s, uses a 13" Panasonic business laptop — yes, they make them, no you can’t get them outside Japan — with an FXGA resolution display (1366×768). His eyesight isn’t what it used to be, and even when wearing glasses, he finds it difficult to read web pages. By default, the display was set to 125% scale, but soon we bumped it up to 150%. Then it turned out that on some web pages, he still needs to zoom in to up to 200% to see things. That’s effectively a 300% zoom. What does that mean in practical terms? It means that your page is less than 450px wide when my dad uses it… on a laptop.

I’ll wait for you to ponder about it…

Next time you think “this is a desktop-only app”, think again. Responsiveness is a bit more nuanced than that.

And did I mention that I’ve set the default font size to “Large”? 😁

Over the years, I’ve learned various techniques that help me deal with all of this, and drawn some conclusions about the prevalent approaches in use today. Let me give you a quick tour of what responsive means to me, and the basic principles of high-quality robust layouts that will take some pounding without falling apart. Hopefully it will work for you too.

Screen-based breakpoints are useless

I sometimes see people making recommendations like this:

@media (max-width: 599px) { /* phone */

}

@media (min-width: 600px) { /* large phone */

}

@media (min-width: 900px) { /* tablet */

}

@media (min-width: 1200px) { /* laptop */

}

@media (min-width: 1800px) { /* desktop */

}

They back these values up with meticulous research on screen sizes derived from some browser usage stats, with fancy charts and all. However, all of that is completely useless, because, what you really want is not screen size, but the size of the viewport! And that changes based on various factors. Zoom, windows size, display scale, etc. Now, even if we ignore the display scale and treat it as a physical display size, we still have two factors affecting the viewport, which is two too many.

If you go to GitHub, and you zoom it in all the way to 500%, you see that it basically goes into what we’d normally call “mobile layout”.

Screenshot of the GitHub dashboard taken on a laptop screen that appears like it was opened on a mobile device
GitHub on a “laptop screen”, zoomed in 500%

The layout looks like something you’d find on a mobile device, but here’s the catch. It’s extremely short. It’s still a horizontal (landscape) format! Hopefully this drives the point home about phrases like “laptop” and “tablet” being completely irrelevant for responsiveness.

Who uses 400%~500% zoom anyway? Well, we already said my dad uses effectively a 300% zoom. But even if we don’t have problems with vision, we may still zoom in. For instance, I sometimes use a 400% zoom on my 27" 4K monitor (already scaled to 125%) because sometimes I’m chilling in my sofa some 2m away from it with a Bluetooth mouse, and I just want to check something real quick. I can’t see anything without zooming in 400%. Is this a first world problem? Yes, of course it is. I just wanted to let you know that it’s not just people with reduced ability that have a need for web pages to do what is expected when you use the zoom.

The point is this, though: people do things you wouldn’t expect for all kinds of reasons. It’s not just about “accessibility” and “people with disabilities”. It’s high quality craftsmanship if your stuff just works™️.

And if engineering pride and the wish to make quality stuff isn’t swaying you, you can consult the official guideline for Web accessibility.

Adding font size to the mix

Now let’s add to all of this the font size. If you do everything in pixels, what happens if you change the default font size of the browser?

Consider this code:

button {
font: inherit;
color: inherit;
cursor: pointer;
height: 32px;
padding: 6px 16px;
border: 1px solid #400;
border-radius: 4px;
background: #dee;
}

It looks alright at the default font size.

Screenshot of a button at normal font size

But what if we change the font size to “Large” in Chrome? Now the button is all wonky.

Button at “Large” font size

That doesn’t look very high-quality does it?

We can “fix” this by specifying the font size of 16px (which is the default in most browser) on the <body>. That would scale the interface back to its “original” state. This fix has an undersirable side-effect. It also prevents the user-specified font size from taking effect.

When someone goes through the trouble of changing the font size, it does not take a psychologist to determine that they very likely expect their action to have some effect. This “fix” of specifying the 16px font size on <body> is no good. It’s not responding to the user’s font size change — it’s not… responsive.

The problem with our button is that its dimensions did not scale with the font size change. That’s the root issue that we should be addressing. CSS offers us a whole slew of text-relative units — units that change with text metrics. Here’s a concise list that is directly tied to the font size:

  • em — the font size of the element (including an inherited one)
  • rem — same as 1em on the <html> element — if you don’t specify a font size on the <html> element (don’t), it should match the browser’s default font size
  • ch — roughly the width of the number 0 using the current font of the element (or an inherited one)
  • ex — represents the height of the character x using the current font of the element (or an inherited one)

Of these, the em and rem are most typically used. Quite expectedly, as with anything that can be implemented in multiple different ways, there are religious battles between people defending one approach against another. I’m just going to show the fixed CSS using em and note that replacing em with rem would have the exact same result in this particular case:

button {
font: inherit;
color: inherit;
cursor: pointer;
height: 2em;
padding: 0.3em 1em;
border: max(1px, 0.07em) solid #400;
border-radius: 0.25em;
background: #dee;
}

With this change, the default font size affects all aspects of the button’s appearance making it scale up evenly, retaining the proportions.

Screenshot of the button that scaled evenly after fixing the CSS

How do I calculate the correct rem and em values? Well, for the most part I don’t. I just eyeball it and make it look about right. Since these two units are based on the font size, I just look at the text and “Ok, about half the text size.” Get it into the ballpark and then fine-tune. If I’m working off a Figma design, I do about the same. This is a skill. It can be learned.

Now I get that a some people reading this can’t do this yet. It requires visual training. In case it doesn’t quite work for you, and you find yourself wanting to go back to good ol’ sweetheart, pixels, I’ll give you another option later on.

Speaking of font sizes, from the accessibility standpoint, there are no prescribed sizes that you should be using on your page. This is partly because the user can control the default font size and an accessible web page is expected to honor that. However, do remember that 14px is small for a lot of situations (situation = person’s ability + equipment + viewing conditions). If you need to present text that is smaller than the default, don’t go below 87.5% (or 0.875em / 0.875rem) of the default size ever (that’s 14px at the browser’s factory default in most cases). Remember, that’s 14px for the smallest text on the page!

Responding properly

In programming, there’s not much that you can objectively declare as “proper”. Most of what we say is essentially just opinions, or at least come with a bag of trade-offs so they are not universally proper. However, there are most certainly things that you can objectively say are proper — things that make a difference between something working or not.

In the case of responsive layouts, (text-)relative units are the proper way to go about it. And you can’t just do it “in most places”. It must be be all-in. Here’s what happens when someone doesn’t get this right.

Screenshot of the LinkedIn toolbar with broken layout due to a mix of pixel and text-relative units

Someone messed up at LinkedIn. They mixed text-relative and pixel values and this is what you get — the opposite of graceful and high-quality.

To recap: use the text-relative units everywhere — including media queries. Period.

There’s a few exceptions where you might use %, vw, vh and similar relative units. In my usage, these comprise less than 5% of the length units, and mostly related to layout. YMMV.

“But, but, everyone is doing pixels! How could this be!” Sigh… Yes, most developers are using pixels. No, most developers are not the best developers who consistently and deliberately produce excellent results. The best developers are probably less than 5% of the global developer population — I recall reading this somewhere, but don’t hold me to it .

I’m not trying to say “I’m the top X%, so you’d better do what I tell you to!” Rather, what I’m saying is that if you base your opinion on what the 95% of the average and below-average developers are doing, you’ll end up with an average or below-average result. Rather than borrowing other people’s opinion, maybe it’s better to base it on what actually works and what doesn’t, given the concrete requirements. Again, YMMV.

Ok, moving on.

Going (back) to the realm of opinion, I have two more suggestions, in order to make responsive layouts truly responsive without working too hard.

First, master the flexbox. I’ve already written about using flexboxes to create responsive layouts. Apparently, you can also use grids in a similar way, but I haven’t really mastered that technique yet, partly due to flexboxes working so well most of the time. If you find some good resource that doesn’t use examples with mixed text-relative and pixel units, please let me know. 😉

The second suggestion is to use media query breakpoints based on content rather than arbitrary screen sizes. It’s a simple technique, and surprisingly effective. What you do is you basically change the viewport width until something starts looking suspicious or outright breaks. You then back off a bit until it restores to normal. That’s your breakpoint for the particular thing that broke. Think “breakpoint is a point where things break”. You then convert the pixel width of the viewport into rem by dividing it by 16 (so if the breakpoint is 1118px then you get 70rem, rounded).

@media (width < 70rem) {
.broken-element {
flex-direction: column;
align-items: stretch;
}
}

(Yes, you can totally use rem and em in media queries!)

The idea here is that you address each part of the page separately and you get an UI that has a fairly robust support for any screen size. Sometimes you will want to address different parts of the page at the same breakpoint, but that’s just a matter of adjusting the breakpoints by taking the WCD (widest common denominator… it’s a joke 😝).

This should cover virtually all viewport- and font-size-related responsiveness you could run into.

That’s not all

Users may use their custom stylesheets, too. Now, you can’t possibly account for everything the user could do with that. No way. However, they don’t use those stylesheets for everything either.

The most critical use of the custom CSS is for readability. Users may decide to expand the letter spacing, increase the line height, or add more inter-paragraph spacing. This is usually due to a reduced cognitive ability, but, really, it doesn’t matter why they do it, we still need to make sure our page can respond to such overrides, at least to a reasonable degree.

On Chrome and Chrome-based browsers, we have an extension called Text Spacing Editor.

Screenshot of the Text Spacing Editor extension in the Brave browser

This extension lets us simulate the way in which users may override our page styles. What we do with it is, we turn on the overrides, and we make sure that nothing breaks when the custom styles are applied.

Not messing too much with how browser normally lays things out (expands the box vertically as content grows, for instance) is the best advice I can give you. If you give stuff explicit heights, for instance, think about whether you absolutely must do that, or maybe a flex or a grid container would be a better solution.

Remember, without CSS, the page is 100% responsive. You are breaking that with your CSS, so the idea here is that you want to minimize the breakage, rather than fix the broken stuff.

But, but Figma!

The reality is that we still have UX designers out there that are not fully qualified to work on web UI projects. They just love their pixels. And even when they hate them, the tools they use are hard-wired to do pixels. Even if you master the text-relative units, you may still find it tedious to convert between pixels and rem or em, especially if you can’t eyeball them like I can. Or the said designer starts nitpicking about two pixels here or a pixel there in completely text-relative layout... 😭 I get it. It’s not easy.

If you really can’t do it the right way, consider the following solution.

There’s a PostCSS plugin called pxtorem. As the name suggests, it processes your CSS and converts all px values to rem. If you’re going to use this tool, however, please consider two things:

  1. It’s most likely abandonware, so you may need to fix bugs yourself.
  2. By default it only converts a handful of properties, so you will still run into issues.

My recommendation is to use the following settings:

pxtorem({
propList: ['*'],
mediaQuery: true,
})

Now you can use pixels everywhere, meaning you can use the Figma values directly, as specified by the designer. You can also use pixels in media queries too. When you change the default font size, the layout should scale correctly, because the processed CSS is in rem.

Now, this recommendation comes with two caveats:

  • Unlike em, this technique will not reduce the overall amount of CSS you need for the layout. The em can save you quite a bit of work if you do it right.
  • I’ve never used it on a project beyond just testing it a little, so I can’t say it if has any undesirable side-effects.

Conclusion

Responsiveness is not about a simple set of arbitrary screen widths based on even more arbitrary browser usage stats. Responsiveness is about robustness: how much can you stretch or squeeze the page before it starts falling apart.

A high-quality web UI is, among other things, extremely robust. It manages to convey the information with consistency regardless of the viewport size (down to a reasonable limit) or font size preferences. And there are many factors that influence this, including zoom, window size, font size, and arbitrary user-inflicted stylesheets.

Taking all of that into account and coming up with “pixel perfect” solutions is, if you ask me, an exercise in masochism. Rather, we have to learn how to use a few simple tools that will cover all (or at least a virtually all) real-life cases without having to worry about any specific ones. That sometimes takes us to paths seldom taken, but if that’s what’s needed, it’s the valid path. 😉

--

--

Helping build an inclusive and accessible web. Web developer and writer. Sometimes annoying, but mostly just looking to share knowledge.