Frontend Masters Boost RSS Feed https://frontendmasters.com/blog Helping Your Journey to Senior Developer Wed, 25 Jun 2025 23:04:07 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 225069128 Quantity Query Carousel https://frontendmasters.com/blog/quantity-query-carousel/ https://frontendmasters.com/blog/quantity-query-carousel/#comments Wed, 25 Jun 2025 23:04:06 +0000 https://frontendmasters.com/blog/?p=6323 The concept of a quantity query is really neat. Coined by Heydon back in 2015, the idea is that you apply different styles depending on how many siblings there are. They was a way to do it back then, but it’s gotten much easier thanks to :has(), which not only makes the detection easier but gives us access to the parent element where we likely want it.

For instance:

.grid {
  display: grid;

  &:has(:nth-child(2)) {
    /* Has at least 2 elements */
    grid-template-columns: 1fr 1fr;
  }
 
  /* Use a :not() to do reverse logic */
}

What if we kept going with the idea where we…

  • If there is 1 element, let it be full-width
  • If there are 2 elements, set them side-by-side
  • If there are 3 elements, the first two are side-by-side, then the last is full-width
  • If there are 4 elements, then it’s a 2×2 grid

Then…

  • If there are 5+ elements, woah there, let’s just make it a carousel.

I heard Ahmad Shadeed mention this idea on stage at CSS Day and I had to try it myself. Good news is that it works, particularly if you can stomach the idea of a “carousel” just being “horizontal overflow with some scroll snapping” in Firefox/Safari for now. Of course you’d be free to make your own fallback as needed.

Here’s the whole gang:

Setup & One

The default setup can be something like:

.grid {
  display: grid;
  gap: 1rem;
}

Honestly we don’t even really need to make it a grid for one item, but it doesn’t really hurt and now we’re set up for the rest of them.

Two

Does it have two? Yeah? Let’s do this.

.grid {
  ...

  &:has(:nth-child(2)) {
    grid-template-columns: 1fr 1fr;
  }
}

Note that if our grid has three or more elements, this will also match. So if want to do something different with columns, we’ll need to override this or otherwise change things.

Three

To illustrate the point, let’s match where there are only three items.

.grid {
  ...

  &:has(> :nth-child(3)):not(:has(> :nth-child(4))) {
    > :nth-child(3) {
      grid-column: span 2;
    }
  }
}

So we’re not going to change the 2-column grid, we’ll leave that alone from two. And now we’re not selecting the grid itself, but just grabbing that third item and stretching it across both columns of the grid.

Four

We can… do nothing. It’s already a two-column grid from two. So let’s let it be.

Five+

This is the fun part. We already know how to test for X+ children, so we do that:

.grid {
  ...

  &:has(:nth-child(5)) {
    grid-template-columns: unset;
  }
}

But now we’re unseting those columns, as we don’t need them anymore. Instead we’re going with automatic column creation in the column direction. We could use flexbox here too essentially but we’re already in a grid and grid can do it with easy sturdy columns so might as well. Then we’ll slap smooth scrolling and scroll snapping on there, which will essentially be the fallback behavior (only Chrome supports the ::scroll-button stuff that makes it carousel-like for now).

.grid {
  ...

  &:has(:nth-child(5)) {
    grid-template-columns: unset;

    grid-auto-flow: column;
    grid-auto-columns: 200px;

    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;

    > div {
      scroll-snap-align: center;
    }
  }
}

Actually Carouselling

We’re all set up for it, we just need those back/forward buttons to make it really be a carousel. That’s a CSS thing now, at least in Chrome ‘n’ friends, so we can progressively enhance into it:

.grid {
  ...

  &:has(:nth-child(5)) {
    ...

    anchor-name: --⚓️-carousel;

    &::scroll-button(*) {
      position: absolute;
      top: 0;
      left: 0;
      position-anchor: --⚓️-carousel;
      background: none;
      border: 0;
      padding: 0;
      font-size: 32px;
    }

    &::scroll-button(right) {
      position-area: center inline-end;
      translate: -3rem -0.5rem;
      content: "➡️" / "Next";
    }

    &::scroll-button(left) {
      position-area: inline-start center;
      translate: 3rem -0.5rem;
      content: "⬅️" / "Previous";
    }
  }
}

