The Cascade

May 2, 2024

An alternative proposal for CSS masonry

Rachel Andrew:

The Chrome team believes that masonry should be a separate layout method, defined using display: masonry (or another keyword should a better name be decided upon). Later in this post, you can see some examples of what that could look like in code.

There are two related reasons why we feel that masonry is better defined outside of grid layout—the potential of layout performance issues, and the fact that both masonry and grid have features that make sense in one layout method but not the other.

The most compelling argument for me is the potentially very confusing overlaps and underlaps between grid and masonry where sometimes a grid property just won’t apply to a masonry-style layout. However, this example that Rachel shares in her post certainly feels icky to me:

.masonry {
  display: masonry;
  masonry-template-tracks: repeat(auto-fill, auto);
}

.placed {
  masonry-track: 2 / 5;
}

At a quick glance, and without understanding too closely what’s going on under the hood, I’d question why the heck this looks so much like a grid-but-not-a-grid syntax. Maybe I just gotta sit with it some more though!

April 28, 2024

Popover API

Una Kravets has the scoop on the Popover API being fully supported in browsers:

It’s happening! One of the features I am most hyped about has just landed across all modern browsers and is officially a part of Baseline 2024. And this feature is the Popover API. Popover provides so many awesome primitives and developer affordances for building layered interfaces like tooltips, menus, teaching UIs, and more.

Baseline” is a way of measuring browser support that I had somehow managed to skip right over when it was announced last year. But anyway, back to Una’s post: Popover is real neat because you don’t have to worry about z-index or focus management, and using the API is as simple as writing some good ol’ HTML:

<button popovertarget="mypopover">Toggle the popover</button>
<div id="mypopover" popover>Popover content</div>

I like this! This syntax feels very much like invokers and ya know that I love those. But one question I had when reading Una’s post was this: when and how should I use the Popover API?

Thankfully, Hidde de Vries has collected a bunch of examples from menus and popover lists to teaching UIs and mega-navs that’s worth looking at in his post about Popover semantics. Hidde also reminds me that the title attribute exists which makes things even more complicated. And it looks like there’s rumblings about being able to customize the styles of that in the future which I would LOVE.

Another confounding question here: when should I use [popover] and when should I reach for <dialog>? MDN recommends that this decision should be based on whether you need the content of the page to inert” whilst this thing is active:

Popovers created using the Popover API are always non-modal. If you want to create a modal popover, a <dialog> element is the right way to go. There is significant overlap between the two — you might for example want to create a popover that persists, but control it using declarative HTML. You can turn a <dialog> element into a popover (<dialog popover> is perfectly valid) if you want to combine popover control with dialog semantics.

Huh! So you can write something like this:

<dialog popover id="mypopover">This is some content</dialog>
<button popovertarget="mypopover">Open</button>

That’s handy. But wait, okay. Now I’m really, extremely confused—when should I use title versus popover versus dialog again? Back to Hidde’s post, he notes:

My advice would be that whenever tooltips contain more than just plain text, a non-modal dialog would be more appropriate…

So I think the logic goes something like this:

  1. Use title if the content in my popover is just text.
  2. Use the popover attribute if the content is plain text and also hs like a menu of options.
  3. Use <dialog> if you need to force the user to make a decision or block all other interactions on the page.

Update: Sara Soueidan replied with a great note that we shouldn’t reach for the title attribute at all and linked to this equally great post about it:

It is primarily displayed as a native tooltip in desktop browsers, and revealed when a user mouse hovers over markup elements the title is set to. Because of this, it has been a universal usability challenge since its inception, as not all users have been consistently able to interact with it.

April 27, 2024

Can you detect overflow with CSS?

Yes! You can! But before we get to that I need to backtrack a bit.

The other day I made a daft and careless mistake whilst writing about cool queries and specifically this bit of code:

.parent {
  max-width: 300px;
}

.child {
  width: 500px;

  @media (overflow-inline) {
    background: yellow;
  }
}

