An Interactive Guide to CSS Container Queries
Another terrific interactive tutorial from Ahmad, this time on container queries.
Another terrific interactive tutorial from Ahmad, this time on container queries.
Wouldn’t it be great if all web tools gave warnings like this?
As you generate and tweak your type scale, Utopia will now warn you if any steps fail WCAG SC 1.4.4, and tell you between which viewports the problem lies.
This is a wonderfully in-depth interactive explainer on touch target sizes, with plenty of examples.
Remember when I wrote about sizes="auto"
? Well, it’s coming to Chrome! Hallelujah!
As well as her personal site, wordridden.com, Jessica also has a professional site, lostintranslation.com.
Both have been online for a very long time. Jessica’s professional site pre-dates the Sofia Coppola film of the same name, which explains how she was able to get that domain name.
Thanks to the internet archive, you can see what lostintranslation.com looked like more than twenty years ago. The current iteration of the site still shares some of that original design DNA.
The most recent addition to the site is a collection of images on the front page: the covers of books that Jessica has translated during her illustrious career. It’s quite an impressive spread!
I used a combination of CSS grid and responsive images to keep the site extremely performant. That meant using a combination of the picture
element, source
elements, srcset
attributes, and the sizes
attribute.
That last part always feels weird. I have to tell the browser what sizes the images will displayed at, which can change depending on the viewport width. But I’ve already given that information in the CSS! It feels weird to have to repeat that information in the HTML.
It’s not just about the theoretical niceties of DRY: Don’t Repeat Yourself. There’s the very practical knock-on effects of having to update the same information in two places. If I update the CSS, I need to remember to update the HTML too. Those concerns no longer feel all that separate.
But I get it. Browsers use a look-ahead parser to start downloading images as soon as possible, so I understand why I need to explicitly state what size the images will be displayed at. Still, it feels like something that a computer should be calculating, not something for a human to list out by hand.
But wait! Most of the images on that page also have a loading
attribute with a value of “lazy”. That tells browsers they don’t have to download the images immediately. That sort of negates the look-ahead parser.
That’s why the HTML spec now includes a value for the sizes
attribute of “auto”. It’s only supposed to be used in conjunction with loading="lazy"
(otherwise it means 100vw
).
Browser makers are on board with this. You can track the implementation progress for Chromium, WebKit, and Firefox.
I would very much like to see this become a reality!
Michelle has written a detailed practical guide to container queries here.
This is a terrific walkthrough from Andy showing how smart fundamentals in your CSS can give you a beautiful readable document without much work.
Instead of thinking about responsive design in terms of media queries, I like to think of responsive design in these categories.
- Responsive to the content
- Responsive to the viewport
- Responsive to the container
- Responsive to the user preferences
Like a little mini Utopia:
Handy little tool for calculating viewport-based clamped values.
Gerry talks about “top tasks” a lot. He literally wrote the book on it:
Top tasks are what matter most to your customers.
Seems pretty obvious, right? But it’s actually pretty rare to see top tasks presented any differently than other options.
Look at the global navigation on most websites. Typically all the options are given equal prominence. Even the semantics under the hood often reflect this egalitarian ideal, with each list in an unordered list. All the navigation options are equal, but I bet that the reality for most websites is that some navigation options are more equal than others.
I’ve been guilty of this on The Session. The site-wide navigation shows a number of options: tunes, events, discussions, etc. Each one is given equal prominence, but I can tell you without even looking at my server logs that 90% of the traffic goes to the tunes section—that’s the beating heart of The Session. That’s why the home page has a search form that defaults to searching for tunes.
I wanted the navigation to reflect the reality of what people are coming to the site for. I decided to make the link to the tunes section more prominent by bumping up the font size a bit.
I was worried about how weird this might look; we’re so used to seeing all navigation items presented equally. But I think it worked out okay (though it might take a bit of getting used to if you’re accustomed to the previous styling). It helps that “tunes” is a nice short word, so bumping up the font size on that word doesn’t jostle everything else around.
I think this adjustment is working well for this situation where there’s one very clear tippy-top task. I wouldn’t want to apply it across the board, making every item in the navigation proportionally bigger or smaller depending on how often it’s used. That would end up looking like a ransom note.
But giving one single item prominence like this tweaks the visual hierarchy just enough to favour the option that’s most likely to be what a visitor wants.
That last bit is crucial. The visual adjustment reflects what visitors want, not what I want. You could adjust the size of a navigation option that you want to drive traffic to, but in the long run, all you’re going to do is train people to trust your design less.
You don’t get to decide what your top task is. The visitors to your website do. Trying to foist an arbitrary option on them would be the tail wagging the dog.
Anway, I’m feeling a lot better about the site-wide navigation on The Session now that it reflects reality a little bit more. Heck, I may even bump that font size up a little more.
Reminder:
em
andrem
work with the user’s font size;px
completely overrides it.
Trys has written up how he made that nifty little resizing widget on the Utopia homepage.
Prompted by Utopia, Piper shares her methodology for fluid type in Sass.
CSS got some pretty nifty features recently. There’s the min()
and max()
functions. If you use them for, say, width
you can use one rule where previously you would’ve needed to use two (a width
declaration followed by either min-width
or max-width
). But they can also be applied to font-size
! That’s very nifty—we’ve never had min-font-size
or max-font-size
properties.
There’s also the clamp()
function. That allows you to set a minimum size, a default size, and a maximum size. Again, it can be used for lengths, like width
, or for font-size
.
Over on thesession.org, I’ve had some media queries in place for a while now that would increase the font-size
for larger screens. It’s nothing crucial, just a nice-to-have so that on wide screens, the font is bumped up accordingly. I realised I could replace all those media queries with one clamp()
statement, thanks to the vw
(viewport width) unit:
font-size: clamp(1rem, 1.333vw, 1.5rem);
By default, the font-size is 1.333vw
(1.333% of the viewport width), but it will never get smaller than 1rem
and it will never get larger than 1.5rem
.
That works, but there’s a bit of an issue with using raw vw
units like that. If someone is on a wide screen and they try to adjust the font size, nothing will happen. The viewport width doesn’t change when you bump the font size up or down.
The solution is to mix in some kind of unit that does respond to the font size being bumped up or down (like, say, the rem
unit). Handily, clamp()
allows you to combine units, just like calc()
. So I can do this:
font-size: clamp(1rem, 0.5rem + 0.666vw, 1.5rem);
The result is much the same as my previous rule, but now—thanks to the presence of that 0.5rem
value—the font size responds to being adjusted by the user.
You could use a full 1rem
in that default value:
font-size: clamp(1rem, 1rem + 0.333vw, 1.5rem);
…but if you do that, the minimum size (1rem
) will never be reached—the default value will always be larger. So in effect it’s no different than saying:
font-size: min(1.rem + 0.333vw, 1.5rem);
I mentioned this to Chris just the other day.
Anyway, I got the result I wanted. I wanted the font size to stay at the browser default size (usually 16 pixels) until the screen was larger than around 1200 pixels. From there, the font size gets gradually bigger, until it hits one and a half times the browser default (which would be 24 pixels if the default size started at 16). I decided to apply it to the :root
element (which is html
) using percentages:
:root {
font-size: clamp(100%, 50% + 0.666vw, 150%);
}
(My thinking goes like this: if we take a screen width of 1200 pixels, then 1vw
would be 12 pixels: 1200 divided by 100. So for a font size of 16 pixels, that would be 1.333vw
. But because I’m combining it with half of the default font size—50% of 16 pixels = 8 pixels—I need to cut the vw
value in half as well: 50% of 1.333vw
= 0.666vw
.)
So I’ve got the CSS rule I want. I dropped it in to the top of my file and…
I got an error.
There was nothing wrong with my CSS. The problem was that I was dropping it into a Sass file (.scss
).
Perhaps I am showing my age. Do people even use Sass any more? I hear that post-processors usurped Sass’s dominance (although no-one’s ever been able to explain to me why they’re different to pre-processers like Sass; they both process something you’ve written into something else). Or maybe everyone’s just writing their CSS in JS now. I hear that’s a thing.
The Session is a looooong-term project so I’m very hesitant to use any technology that won’t stand the test of time. When I added Sass into the mix, back in—I think—2012 or so, I wasn’t sure whether it was the right thing to do, from a long-term perspective. But it did offer some useful functionality so I went ahead and used it.
Now, eight years later, it was having a hard time dealing with the new clamp()
function. Specifically, it didn’t like the values being calculated through the addition of multiple units. I think it was clashing with Sass’s in-built ability to add units together.
I started to ask myself whether I should still be using Sass. I looked at which features I was using…
Variables. Well, now we’ve got CSS custom properties, which are even more powerful than Sass variables because they can be updated in real time. Sass variables are like const
. CSS custom properties are like let
.
Mixins. These can be very useful, but now there’s a lot that you can do just in CSS with calc()
. The built-in darken()
and lighten()
mixins are handy though when it comes to colours.
Nesting. I’ve never been a fan. I know it can make the source files look tidier but I find it can sometimes obfuscate what you’re final selectors are going to look like. So this wasn’t something I was using much any way.
Multiple files. Ah! This is the thing I would miss most. Having separate .scss
files for separate interface elements is very handy!
But globbing a bunch of separate .scss
files into one .css
file isn’t really a Sass task. That’s what build tools are for. In fact, that’s what I was already doing with my JavaScript files; I write them as individual .js
files that then get concatenated into one .js
file using Grunt.
(Yes, this project uses Grunt. I told you I was showing my age. But, you know what? It works. Though seeing as I’m mostly using it for concatenation, I could probably replace it with a makefile. If I’m going to use old technology, I might as well go all the way.)
I swapped out Sass variables for CSS custom properties, mixins for calc()
, and removed what little nesting I was doing. Then I stripped the Sass parts out of my Grunt file and replaced them with some concatenation and minification tasks. All of this makes no difference to the actual website, but it means I’ve got one less dependency …and I can use clamp()
!
Remember a little while back when I was making a dark mode for my site? I made this observation:
Let’s just take a moment here to pause and reflect on the fact that we can now use CSS to create all sorts of effects that previously required a graphic design tool like Photoshop.
It feels like something similar has happened with tools like Sass. Sass was the hare. CSS is the tortoise. Sass blazed the trail, but now native CSS can achieve much the same result.
It’s like when we used to need something like jQuery to do DOM Scripting succinctly using CSS selectors. Then we got things like querySelector()
in JavaScript so we no longer needed the trailblazer.
I’ve said it before and I’ll say it again, the goal of any good library should be to get so successful as to make itself redundant. That is, the ideas and functionality provided by the tool are so useful and widely adopted that the native technologies—HTML, CSS, and JavaScript—take their cue from those tools.
You could argue that this is what happened with Flash. It certainly happened with jQuery and Sass. I’m pretty sure we’ll see the same cycle play out with frameworks like React.
A look at the trend towards larger and larger font sizes for body copy on the web, culminating with Resilient Web Design.
There are some good arguments here for the upper limit on the font size there being too high, so I’ve adjusted it slightly. Now on large screens, the body copy on Resilient Web Design is 32px (2 times 1em), down from 40px (2.5 times 1em).
This is a really good explanation of the difference between context-aware layouts—that we’ve had up until now—and content-aware layouts, which are now possible with CSS grid:
With the
min-content
,max-content
andauto
keywords, we can size grid tracks based on their content. I think this is very cool. If we manage to embrace as much of the web’s flexibility as we can, we can get the most out of these tools, and let CSS help us with designing for the unknown.
The history and restoratin of a neglected typeface, complete with this great explanation of optical sizing:
Nix illustrated the point with an analogy: “Imagine if we all decided that 10-year-old boys would be the optimal human form,” he says. “Rather than having babies, we just shrunk 10-year-old boys to baby size, and enlarge them to the size of a full grown man. That’s kind of what we’re combatting.”
A clever little hack to preserve an aspect ratio for any HTML element.
We use two important attributes:
- SVG knows how to maintain aspect ratio
- CSS grid knows how to make overlapping items affect each other’s size
A great description of one of the most powerful features in CSS Grid.
This function opens the door to us being able to write much more powerful and succinct CSS by allowing us to set, as a value for a grid track, a function including both a minimum and maximum value.
A nice rundown of some of the fun you can have with viewport units.
I’m very glad the problems with vh
units I wrote about a little while back is getting fixed in Chrome for mobile.