What Are CSS Container Fashion Queries Good For? — Smashing Journal

0
15
What Are CSS Container Style Queries Good For? — Smashing Magazine


We’ve relied on media queries for a very long time within the responsive world of CSS however they’ve their share of limitations and have shifted focus extra in the direction of accessibility than responsiveness alone. That is the place CSS Container Queries are available. They utterly change how we strategy responsiveness, shifting the paradigm away from a viewport-based mentality to 1 that’s extra thoughtful of a element’s context, equivalent to its dimension or inline-size.

Querying components by their dimensions is among the two issues that CSS Container Queries can do, and, in actual fact, we name these container dimension queries to assist distinguish them from their means to question towards a element’s present kinds. We name these container type queries.

Present container question protection has been largely targeted on container dimension queries, which take pleasure in 90% world browser assist on the time of this writing. Fashion queries, then again, are solely out there behind a function flag in Chrome 111+ and Safari Know-how Preview.

The primary query that involves thoughts is What are these type question issues? adopted instantly by How do they work?. There are some good primers on them that others have written, and they’re value trying out.

However the extra fascinating query about CSS Container Fashion Queries would possibly really be Why we should always use them? The reply, as at all times, is nuanced and will merely be it relies upon. However I wish to poke at type queries a bit of extra deeply, not on the syntax stage, however what precisely they’re fixing and what kind of use instances we’d discover ourselves reaching for them in our work if and after they acquire browser assist.

Why Container Queries

Speaking purely about responsive design, media queries have merely fallen quick in some features, however I feel the primary one is that they’re context-agnostic within the sense that they solely think about the viewport dimension when making use of kinds with out involving the dimensions or dimensions of a component’s mum or dad or the content material it comprises.

This often isn’t an issue since we solely have a most important ingredient that doesn’t share area with others alongside the x-axis, so we are able to type our content material relying on the viewport’s dimensions. Nevertheless, if we stuff a component right into a smaller mum or dad and keep the identical viewport, the media question doesn’t kick in when the content material turns into cramped. This forces us to jot down and handle a complete set of media queries that concentrate on super-specific content material breakpoints.

Container queries break this limitation and permit us to question rather more than the viewport’s dimensions.

Diagram of a component in a web browser with examples of how to query its size and the viewport size.
Determine 1: When media queries look solely on the viewport, they overlook vital particulars in regards to the components within the viewport, most notably, their sizes. (Massive preview)

How Container Queries Typically Work

Container dimension queries work equally to media queries however permit us to use kinds relying on the container’s properties and computed values. In brief, they permit us to make type modifications based mostly on a component’s computed width or top whatever the viewport. This kind of factor was as soon as solely potential with JavaScript or the ol’ jQuery, as this instance exhibits.

As famous earlier, although, container queries can question a component’s kinds along with its dimensions. In different phrases, container type queries can take a look at and monitor a component’s properties and apply kinds to different components when these properties meet sure circumstances, equivalent to when the ingredient’s background-color is about to hsl(0 50% 50%).

That’s what we imply when speaking about CSS Container Fashion Queries. It’s a proposed function outlined in the identical CSS Containment Module Stage 3 specification as CSS Container Measurement Queries — and one which’s at the moment unsupported by any main browser — so the distinction between type and dimension queries can get a bit complicated as we’re technically speaking about two associated options beneath the identical umbrella.

We’d do ourselves a favor to backtrack and first perceive what a “container” is within the first place.

Containers

A component’s container is any ancestor with a containment context; it could possibly be the ingredient’s direct mum or dad or maybe a grandparent or great-grandparent.

A containment context implies that a sure ingredient can be utilized as a container for querying. Unofficially, you may say there are two sorts of containment context: dimension containment and type containment.

Measurement containment means we are able to question and monitor a component’s dimensions (i.e., aspect-ratio, block-size, top, inline-size, orientation, and width) with container dimension queries so long as it’s registered as a container. Monitoring a component’s dimensions requires a bit of processing within the consumer. One or two components are a breeze, but when we needed to consistently monitor the size of all components — together with resizing, scrolling, animations, and so forth — it might be an enormous efficiency hit. That’s why no ingredient has dimension containment by default, and we have now to manually register a dimension question with the CSS container-type property once we want it.

Then again, type containment lets us question and monitor the computed values of a container’s particular properties via container type queries. Because it at the moment stands, we are able to solely verify for customized properties, e.g. --theme: darkish, however quickly we may verify for a component’s computed background-color and show property values. In contrast to dimension containment, we’re checking for uncooked type properties earlier than they’re processed by the browser, assuaging efficiency and permitting all components to have type containment by default.