After misreading the spec and making a quick demo in the browser, I thought I had cracked the problem of being able to detect scrollable containers with just CSS. I assumed what was happening here was that the overflow-inline media query above would detect when the child element was exceeding the bounds of the parent element and then I could change its background to yellow. The demo appeared to work, I had solved the three body problem in CSS, wonderful.

Except, well. It doesn’t work!

Later that same day, Kilian Valkhof wrote up a very good post about why this isn’t the case at all:

Media features say something about the device, and the overflow media feature says something about how the device handles overflowing. It doesn’t say anything about if the page currently is overflowing, just how it would handle overflowing.

Overflow can be used to check how the device”, or rather the medium in this case, handles overflowing content. On a screen, in most browsers, overflow is going to be scroll”: If the content overflows, the device deals with that by letting you scroll. But when your medium is print overflow-block is going to evaluate to paged”. Paper doesn’t scroll, it will continue the content on the next printed page instead.

As Kilian notes, media queries like the one I scribbled above are about the medium, not the page. So this is where I made my fatal and embarrassing mistake that we shall all now politely forget.

Thankfully though, instead of spreading CSS rumors on the internet like I had, back in 2023 Bramus had shown an honest way to detect overflow with—of course!—scroll driven animations. The code isn’t quite as succinct as have a nice little query do the work for us but this does make a bunch of sense to me, even at a quick glance:

@keyframes detect-scroll {
  from,
  to {
    --can-scroll: ;
  }
}

.container {
  animation: detect-scroll linear;
  animation-timeline: scroll(self);

  --bg-if-can-scroll: var(--can-scroll) lime;
  --bg-if-cant-scroll: red;
  background: var(--bg-if-can-scroll, var(--bg-if-cant-scroll));
}

Bramus writes:

For elements that have scrollable overflow, the animation will be active, so the computed value of --can-scroll will be 1. For elements without scrollable overflow, the value will be 0

Fantastic! The whole post walks through this block of code in much more detail and so it’s very much worth reading but this made me sigh a great sigh of relief. I made a little demo where you can add more words to a sentence and see the moment these styles are enabled, too:

See the Pen Detect scroll with CSS by Robin Rendle (@robinrendle) on CodePen.

It’s also worth pointing to Bramus’s excellent page of demos for scroll-driven animations. I feel like it’s about time that I dig my heels in and really explore what’s possible since up until now I’ve been ignoring this CSS superpower.

April 24, 2024

Design Token to CSS

The other day I mentioned Saneef Ansari’s excellent postcss-design-token-utils that converts a bunch of JSON into custom CSS properties and utility classes. At the end I aggressively rambled about how much I’d love to see this as a dedicated website and, well, Saneef built the darn thing!

It’s called Design Token to CSS and it’s very much worth checking out. I’ve fallen out of the loop a little bit when it comes to syncing design tokens like this between Figma and your front-end codebase but I imagine this is extremely helpful for keeping designers and engineers on the same page there.

Also! This tool is part of Saneef’s collection of nano-sized tools for web designers” called nanools which I had never seen before but holy heck is color × color worth looking at as well. It lets you build full blown color systems—but I especially love how Saneef goes about explaining luminance, chroma, and hue.

April 23, 2024

CSS shapes

Making shapes with CSS will never fail to impress me and I just spotted this great resource by Temani Afif that has a ton of examples. You’ve got the basics, like squares, triangles, and circles but then the more wild examples like ribbons and grid lines. Great stuff.

Also, completely unrelated to all this, but I had a lot of fun scrolling through Temani’s work on Codepen. Especially this trippy, 3d parallax effect and this, the ultimate CSS shape of them all: a CSS-only Goku.

April 22, 2024

Cool queries

There’s lots of query-related things that CSS can do that’s pretty neat. First up you’ve got your bog standard media queries where you can activate some styles when the size of the viewport reaches a certain value:

@media (min-width: 700px) {
  .my-element {
    background: yellow;
  }
}

But now that nesting has pretty good support then it’s just a whole lot nicer to organize them more like this:

.my-element {
  @media (min-width: 500px) {
  }
  @media (min-width: 700px) {
  }
  @media (min-width: 800px) {
  }
}

Ahhhh that is very nice and finally matches my mental model of how these queries are applied by the browser. But as I was spelunking through the Media Queries Level 5 spec, I noticed that you can also write these media queries like this:

.my-element {
  @media (width >= 500px) {
    background: yellow;
  }
}

That’s even nicerer! I’m not sure why I’ve never heard of this syntax before but it sure looks way tidier to me. You can even do more complex things with this syntax like add a more complex range to the mix:

.my-element {
  @media (1000px <= width <= 1500px) {
    background: yellow;
  }
}

This tells the browser to make the background yellow between 1000px and 1500px. That’s really, really nice. I also had no idea that was possible until just a second ago!

Then there’s of course other pretty popular things that media queries can do, such as enable styles based on the orientation of the device. We’ve likely used these to fix some weird mobile bug:

.my-element {
  @media (orientation: portrait) {
  }
  @media (orientation: landscape) {
  }
}

Also, as I mentioned yesterday, you can detect JavaScript support with a media query now:

.my-element {
  @media (scripting: enabled) {
  }
}

Another real shocker found deep in the spec is that it’s possible to use a media query to style an element based on whether it’s overflowing the parent, like this:

.parent {
  max-width: 300px;
}

.child {
  width: 500px;

  @media (overflow-inline) {
    background: yellow;
  }
}

What! That seems fantastically useful! You can play with a demo but it was surprising to me that this bad boy seems to have great browser support already? I must’ve missed the memo.

Update: Kilian Valkhof replied to this and explained that what’s going on here isn’t what I originally assumed! @media queries only apply to the user’s device, not to anything on the page itself. Which is an extreme bummer! It seems that being able to detect whether an element is overflowing with CSS alone isn’t quite possible yet unfortunately.

Okay then there’s the really cool stuff, like container queries. Ahmad Shadeed’s primer is required reading but I’ve always been confused by the syntax without nesting. With nesting, container queries are so much easier to read:

.parent {
  container-name: parent;
  container-type: inline-size;
}

.child {
  @container parent (width >= 1000px) {
    background: yellow;
  }
}

It’s pretty neat that you can add in the width shorthand there and it all works together. I love it when multiple things overlap in this way to improve the whole ecosystem” of CSS.

Next up: Una Kravets’s post on style queries was a real what the heck moment when I first read it because you can now check to see if an element has certain styles applied to it. Like this:

.parent {
  --background: red;
}

.child {
  @container style(--background: red) {
    background: green;
  }
}

The child element here will only have a green background if the variable in a parent element is set to red.

Right now it looks like browsers only support checking CSS variables like this but in the future they’ll expand to do all sorts of bonkers things. I still feel like I’m wrapping my head around the implications of these style queries but this will be nice if you have a component with a mode or a toggle from one visual state to another. Say, a navigation component that can switch orientations and the child elements change their style based on one CSS value.

April 21, 2024

Detect JavaScript Support

Ryan Mulligan on the new-to-me scripting CSS media feature:

With this feature, we can provide alternative CSS rules depending on whether or not JavaScript is available in the user’s browser. It can also help reduce flashes of unstyled content or undesirable layout shifts.

This is super neat, so you can now write a media query and ask the browser if JavaScript is on:

@media (scripting: enabled) {
  .my-element {
    /* enhanced styles if JS is available */
  }
}

…or disabled:

@media (scripting: none) {
  .my-element {
    /* fallback styles when JS is not supported */
  }
}

Ryan has a great demo of when you want to do that, how to combine queries together for some powerful styling possibilities, and some issues that might pop up when browser extensions are installed.

April 20, 2024

Masonry Layouts in CSS

Jen Simmons has written a stellar piece for the WebKit blog about masonry layouts and a proposal to introduce them in CSS Grid Level 3:

At its core, CSS Grid Level 3 provides a mechanism for turning off rows. It lets us create a columnar grid — a grid that’s made up of columns alone.

