Skip to main content

The Cascade is a blog about the past, present, and future of CSS.

Howdy—Robin Rendle here.

We’re living through a golden age: CSS was once a language that was easy to make fun of but has now transformed into a serious and expressive toolkit for building visual interfaces. Although making fun of CSS was always lame, today, in 2024, it shows a deep lack of curiosity. The train has left the station. CSS rules. Get with the program.

But this didn’t happen randomly. Thousands of dedicated, smart folks have worked tirelessly over decades to get us to this point where CSS is—in this humble blogger’s opinion—the best design tool ever made. Every day some new super power is unlocked for us in browsers and with each new power the web becomes a better place, thanks to them.

So this blog exists to keep me in the loop and somewhat up to date with everything that’s possible with CSS but also it’s a reminder to celebrate the people doing the hard work building these tools for us.

You can subscribe to The Cascade via RSS, shoot me an email if you absolutely must, or follow the feed. This project is directly supported by readers and the membership program.

Right now the newsletter is taking a bit of a break whilst I figure out a healthy publishing cadence, but you can subscribe below:

CSS Grid Areas

After all these years of using CSS Grid and I still feel like I haven’t grokked everything that it’s capable of. Ahmad Shadeed’s latest interactive tutorial is a great reminder of that where he’s written about named grid areas and explains the following bit of CSS:

.layout {
  grid-template-columns:  [full-start] 1fr
                          [content-start] 2fr
                          [content-end] 1fr [full-end];
}

So what Ahmad has done here is defined and named specific areas of a grid and then he can use them elsewhere in his CSS like this:

.layout-item {
  grid-column: content-start / content-end;
}

Smart stuff.

:nth-child(of)

I’m not sure how I fell of the bandwagon of Manuel Matuzović’s excellent 100 days of CSS...

It’s time to get me up to speed with modern CSS. There’s so much new in CSS that I know too little about. To change that I’ve started #100DaysOfMoreOrLessModernCSS. Why more or less modern CSS? Because some topics will be about cutting-edge features, while other stuff has been around for quite a while already, but I just have little to no experience with it.

That’s from the latest post all about this syntax I’d never seen before:

li:nth-child(even of :not([hidden])) {
  background-color: aqua;
}

The idea is to filter all of the even list elements that aren’t hidden. That is extremely useful and good to know!

My favorite line of CSS

I think my favorite line of CSS has finally been usurped! For maybe a decade my favorite line was this bad boy:

.element {
	display: grid;
	place-items: center;
}

Pure, simple, perfect. Throw some viewport units in on top and you now have Megazord-like super powers to take control of the browser canvas like you never could before.

Now though, my favorite line is this:

@view-transition {
	navigation: auto;
}

As Dave Rupert wrote a while back, this is the easiest way to add view transitions to your website today. And I’ve already added them here and on my blog whilst I figure out how exactly pages should animate in and out without making everyone sick of it.

Honestly, if this was the new default animation between pages on the internet then I’d be chuffed to bits. It feels a bit more app-like, a bit more polished and less janky then the clunky page switching of the past.

But but but!

Eric Portis warns us that this is all more complicated than it seems and when we add view transitions we need to test the heck out of them because they break incremental loading:

I worry that giving developers tools to explicitly block render – with or without View Transitions – is going to make experiencing the web on slow connections and cheap devices much worse.

[...] We need tuned timeouts that ensure that the long tail of slow devices/connections don’t wait for View Transitions if they would excessively delay first paint.

So: I am very excited about my favorite new line of CSS but we have to tread lightly with this stuff because it could end up hurting folks more than it helps.

Motion blur

Oooooh here’s something I’ve never thought about when it comes to CSS: motion blur! Adam Argyle wrote this back in 2019 (!) but it still feels new and exciting to me:

Professional polish of motion graphics often includes the application of motion blur. In most cases, it's a boolean toggled at the layer level, that then tells the engine to set a blur amount based on the speed of the pixels. This effect makes animations much closer to real life, among other nice benefits. CSS currently is incapable of such an effect. Instead, a strobing, ghosted type effect is often what we get instead.

I like this proposal, too:

.animated-layer {
  animation: rotate .5s linear infinite;
  motion-rendering: blur;
  motion-shutter-angle: 180deg;
}

@keyframes rotate {
  to {
    transform: rotate(1turn);
  }
}