Did you catch that? Whereas dimension containment is one thing we manually register on a component, type containment is the default habits of all components. There’s no have to register a method container as a result of all components are type containers by default.

And the way will we register a containment context? The simplest means is to make use of the container-type property. The container-type property will give a component a containment context and its three accepted values — regular, dimension, and inline-size — outline which properties we are able to question from the container.

/* Measurement containment within the inline path */
.mum or dad {
  container-type: inline-size;
}

This instance formally establishes a dimension containment. If we had accomplished nothing in any respect, the .mum or dad ingredient is already a container with a type containment.

Measurement Containment

That final instance illustrates dimension containment based mostly on the ingredient’s inline-size, which is a elaborate means of claiming its width. Once we speak about regular doc movement on the internet, we’re speaking about components that movement in an inline path and a block path that corresponds to width and top, respectively, in a horizontal writing mode. If we have been to rotate the writing mode in order that it’s vertical, then “inline” would check with the peak as a substitute and “block” to the width.

Contemplate the next HTML:


We may give the .cards-container ingredient a containment context within the inline path, permitting us to make modifications to its descendants when its width turns into too small to correctly show all the things within the present format. We hold the identical syntax as in a standard media question however swap @media for @container

.cards-container {
  container-type: inline-size;
  }

  @container (width < 700px) {
  .playing cards {
    background-color: pink;
  }
}

Container syntax works nearly the identical as media queries, so we are able to use the and, or, and not operators to chain totally different queries collectively to match a number of circumstances.

@container (width < 700px) or (width > 1200px) {
  .playing cards {
    background-color: pink;
  }
}

Components in a dimension question search for the closest ancestor with dimension containment so we are able to apply modifications to components deeper within the DOM, just like the .card ingredient in our earlier instance. If there is no such thing as a dimension containment context, then the @container at-rule gained’t have any impact.

/* 👎 
 * Apply kinds based mostly on the closest container, .cards-container
 */
@container (width < 700px) {
  .card {
    background-color: black;
  }
}

Simply in search of the closest container is messy, so it’s good follow to call containers utilizing the container-name property after which specifying which container we’re monitoring within the container question simply after the @container at-rule.

.cards-container {
  container-name: cardsContainer;
  container-type: inline-size;
}

@container cardsContainer (width < 700px) {
  .card {
    background-color: #000;
  }
}

We will use the shorthand container property to set the container title and sort in a single declaration:

.cards-container {
  container: cardsContainer / inline-size;

  /* Equal to: */
  container-name: cardsContainer;
  container-type: inline-size;
}

The opposite container-type we are able to set is dimension, which works precisely like inline-size — solely the containment context is each the inline and block instructions. Meaning we are able to additionally question the container’s top sizing along with its width sizing.

/* When container is lower than 700px vast */
@container (width < 700px) {
  .card {
    background-color: black;
  }
}

/* When container is lower than 900px tall */
@container (top < 900px) {
  .card {
    background-color: white;
  }
}

And it’s value noting right here that if two separate (not chained) container guidelines match, essentially the most particular selector wins, true to how the CSS Cascade works.

To date, we’ve touched on the idea of CSS Container Queries at its most simple. We outline the kind of containment we would like on a component (we regarded particularly at dimension containment) after which question that container accordingly.

Container Fashion Queries

The third worth that’s accepted by the container-type property is regular, and it units type containment on a component. Each inline-size and dimension are secure throughout all main browsers, however regular is newer and solely has modest assist in the intervening time.

I think about regular a little bit of an oddball as a result of we don’t need to explicitly declare it on a component since all components are type containers with type containment proper out of the field. It’s potential you’ll by no means write it out your self or see it within the wild.

.mum or dad {
  /* Pointless */
  container-type: regular;
}

If you happen to do write it or see it, it’s more likely to undo dimension containment declared elsewhere. However even then, it’s potential to reset containment with the worldwide preliminary or revert key phrases.

.mum or dad {
  /* All of those (re)set type containment */
  container-type: regular;
  container-type: preliminary;
  container-type: revert;
}

Let’s take a look at a easy and considerably contrived instance to get the purpose throughout. We will outline a customized property in a container, say a --theme.

.cards-container {
  --theme: darkish;
}

From right here, we are able to verify if the container has that desired property and, if it does, apply kinds to its descendant components. We will’t instantly type the container because it may unleash an infinite loop of altering the kinds and querying the kinds.

.cards-container {
  --theme: darkish;
}

@container type(--theme: darkish) {
  .playing cards {
    background-color: black;
  }
}