Neat! Masonry layouts are typically seen as Pinterest-esque interfaces and they’ve often been a bit painful to make (although it has been possible with JavaScript as Andy Barefoot showed back in 2017). Thankfully Jen shares half a dozen demos beyond the typical image waterfall example that I’ve seen many times before and so it’s clear how useful this sort of thing might be for complex typographic layouts like this example. Exciting stuff!

Okay, show me the proposal!

.container {
  display: grid;
  grid-template-columns: 14ch repeat(auto-fill, minmax(28ch, 1fr)) 14ch;
  grid-template-rows: masonry;
}

That last line is where the magic happens. Effectively what you’re telling CSS Grid to do here is to ignore any rows so you can create an asymmetrical grid. However! There is some drama about this proposal as some folks argue that masonry layouts should instead be toggled on the display property like this:

.container {
  display: masonry;
}

Since Jen is asking for feedback in her post about this proposal, here’s my take on all this hot drama:

  • Having another display option like display: masonry; feels wrong to me and before I even got to Jen showing the proposed CSS, I imagined masonry layouts as simply being CSS Grid but with rows turned off.
  • In the display: masonry version of CSS, I would definitely forget what’s available in Masonry layouts versus Grid layouts. I guarantee I’d have to look up what grid-like options (such as gap or grid-columns or whatever) were possible and I fear, like Jen does, that they’d get out of sync somehow between browsers.
  • I was sort of happy when we stopped using columns in CSS and moved to CSS Grid but with this masonry option we’d have to go back to that? That feels like a bummer to me. I don’t want two separate syntaxes to denote columns in CSS! I am the problem!
  • I had a maybe-dumb-thought whilst reading the proposal: what about masonry layouts where you can turn the columns off but keep the rows? So a horizontal masonry layout with grid-template-columns: masonry instead?
  • Nope, okay. That suggestion broke my brain and I think I’ve forgotten both how CSS Grid works and my own name now. Please send help.

Anyway, I think we should go with Jen’s suggestion of masonry being a subset of options within CSS Grid. That’s the most reasonable, maintainable, and nice-to-the-platform option available with this proposal I reckon.

April 19, 2024

Design systems and strictness

A while back I rambled out loud about how some design systems are way too strict:

Being on the other side of design systems now as a product designer is super interesting. I kinda hate the rules and regulations and nitpicking! And if I can’t lean into the system then I will fight against it with every fiber of my being.

So, okay, what parts of a design system should be strict and which parts should allow for more flexibility then? For example, design tokens should probably be super strict. You should rarely add a new font size or color variable or space things out with 7px when your systems sets an 8px scale. Adding a new font to a design system should also be a very slow and deliberate process that shouldn’t be rushed. So that’s a no-go, too.

However, components should be pretty locked down. You should be allowed to add components back to the system but a design systems team should be able to step in and demand that you use the standardized Button component over NewFancyButton. Otherwise all hell breaks loose and you have 5 different versions of everything.

However, however: adding variants of a component should be pretty lenient. If I need to add a green button to the design system then I don’t think that’s where a ton of oversight should be required to stop that because a variant doesn’t do as much damage as a whole new component might.

My rambling point here with all this is that there’s a spectrum of strictness when it comes to design systems and I think a lot of the work I did in the past ignored that. I saw our component library as being this pure and perfect thing that shouldn’t be messed with. But such intense strictness actually ended up hurting our team in the long run: folks didn’t want to contribute or help make things more consistent across the product. I still struggle, generally, with wanting to be right all the time. I want to correct all the wrong-ness in the world! And when it comes to design systems there’s so much sillyness and incorrectness and misbehavior that I want to sweep it all away with a wave of my hand.

But being useful is almost always better than being right. Both in life generally and also when it comes to our work in building sustainable and kind design systems.

April 19, 2024

Playing with Infinity in CSS

There’s not many blog posts that upset the natural order of CSS, but I’m still reeling from this one by Will Boyd where, back in February, he wrote about the infinity constant in CSS.