Also, reading the comments on this thread is fun and reveals just how complex the standards process is for adding anything new to CSS.

Inline conditionals in CSS, now?

This is a great post by Lea Verou all about conditional statements in CSS:

A couple days ago, I posted about the recent CSS WG resolution to add an if() function to CSS. Great as it may be, this is still a long way off, two years if everything goes super smoothly, more if not. So what can you do when you need conditionals right now?

You may be pleased to find that you’re not completely out of luck. There is a series of brilliant, horrible hacks that enable you to expose the kinds of higher level custom properties that conditionals typically enable.

One technique that hurts my brain to think about is what Jane Ori called “type grinding” a few years ago. The idea is that you daisy-chain multiple @property declarations to change the value of a CSS variable as it passes through them. Which makes for a wild, mind-bending read.

Javascript free navigation

Thanks to Andy Bell’s ever-so-good newsletter, The Index, I found myself ooo-ing and aaa-ing at Michelle Barker’s excellent post from back in May about creating a JavaScript-free menu with the latest bells and whistles like anchor-positioning and the Popover API:

Anchor positioning in CSS enables us to position an element relative to an anchor element anywhere on the page. Prior to this we could only position an element relative to its closest positioned ancestor, which sometimes meant doing some HTML and CSS gymnastics or, more often than not, resorting to Javascript for positioning elements like tooltips or nested submenus.

There’s a lot of good examples in that post that are worth checking out but one thing that stuck out to me was the <menu> HTML element which we can use like this for a series of interactive actions:

<menu>
  <li><button>Copy</button></li>
  <li><button>Cut</button></li>
  <li><button>Paste</button></li>
</menu>

...how have I never used this before!?

Learn Grid Now, Container Queries Can Wait

Miriam Suzanne:

There’s no rush to rip out all your media queries, and replace them with containers. You’ll be fine waiting for widely available support and your next scheduled re-factor.

But if you’re still avoiding grid – whatever your reasons – you are, in fact, missing out. CSS grid is one of the best features in CSS, and one of the biggest time-savers on every site we build.

One part of Grid that I’ve been ignoring for too long is the whole template areas syntax...

.grid {
  display: grid;
  grid-template-areas:
    "head head"
    "nav  main"
    ".  foot";
}

.grid header {
  grid-area: head;
}

/* etc. etc. */

For some reason it doesn’t feel grid-y enough for me but I should just sit down and try to get used to it. Even looking at it now I’m slowly realizing that it’s real nice that you can use . to denote an empty column which makes things quite a bit easier. Oh and this reminds me, the other day I spotted this CSS Grid Template Builder by Anthony Dugois and it’s pretty handy!

In defense of asymmetric grids

CSS Grid has made me lazy. Whenever I start a new project I tend to pick a 12 column grid and move on to other things...

.grid {
	grid-template-columns: repeat(12, 1fr);
}

...it’s almost too easy! This is probably also out of laziness in the design, too. I’d often pickup the Bootstrap grid and after years of use I began to see every page built out of 12 columns. So layouts, and the grid systems they’re built out of, felt like a solved problem to me.

But I got into a layout pickle the other day because of this laziness: I realized that my layout looked so weird and wonky here on The Cascade because I was using my trusty 12 column layout without thinking about it. I’d then set every element on the page to fit within those arbitrary constraints:

.main {
	grid-column: 5/13;
}

.sidebar {
	grid-column: 1/5;
}

The problem here was the sidebar would stretch too far at larger screen sizes and felt visually gross. So I did the unthinkable and tweaked the grid to fit the content instead:

.grid {
	grid-template-columns: repeat(4, minmax(auto, 80px)) repeat(7, auto);
}

Translation: “make 4 columns with a max-width of 80px each, and then 7 columns that take up the space left over.” Reading CSS like this always takes an extra second for me to parse and walkthrough step by step but effectively all we’re doing here is telling the first 4 columns to be a different size than the remaining columns.

Gasp! An asymmetric grid! The horror!

Anyway, CSS grid is just a tool and it doesn’t have to be perfect or simple or make everything line up in equal measure. But I also realized here that the grid should always be subservient to the content.

Or: content first, grid last.

A modern approach to browser support

The folks at Clearleft published their browser support policy the other day which is pretty interesting:

Underlying our browser support policy are two foundational principles:

  1. Website content and core functionality should be accessible to everyone.
  2. It’s okay for websites to look different in different browsers.

If content is unreadable in some browsers, that’s a bug that we will fix. If content is displayed slightly differently in some browsers, we consider that to be a facet of the web, not a bug. This means that there will sometimes be subtle visual and functional differences from browser to browser. We deem this acceptable provided that content and core functionality are unaffected.

Responsive posters in CSS

The other day I spotted these lovely VHS posters and wondered how you might make that slanted background pattern there with just CSS. So I took a crack at it and got pretty close:

See the Pen Responsive JVC Video Cassette by Robin Rendle (@robinrendle) on CodePen.

This isn’t an SVG (which is probably the best way to make something like this) but instead it’s plain ol’ CSS. Try and resize the browser though and you’ll see it respond to the height and width of the viewport which feels swish.

So here’s a quick summary of the trick: first I set up container queries and aspect-ratio on the wrapper element...

.cassette {
	container-type: inline-size;
	aspect-ratio: 70/99;
}

That’s what handles all the resizing. Now we can set up some variables:

:root {
  --first-stop: 20cqw;
  --second-stop: 40cqw;
  --third-stop: 60cqw;
  --fourth-stop: 80cqw;
  --fifth-stop: 100cqw;
  --sixth-stop: 150cqw;
  --red: hsl(350, 100%, 50%);
  --orange: hsl(13, 100%, 50%);
  --gray: hsl(17, 19%, 73%);
  --white: hsl(0, 0%, 100%);
}

Those cqw length units are pretty dang handy. 1cqw is 1% of the container’s width which means I can use them in the repeating-linear-gradient property below to create that background pattern:

.body-wrapper {
  background-image:
    repeating-linear-gradient(
      135deg,
      var(--white),
      var(--white) var(--first-stop),
      var(--red) var(--first-stop),
      var(--red) var(--second-stop),
      var(--orange) var(--second-stop),
      var(--orange) var(--third-stop),
      var(--gray) var(--third-stop),
      var(--gray) var(--fourth-stop),
      var(--white) var(--fourth-stop),
      var(--white) var(--fifth-stop),
      var(--white) var(--sixth-stop),
    );
}

I’m sure there’s a more elegant way of doing this but I kept playing around with these values until it looked right. I also set the font-size and position of elements to respond to the size of the container, too:

header {
  padding: 3cqw;
}

h2 {
  font-size: 10cqw;
}

This is fantastic because it means I don’t have to write a bunch of media queries all over the place. I just set the font-size and forget it. And that’s not a dig at media queries! Well, okay – it is. But I am slowly coming to the conclusion that media queries are a design smell. Sometimes they indicate that my code could use more modern CSS techniques to avoid that little bit of extra complexity and logic.

Wellllll...sorta. Chris wrote about this optimistic view of container queries where we all assumed that...

...on any given project, there would be more @container in the CSS than @media. I joined that refrain. I thought for sure it would be true, if not, more like 75%, especially considering how so many sites these days are created by composing sites through component libraries, and since components don’t know where they will be used, the CSS that style those components would use all container queries.

So, did this turn out to be true?

Anecdotally: no, not really.

It’s a great breakdown of why we don’t use container queries all over the place like I have in my little demo above. But either way, this whole “web poster” design with cqw units and a dash of aspect-ratio is super neat and it reminds me of what Jeremy said just the other day:

I really like the newly-launched website for this year’s XOXO festival. I like that the design is pretty much the same for really small screens, really large screens, and everything in between because everything just scales. It’s simultaneously a flyer, a poster, and a billboard.

An intro to CSS anchor positioning

I’ve been half-ignoring anchor positioning but patiently waiting for it to land in browsers for a while now. But this explanation by Brecht yesterday reminded me to buckle up and learn it all properly since it just landed in the latest version of Chrome and Edge.

One thing I hadn’t heard of before Brecht’s post was the inset-area property which sure is handy. As he explains, it lets you draw a grid around an element and then position something left/right/up/down within it:

.anchored {
  position-anchor: --anchortome;
  position: absolute;
  inset-area: top span-all;
}

This feels like how I’d anchor-position something like a tooltip maybe 95% of the time which is neat. But! Although anchor positioning sure is going to be amazing for tooltips, it goes far beyond that. Re-reading Roman Komarov’s post from last year made me spit-take again:

It becomes possible to highlight something in a completely different place on the page, allowing elements to “know of each other”.

Not only does that sound punk rock and strangely ominious, I think that’s what anchor positioning really opens up.

Charm

Since I’ve been working as a designer for a few years now, I’ve felt like I needed to upgrade my command line chops. So I downloaded Warp and fell down a big rabbit hole of futzing and playing with everything I’d missed or ignored over the years.

This led me to Charm, a group of folks who make super interesting command line tools. They make stuff like VHS which helps you create GIFs of your terminal, or Pop that lets you send email via the command line. Bubbles is neat too, it’s a set of components for building your own CLI tools.

The one thing that I love about Charm is that they clearly spend an awful lot of time polishing their work. From the website to the README, everything is clear and everything shines with thoughtfulness. I especially loved this post about their process by Christian Rocha:

The README is critically important to the success of an open source product. It’s often a developer’s first point of contact with a project and the place where a developer will, in a matter of seconds, judge whether the project worthy of further consideration. With this in mind, put a lot of effort into README design, optimizing for strong first impressions.

Our strategy is to simply follow the age-old rule of advertising: showing the product. Good products, when presented correctly, will sell themselves, which is why we spend spend so much time on user experience and attention to detail.

A common web component learning blunder

Dave Rupert:

Through stalking the #WebComponents hashtag and my Frontend Masters course, I’m privy to a lot of developers’ first experiences with web components. There’s a wide range of people digging in, but the most common first-time experience I come across is a developer coming from a classical component framework like React with JSX going straight to writing vanilla Web Components, becoming frustrated, and then deeming web components “not ready for primetime.”

The gist here is that HTML Web Components aren’t really meant to replace these frameworks at all and that’s something I really struggled with until I had my eureka moment a few months ago:

I don’t think we should see web components like the ones you might find in a huge monolithic React app: your Button or Table or Input components. Instead, I’ve started to come around and see Web Components as filling in the blanks of what we can do with hypertext: they’re really just small, reusable chunks of code that extends the language of HTML.

The Gap

Ahmad Shadeed has written another beautifully designed and polished deep-dive but this time all about the gap property. Sometimes it’s nice to go back in time and remember all the negative-margin hacks and complicated layout-related code we had to write just to space things apart. Thankfully no longer!

Another thing Ahmad’s post reminded me is that not only is gap straight up easier to use than managing a lot of margin declarations, but it also prevents layout bugs too. On this thread, Ahmad writes:

Building a UI that doesn’t break is impossible, but at least, you can reduce the issues that might happen:

  • Ask questions: what will happen if there is only one item? or do you expect another variation of this component that will do X and Y?
  • Make sure to stress-test the UI for any spacing issues.

Keeping up with CSS

Max Böck on all the new things in CSS:

While learning the syntax for any given CSS feature is usually not that hard, re-wiring our brains to think in new ways is significantly harder. We’ll not only have to learn the new way, we’ll also have to unlearn the old way, even though it has become muscle memory at this point.

Yes! This is the hardest part of web development for me, as there’s a lot of work that has to go into tearing down the years of experience I’ve had with, say, how CSS used to limit us in terms of layout. CSS Grid isn’t rocket science but unlearning the old and familiar ways sure is complicated. That takes time and serious amounts of effort.

And yet even though we’re living through the golden age of UI on the web, it doesn’t mean we have to use every new CSS thing in every single project. Sure, if something helps save you time then that’s great. Or if it unlocks some new super power then that’s fantastic. But you don’t have to use @layers and logical properties and :is or :has if you don’t need them.

It’s more than ok if you don’t know how the fanciest features work today and I don’t think you should force yourself to put them into a project if you don’t really need to. Yes, many of these new tricks expand what’s possible on the web today and make things easier but it’s perfectly fine to not be at the absolute bleeding edge of this stuff. And keeping up with CSS shouldn’t feel like a full time job, and it definitely shouldn’t stress you out.

This is the real magic of CSS though! All the old techniques from a decade ago work just fine. Are there better ways to do X, Y, or Z? Sure. But it’s also okay to not always be optimizing, to not always be grinding away at the infinite factory line of new CSS features in this mad-dash panic to feel like you’re a real front-end developer.

The thing is that a webpage from 1995 or a webpage from 2035 can—and often should!— work just the same. That’s ok!