See that type() operate? Sooner or later, we could wish to verify if a component has a max-width: 400px via a method question as a substitute of checking if the ingredient’s computed worth is greater than 400px in a dimension question. That’s why we use the type() wrapper to distinguish type queries from dimension queries.

/* Measurement question */
@container (width > 60ch) {
  .playing cards {
    flex-direction: column;
  }
}

/* Fashion question */
@container type(--theme: darkish) {
  .playing cards {
    background-color: black;
  }
}

Each sorts of container queries search for the closest ancestor with a corresponding containment-type. In a type() question, it is going to at all times be the mum or dad since all components have type containment by default. On this case, the direct mum or dad of the .playing cards ingredient in our ongoing instance is the .cards-container ingredient. If we wish to question non-direct mother and father, we are going to want the container-name property to distinguish between containers when making a question.

.cards-container {
  container-name: cardsContainer;
  --theme: darkish;
}

@container cardsContainer type(--theme: darkish) {
  .card {
    shade: white;
  }
}

Bizarre and Complicated Issues About Container Fashion Queries

Fashion queries are utterly new and convey one thing by no means seen in CSS, so they’re sure to have some complicated qualities as we wrap our heads round them — some which can be utterly intentional and nicely thought-out and a few which can be maybe unintentional and could also be up to date in future variations of the specification.

Fashion and Measurement Containment Aren’t Mutually Unique

One intentional perk, for instance, is {that a} container can have each dimension and magnificence containment. Nobody would fault you for anticipating that dimension and magnificence containment are mutually unique considerations, so setting a component to one thing like container-type: inline-size would make all type queries ineffective.

Nevertheless, one other humorous factor about container queries is that components have type containment by default, and there isn’t actually a approach to take away it. Take a look at this subsequent instance:

.cards-container {
  container-type: inline-size;
  --theme: darkish;
}

@container type(--theme: darkish) {
  .card {
    background-color: black;
  }
}

@container (width < 700px) {
  .card {
    background-color: pink;
  }
}

See that? We will nonetheless question the weather by type even once we explicitly set the container-type to inline-size. This appears contradictory at first, nevertheless it does make sense, contemplating that type and dimension queries are computed independently. It’s higher this manner since each queries don’t essentially battle with one another; a method question may change the colours in a component relying on a customized property, whereas a container question modifications a component’s flex-direction when it will get too small for its contents.