That’ll do it! Here’s the demo and I’ll video it in case you’re not in Chrome.

]]>
https://frontendmasters.com/blog/quantity-query-carousel/feed/ 1 6323
Quantity Queries are Very Easy with CSS :has() https://frontendmasters.com/blog/quantity-queries-are-very-easy-with-css-has/ https://frontendmasters.com/blog/quantity-queries-are-very-easy-with-css-has/#comments Mon, 11 Dec 2023 22:21:34 +0000 https://frontendmasters.com/blog/?p=239 What is a quantity query? It’s a bit of CSS that allows you to style an element (and its descendants) based on how many children the element has.

Example of a Quantity Query Situation

Imagine a homepage currently showing 20 articles, more than you normally show. Maybe it’s a slow news day, and the lead editor is shooting for variety. So because that’s a lot, you want CSS to scale them down a bit or re-arrange them to make them more equally browsable.

But then you have a really big news day with a lead story, so you decide only to show five articles, the first of which you want to make very large.

Quantity queries (originally coined by Heydon Pickering, best I can tell) may be a solution for this. Here’s some pseudo code to explain:

main {
  
  /* if (articles > 20) */
  display: grid;
  grid-template-columns: repeat(5, 1fr);

  /* if (articles > 5) */
  display: flex;
  flex-wrap: flex;
  article:first-of-type { width: 100% }

  /* default */
  display: block;

}

Old School Quantity Query

Believe it or not, this has been possible in CSS for a while! It just involves some trickery. Here’s Heydon’s first example:

button {
  font-size: 2em;
}

button:not(:only-of-type) {
  font-size: 1.25em;
}

See what that’s doing? That second selector is saying: if this button isn’t totally alone in its parent element, scale down the font-size. Essentially: if one, do this, if more, do this. A simple quantity query. With increasingly complicated selectors like that, you can pull of quantity math. You didn’t see it very often though, as it was pretty weird and complicated.

Quantity Queries with :has()

Now we have :has() in CSS, supported across all the major browsers as of just this month, and we can use it to make quantity queries a lot easier.

Here’s how it works.

You check if an element has an element at all at an :nth-child() position. For example, to check if an element has 10 or more elements (a quantity query), you can do:

.element {
  &:has(> :nth-child(10)) {

    /* Style things knowing that 
       `.element` has at least 10 
       children */

  }
}

You can keep going if you like. Just know that for each of them that matches, all the styles will be applied, so it’s rather additive.

.element {

  &:has(> :nth-child(10)) { }
  &:has(> :nth-child(20)) { }
  &:has(> :nth-child(30)) { }

}

(Note the > child selector is a bit safer than not using it, as it protects against some descendant element having this many children, which is probably not what you mean.)


You could also make sure you’re checking for a particular element type. Like perhaps doing some special styling for a menu that has 10 options within it (or more), knowing that select menus can contain <hr /> element seperators now, which aren’t actually menu options.

selectlist {

  &:has(option:nth-of-type(10)) { }

}

(Wondering what a <selectlist> is? We can’t use it quite yet, but it’s looking to be a 100% CSS styleable <select>, which is awesome).

Example

Here’s a demo where a range input changes the number of children elements are within a parent, then changes the styling of the children depending on how many there are.


What other things can you think of where quantity queries would benefit? Perhaps you would style an article differently if it contains 5 or more header elements. Perhaps you could style table rows to look “collapsed” if there are over a certain threshold of them. Perhaps you would style a comment thread differently if a certain comment has more than 10 replies.

Wanna keep digging into more? Jen Kramer’s course Intermediate HTML & CSS gets into this in the video Level 4 Pseudo-Class Selectors.

]]>
https://frontendmasters.com/blog/quantity-queries-are-very-easy-with-css-has/feed/ 5 239