You can use it just like this…

.element {
  z-index: calc(infinity);
}

…and that’ll always be the biggest z-index on the page. That’s neat! And…weird? It’s definitely worth checking out Will’s list of practical examples of infinity, too.

April 19, 2024

Web Awesome

I’m real late to the party here but the folks behind Font Awesome have just a few days left to go on their incredibly successful Kickstarter project, Web Awesome:

Web Awesome provides a collection of meticulously designed, highly customizable UI components built on a framework agnostic technology. Why spend hundreds of hours (or more) building a design system from scratch? Why make a component library that only works with one framework?

April 19, 2024

Invisible success

Eric Bailey has some great thoughts about why it’s tough to show success on a design systems team:

In a business context, design system work means numbers go down. Less bug reports, faster design iteration, shorter development cycles, fewer visual inconsistencies, smaller staffing requirements that enable folks to work on more interesting challenges, etc. All good things.

Unfortunately, contemporary business practices only reward numbers going up. There isn’t much infrastructure in place to quantify the constant, silent, boring, predictable, round-the-clock passive successes of this aspect of design systems after the initial effort is complete.

This is an interesting discussion because it’s hard to quantify what’s good work when it comes to design systems. For example, when I was on a team like that I would struggle to make the case for refactoring tons of janky and confusing CSS.

Ultimately I felt like I was good at the systems part—I could tell when there were too many similar components or when we should refactor something—but I was really, really bad at the storytelling part of design systems. And that kind of work is really more story than systems: explaining to folks up the chain why this seemingly insignificant series of tasks is an investment in the future. Or why this one tiny detail is worthy of our care today so we don’t have to worry about it later.

April 19, 2024

How to create CSS utility classes

Just last week Andy Bell wrote about his CSS boilerplate for starting new projects. But the part that caught my eye was the way that he’s using Tailwind to generate CSS utility classes like this:

.m-l-10 {
  margin-left: 10px;
}
.m-r-10 {
  margin-right: 10px;
}

In my experience, utility classes in CSS—think Tailwind of Tachyons—tends to be a nightmare but a limited subset of them such as spacing classes or typographic helpers like this are genuinely handy. The problem is that you’ve often got to pick all of them outta those kinds of CSS frameworks or write your own by hand which is error prone and slow.

So Andy has a JSON file for each design token, like colors, spacing, font-size, which he then runs through Tailwind to create the classes. Then he imports that directly into his CSS. My first thought was: whoa! For every new project it must be amazing to tweak a few variables in a JSON file, run a command, and get a fresh batch of CSS utilities.

The other day I then spotted that Saneef Ansari was inspired by all this and made a PostCSS plugin to do the same thing, except without Tailwind as a dependency. These design utilities then get converted into CSS custom properties like this:

:root {
  --color-accent: #16a34a;
  --color-dark: #111827;
  --color-light: #f3f4f6;
  --space-xs: 0.25rem;
  --space-s: 0.5rem;
  --space-m: 1rem;
  --space-l: 2rem;
}

And you can also create the CSS utility classes with a single line of code!

That’s…extremely neat! My only jerky thought here is this: what if I didn’t want to use PostCSS as a dependency either? I could imagine a website where I just import some JSON or write my own classes, hit a button, and then export either a list of CSS classes…

.bg-dark {
  background: #333;
}
.bg-light {
  background: #fff;
}

…or custom properties…

:root {
  --bg-dark: #333;
  --bg-light: #fff;
}

…or both! Then I’m free to slam that into any project regardless of how it’s built and so I don’t need to even have npm as a dependency.

April 10, 2024

How to display language-specific quotes in CSS

Stefan Judis:

Quotation characters are language-specific. French, for example, uses « and ». Going all in with double quotes isn’t correct and will anger quite some typography nerds (and French people).

I love this attention to detail and totally didn’t know you could do the following and browsers will pick the right kind of quote depending on the user’s language:

blockquote::before {
  content: open-quote;
}

blockquote::after {
  content: close-quote;
}