See the Pen [Conflicting Style and Size Queries [forked]](https://codepen.io/smashingmag/pen/KKLyeQQ) by Monknow.

See the Pen Conflicting Fashion and Measurement Queries [forked] by Monknow.

If energetic type and dimension queries are configured to use conflicting kinds, essentially the most particular selector wins based on the cascade, so the weather have a black background shade till the container will get under 700px and the dimensions question (which is written with extra specificity on this instance than the final one) kicks in.

Unnamed Fashion Queries Verify Each Ancestor For a Match

In that final instance, you will have observed one other bizarre factor in type queries: The .card ingredient is inside an unnamed type question, so since all components have a method containment, it needs to be querying its mum or dad, .playing cards. Nevertheless, the .playing cards ingredient doesn’t have any form of --theme property; it’s the .cards-container ingredient that does, however the type question is energetic!

.cards-container {
--theme: darkish;  
}

@container type(--theme: darkish) {
  /* That is nonetheless energetic! */
  .card {
    background-color: black;
  }
}

How does this occur? Don’t unnamed type queries question solely their mum or dad ingredient? Nicely, not precisely. If the mum or dad doesn’t have the customized property, then the unnamed type question appears to be like for ancestors increased up the chain. If we add a --theme property on the mum or dad with one other worth, you will notice the type question will use that ingredient because the container, and the question now not matches.

.cards-container {
--theme: darkish;  
}

.playing cards {
  --theme: mild; /* That is now the matching container for the question */
}

/* This question is now not energetic! */
@container type(--theme: darkish) {
  .card {
    background-color: black;
  }
}

I don’t understand how intentional this habits is, nevertheless it exhibits how messy issues can get when working with unnamed containers.

Components Are Fashion Containers By Default, However Types Queries Are Not

The truth that type queries are the default container-type and dimension is the default question appears a bit of mismatched in that we have now to explicitly declare a type() operate to jot down the default kind of question whereas there is no such thing as a corresponding dimension() operate. This isn’t a knock on the specification, however a kind of issues in CSS you simply have to recollect.

What Are Container Fashion Queries Good For?

At first look, type queries look like a function that opens up infinite potentialities. However after enjoying with them, you don’t actually get a transparent concept of what downside they’d resolve — a minimum of not after the time I’ve spent with them. All of the use instances I’ve seen or thought up aren’t instantly all that helpful, and the obvious ones resolve issues with well-established options already in place. A brand new means of writing CSS based mostly on kinds reacting to different kinds appears liberating (the extra methods to jot down CSS, the merrier!), but when we’re already having bother usually naming issues, think about how robust managing and sustaining these states and kinds may be.

I do know all these are daring statements, however they aren’t unfounded, so let me unpack them.

The Most Apparent Use Case

As we’ve mentioned, we are able to solely question customized properties with type queries in the intervening time, so the clearest use for them is storing bits of state that can be utilized to alter UI kinds after they change.

Let’s say we have now an internet app, maybe a sport that includes a worldwide leaderboard of prime gamers. Every merchandise within the leaderboard is a element based mostly on a participant that wants totally different styling relying on that participant’s place on the leaderboard. We may type the first-place participant with a gold background, the second-place participant with silver, and the third-place with bronze, whereas the remaining gamers are all styled with the identical background shade.

Let’s additionally assume that this isn’t a static leaderboard. Gamers change locations as their scores change. Meaning we’re most certainly serving the utilizing a server-side rendering (SSR) framework to maintain the leaderboard updated with the newest knowledge, so we may insert that knowledge into the UI via inline kinds with every place as a customized property, --position: quantity.

Right here’s how we would construction the markup:

  1. Roi's avatar

    Roi

Now, we are able to use type queries to verify the present merchandise’s place and apply their respective shiny backgrounds to the leaderboard.

.item-container {
  container-name: leaderboard;
  /* No want to use container-type: regular */
}

@container leaderboard type(--position: 1) {
  .merchandise {
    background: linear-gradient(45deg, yellow, orange); /* gold */
  }
}

@container leaderboard type(--position: 2) {
  .merchandise {
    background: linear-gradient(45deg, gray, white); /* silver */
  }
}

@container leaderboard type(--position: 3) {
  .merchandise {
    background: linear-gradient(45deg, brown, peru); /* bronze */
  }
}

See the Pen [Style Queries Use Case [forked]](https://codepen.io/smashingmag/pen/vYwWrRL) by Monknow.

See the Pen Fashion Queries Use Case [forked] by Monknow.

Some browser shoppers don’t absolutely assist type queries from an embedded CodePen, nevertheless it ought to work should you absolutely open the demo in one other tab. In any case, here’s a screenshot of the way it appears to be like simply in case.

Two leaderboard UIs. One before style queries and one after styles have been applied
Determine 2: We will apply kinds to a component’s youngsters and descendants when the ingredient’s kinds match a sure situation. (Massive preview)

However We Can Obtain the Identical Factor With CSS Lessons and IDs

Most container question guides and tutorials I’ve seen use comparable examples to show the overall idea, however I can’t cease pondering irrespective of how cool type queries are, we are able to obtain the identical end result utilizing lessons or IDs and with much less boilerplate. As an alternative of passing the state as an inline type, we may merely add it as a category.

  1. Roi's avatar

    Roi

Alternatively, we may add the place quantity instantly inside an id so we don’t need to convert the quantity right into a string:

  1. Roi's avatar

    Roi

Each of those approaches depart us with cleaner HTML than the container queries strategy. With type queries, we have now to wrap our components inside a container — even when we don’t semantically want it — due to the truth that containers (rightly) are unable to type themselves.

We even have much less boilerplate-y code on the CSS facet:

#item-1 {
  background: linear-gradient(45deg, yellow, orange); 
}

#item-2 {
  background: linear-gradient(45deg, gray, white);
}

#item-3 {
  background: linear-gradient(45deg, brown, peru);
}

See the Pen [Style Queries Use Case Replaced with Classes [forked]](https://codepen.io/smashingmag/pen/oNRoydN) by Monknow.

See the Pen Fashion Queries Use Case Changed with Lessons [forked] by Monknow.

As an apart, I do know that utilizing IDs as styling hooks is commonly considered as a no-no, however that’s solely as a result of IDs should be distinctive within the sense that no two cases of the identical ID are on the web page on the identical time. On this occasion, there’ll by no means be a couple of first-place, second-place, or third-place participant on the web page, making IDs a protected and acceptable selection on this state of affairs. However, sure, we may additionally use another kind of selector, say a data-* attribute.

There’s something that would add plenty of worth to type queries: a spread syntax for querying kinds. That is an open function that Miriam Suzanne proposed in 2023, the concept being that it queries numerical values utilizing vary comparisons similar to dimension queries.

Think about if we needed to use a lightweight purple background shade to the remainder of the highest ten gamers within the leaderboard instance. As an alternative of including a question for every place from 4 to 10, we may add a question that checks a spread of values. The syntax is clearly not within the spec at the moment, however let’s say it appears to be like one thing like this simply to push the purpose throughout:

/* Don't do this at house! */
@container leaderboard type(4 >= --position <= 10) {
  .merchandise {
    background: linear-gradient(45deg, purple, fuchsia);
  }
}

On this fictional and hypothetical instance, we’re:

  • Monitoring a container referred to as leaderboard,
  • Making a type() question towards the container,
  • Evaluating the --position customized property,
  • Searching for a situation the place the customized property is about to a worth equal to a quantity that’s better than or equal to 4 and fewer than or equal to 10.
  • If the customized property is a worth inside that vary, we set a participant’s background shade to a linear-gradient() that goes from purple to fuschia.

That is very cool, but when this sort of habits is more likely to be accomplished utilizing parts in trendy frameworks, like React or Vue, we may additionally arrange a spread in JavaScript and toggle on a .top-ten class when the situation is met.

See the Pen [Style Ranged Queries Use Case Replaced with Classes [forked]](https://codepen.io/smashingmag/pen/OJYOEZp) by Monknow.

See the Pen Fashion Ranged Queries Use Case Changed with Lessons [forked] by Monknow.

Certain, it’s nice to see that we are able to do that kind of factor instantly in CSS, nevertheless it’s additionally one thing with an present well-established resolution.

Separating Fashion Logic From Logic Logic

To date, type queries don’t appear to be essentially the most handy resolution for the leaderboard use case we checked out, however I wouldn’t deem them ineffective solely as a result of we are able to obtain the identical factor with JavaScript. I’m a giant advocate of reaching for JavaScript solely when essential and solely in sprinkles, however type queries, those the place we are able to solely verify for customized properties, are most certainly to be helpful when paired with a UI framework the place we are able to simply attain for JavaScript inside a element. I’ve been utilizing Astro an terrible lot these days, and in that context, I don’t see why I’d select a method question over programmatically altering a category or ID.

Nevertheless, a case may be made that implementing type logic inside a element is messy. Possibly we should always hold the logic relating to kinds within the CSS away from the remainder of the logic logic, i.e., the stateful modifications inside a element like conditional rendering or capabilities like useState and useEffect in React. The type logic can be the conditional checks we do so as to add or take away class names or IDs with a view to change kinds.

If we backtrack to our leaderboard instance, checking a participant’s place to use totally different kinds can be type logic. We may certainly verify {that a} participant’s leaderboard place is between 4 and ten utilizing JavaScript to programmatically add a .top-ten class, however it might imply leaking our type logic into our element. In React (for familiarity, however it might be much like different frameworks), the element could appear to be this:

const LeaderboardItem = ({place}) => {
  
  • = 4 && place <= 10 ? "top-ten" : ""}`} id={`item-${place}`}> Roi's avatar

    Roi

  • ; };

    In addition to this being ugly-looking code, including the type logic in JSX can get messy. In the meantime, type queries can cross the --position worth to the kinds and deal with the logic instantly within the CSS the place it’s getting used.

    const LeaderboardItem = ({place}) => {
      
  • Roi's avatar

    Roi

  • ; };

    A lot cleaner, and I feel that is nearer to the worth proposition of favor queries. However on the identical time, this instance makes a big leap of assumption that we are going to get a spread syntax for type queries sooner or later, which isn’t a accomplished deal.

    Conclusion

    There are many groups engaged on making trendy CSS higher, and never all options need to be groundbreaking miraculous additions.

    Measurement queries are positively an improve from media queries for responsive design, however type queries look like extra of an answer in search of an issue.

    It merely doesn’t resolve any particular difficulty or is healthier sufficient to exchange different approaches, a minimum of so far as I’m conscious.

    Even when, sooner or later, type queries will have the ability to verify for any property, that introduces a complete new can of worms the place kinds are able to reacting to different kinds. This appears thrilling at first, however I can’t shake the sensation it might be pointless and even chaotic: kinds reacting to kinds, reacting to kinds, and so forth with an pointless facet of boilerplate. I’d argue {that a} extra prudent strategy is to jot down all of your kinds declaratively collectively in a single place.

    Possibly it might be helpful for internet extensions (like Darkish Reader) to allow them to higher verify kinds in third-party web sites? I can’t clearly see it. When you have any options on how CSS Container Fashion Queries can be utilized to jot down higher CSS that I could have ignored, please let me know within the feedback! I’d like to understand how you’re serious about them and the types of how you think about your self utilizing them in your work.

    Smashing Editorial
    (gg, yk)





    Supply hyperlink

    LEAVE A REPLY

    Please enter your comment!
    Please enter your name here