Guides – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Tue, 23 Jul 2024 13:26:39 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 Guides – CSS-Tricks https://css-tricks.com 32 32 45537868 CSS Selectors https://css-tricks.com/css-selectors/ https://css-tricks.com/css-selectors/#comments Mon, 15 Jul 2024 16:13:15 +0000 https://css-tricks.com/?p=378745 A complete guide covering all of the various methods we have to select elements in CSS and how to use them for applying styles.


CSS Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Overview

CSS is really good at many things, but it’s really, really good at two specific things: selecting elements and styling them. That’s the raison d’être for CSS and why it’s a core web language. In this guide, we will cover the different ways to select elements — because the styles we write are pretty much useless without the ability to select which elements to apply them to.

The source of truth for CSS selectors is documented in the Selectors Module Level 4 specification. With one exception (which we’ll get to), all of the selectors covered here are well-covered by browsers across the board, and most certainly by all modern browsers.

In addition to selectors, this guide also looks at CSS combinators. If selectors identify what we are selecting, you might think of combinators as how the styles are applied. Combinators are like additional instructions we give CSS to select a very particular element on the page, not totally unlike the way we can use filters in search engines to find the exact result we want.

Quick reference

Common Selectors

/* Universal */
* {
  box-sizing: border-box;
}

/* Type or Tag */
p {
  margin-block: 1.5rem;
}

/* Classname */
.class {
  text-decoration: underline;
}

/* ID */
#id {
  font-family: monospace;
}

/* Relational */
li:has(a) {
  display: flex;
}

Common Combinators

/* Descendant */
header h1 {
  /* Selects all Heading 1 elements in a Header element. */
}

/* Child */
header > h1 {
  /* Selects all Heading 1 elements that are children of Header elements. */
}

/* General sibling */
h1 ~ p {
  /* Selects a Paragraph as long as it follows a Heading 1. */
}

/* Adjacent sibling */
h1 + p {
  /* Selects a Paragraph if it immediately follows a Heading 1 */
}

/* Chained */
h1, p { 
  /* Selects both elements. */
}

General Selectors

When we talk about CSS selectors, we’re talking about the first part of a CSS ruleset:

/* CSS Ruleset */
selector {
  /* Style rule */
  property: value;
}

See that selector? That can be as simple as the HTML tag we want to select. For example, let’s select all <article> elements on a given page.

/* Select all <article> elements... */
article {
  /* ... and apply this background-color on them */
  background-color: hsl(25 100% 50%);
}

That’s the general process of selecting elements to apply styles to them. Selecting an element by its HTML tag is merely one selector type of several. Let’s see what those are in the following section.

Element selectors

Element selectors are exactly the type of selector we looked at in that last example: Select the element’s HTML tag and start styling!

That’s great and all, but consider this: Do you actually want to select all of the <article> elements on the page? That’s what we’re doing when we select an element by its tag — any and all HTML elements matching that tag get the styles. The following demo selects all <article> elements on the page, then applies a white (#fff) background to them. Notice how all three articles get the white background even though we only wrote one selector.

I’ve tried to make it so the relevant for code for this and other demos in this guide is provided at the top of the CSS tab. Anything in a @layer can be ignored. And if you’re new to @layer, you can learn all about it in our CSS Cascade Layers guide.

But maybe what we actually want is for the first element to have a different background — maybe it’s a featured piece of content and we need to make it stand out from the other articles. That requires us to be more specific in the type of selector we use to apply the styles.

Let’s turn our attention to other selector types that allow us to be more specific about what we’re selecting.

ID selectors

ID selectors are one way we can select one element without selecting another of the same element type. Let’s say we were to update the HTML in our <article> example so that the first article is “tagged” with an ID:

<article id="featured">
  <!-- Article 1 -->
</article>

<article>
  <!-- Article 2 -->
</article>

<article>
  <!-- Article 3 -->
</article>

Now we can use that ID to differentiate that first article from the others and apply styles specifically to it. We prepend a hashtag character (#) to the ID name when writing our CSS selector to properly select it.

/* Selects all <article> elements */
article {
  background: #fff;
}

/* Selects any element with id="featured" */
#featured {
  background: hsl(35 100% 90%);
  border-color: hsl(35 100% 50%);
}

There we go, that makes the first article pop a little more than the others!

Before you go running out and adding IDs all over your HTML, be aware that IDs are considered a heavy-handed approach to selecting. IDs are so specific, that it is tough to override them with other styles in your CSS. IDs have so much specificity power than any selector trying to override it needs at least an ID as well. Once you’ve reached near the top of the ladder of this specificity war, it tends to lead to using !important rules and such that are in turn nearly impossible to override.

Let’s rearrange our CSS from that last example to see that in action:

/* Selects any element with id="featured" */
#featured {
  background: hsl(35 100% 90%);
  border-color: hsl(35 100% 50%);
}

/* Selects all <article> elements */
article {
  background: #fff;
}

The ID selector now comes before the element selector. According to how the CSS Cascade determines styles, you might expect that the article elements all get a white background since that ruleset comes after the ID selector ruleset. But that’s not what happens.

So, you see how IDs might be a little too “specific” when it comes to selecting elements because it affects the order in which the CSS Cascade applies styles and that makes styles more difficult to manage and maintain.

The other reason to avoid IDs as selectors? We’re technically only allowed to use an ID once on a page, per ID. In other words, we can have one element with #featured but not two. That severely limits what we’re able to style if we need to extend those styles to other elements — not even getting into the difficulty of overriding the ID’s styles.

A better use case for IDs is for selecting items in JavaScript — not only does that prevent the sort of style conflict we saw above, but it helps maintain a separation of concerns between what we select in CSS for styling versus what we select in JavaScript for interaction.

Another thing about ID selectors: The ID establishes what we call an “anchor” which is a fancy term for saying we can link directly to an element on the page. For example, if we have an article with an ID assigned to it:

<article id="featured">...</article>

…then we can create a link to it like this:

<a href="featured">Jump to article below ⬇️</a>

<!-- muuuuuuch further down the page. -->

<article id="featured">...</article>

Clicking the link will navigate you to the element as though the link is anchored to that element. Try doing exactly that in the following demo:

This little HTML goodie opens up some pretty darn interesting possibilities when we sprinkle in a little CSS. Here are a few articles to explore those possibilities.

Class selectors

Class selectors might be the most commonly used type of CSS selector you will see around the web. Classes are ideal because they are slightly more specific than element selectors but without the heavy-handedness of IDs. You can read a deep explanation of how the CSS Cascade determines specificity, but the following is an abbreviated illustration focusing specifically (get it?!) on the selector types we’ve looked at so far.

Showing element, class, and ID selectors in a horizontal row from least specific to most specific.

That’s what makes class selectors so popular — they’re only slightly more specific than elements, but keep specificity low enough to be manageable if we need to override the styles in one ruleset with styles in another.

The only difference when writing a class is that we prepend a period (.) in front of the class name instead of the hashtag (#).

/* Selects all <article> elements */
article {
  background: #fff;
}

/* Selects any element with class="featured" */
.featured {
  background: hsl(35 100% 90%);
  border-color: hsl(35 100% 50%);
}

Here’s how our <article> example shapes up when we swap out #featured with .featured.

Same result, better specificity. And, yes, we can absolutely combine different selector types on the same element:

<article id="someID" class="featured">...</article>

Do you see all of the possibilities we have to select an <article>? We can select it by:

  • Its element type (article)
  • Its ID (#someID)
  • Its class (.featured)

The following articles will give you some clever ideas for using class selectors in CSS.

But we have even more ways to select elements like this, so let’s continue.

Attribute selectors

ID and class selectors technically fall into this attribute selectors category. We call them “attributes” because they are present in the HTML and give more context about the element. All of the following are attributes in HTML:

<!-- ID, Class, Data Attribute -->
<article id="#id" class=".class" data-attribute="attribute">
</article>

<!-- href, Title, Target -->
<a href="https://css-tricks.com" title="Visit CSS-Tricks" target="_blank"></a>

<!-- src, Width, Height, Loading -->
<img src="star.svg" width="250" height="250" loading="laxy" >

<!-- Type, ID, Name, Checked -->
<input type="checkbox" id="consent" name="consent" checked />

<!-- Class, Role, Aria Label -->
<div class="buttons" role="tablist" aria-label="Tab Buttons">

Anything with an equals sign (=) followed by a value in that example code is an attribute. So, we can technically style all links with an href attribute equal to https://css-tricks.com:

a[href="https://css-tricks.com"] {
  color: orangered;
}

Notice the syntax? We’re using square brackets ([]) to select an attribute instead of a period or hashtag as we do with classes and IDs, respectively.

The equals sign used in attributes suggests that there’s more we can do to select elements besides matching something that’s exactly equal to the value. That is indeed the case. For example, we can make sure that the matching selector is capitalized or not. A good use for that could be selecting elements with the href attribute as long as they do not contain uppercase letters:

/* Case sensitive */
a[href*='css-tricks' s] {}

The s in there tells CSS that we only want to select a link with an href attribute that does not contain uppercase letters.

<!-- 👎 No match -->
<a href="https://CSS-Tricks.com">...</a>

<!-- 👍 Match! -->
<a href="https://css-tricks.com">...</a>

If case sensitivity isn’t a big deal, we can tell CSS that as well:

/* Case insensitive */
a[href*='css-tricks' i] {}

Now, either one of the link examples will match regardless of there being upper- or lowercase letters in the href attribute.

<!-- 👍 I match! -->
<a href="https://CSS-Tricks.com">...</a>

<!-- 👍 I match too! -->
<a href="https://css-tricks.com">...</a>

There are many, many different types of HTML attributes. Be sure to check out our Data Attributes guide for a complete rundown of not only [data-attribute] but how they relate to other attributes and how to style them with CSS.

Universal selector

CSS-Tricks has a special relationship with the Universal Selector — it’s our logo!

That’s right, the asterisk symbol (*) is a selector all unto itself whose purpose is to select all the things. Quite literally, we can select everything on a page — every single element — with that one little asterisk. Note I said every single element, so this won’t pick up things like IDs, classes, or even pseudo-elements. It’s the element selector for selecting all elements.

/* Select ALL THE THINGS! 💥 */
* {
  /* Styles */
}

Or, we can use it with another selector type to select everything inside a specific element.

/* Select everything in an <article> */
article * {
  /* Styles */
}

That is a handy way to select everything in an <article>, even in the future if you decide to add other elements inside that element to the HTML. The times you’ll see the Universal Selector used most is to set border-sizing on all elements across the board, including all elements and pseudo-elements.

*,
*::before,
*::after {
  box-sizing: border-box;
}

There’s a good reason this snippet of CSS winds up in so many stylesheets, which you can read all about in the following articles.

Sometimes the Universal Selector is implied. For example, when using a pseudo selector at the start of a new selector. These are selecting exactly the same:

*:has(article) { }
:has(article)  { }

Pseudo-selectors

Pseudo-selectors are for selecting pseudo-elements, just as element selectors are for selecting elements. And a pseudo-element is just like an element, but it doesn’t actually show up in the HTML. If pseudo-elements are new to you, we have a quick explainer you can reference.

Every element has a ::before and ::after pseudo-element attached to it even though we can’t see it in the HTML.

<div class="container">
  <!-- ::before psuedo-element here -->
  <div>Item</div>
  <div>Item</div>
  <div>Item</div>
  <!-- ::after psuedo-element here -->
</div>

These are super handy because they’re additional ways we can hook into an element an apply additional styles without adding more markup to the HTML. Keep things as clean as possible, right?!

We know that ::before and ::after are pseudo-elements because they are preceded by a pair of colons (::). That’s how we select them, too!

.container::before {
  /* Styles */
}

The ::before and ::after pseudo-elements can also be written with a single colon — i.e., :before and :after — but it’s still more common to see a double colon because it helps distinguish pseudo-elements from pseudo-classes.

But there’s a catch when using pseudo-selectors: they require the content property. That’s because pseudos aren’t “real” elements but ones that do not exist as far as HTML is concerned. That means they need content that can be displayed… even if it’s empty content:

.container::before {
  content: "";
}

Of course, if we were to supply words in the content property, those would be displayed on the page.


Complex selectors

Complex selectors may need a little marketing help because “complex” is an awfully scary term to come across when you’re in the beginning stages of learning this stuff. While selectors can indeed become complex and messy, the general idea is super straightforward: we can combine multiple selectors in the same ruleset.

Let’s look at three different routes we have for writing these “not-so-complex” complex selectors.

Listing selectors

First off, it’s possible to combine selectors so that they share the same set of styles. All we do is separate each selector with a comma.

.selector-1,
.selector-2,
.selector-3 {
  /* We share these styles! 🤗 */
}

You’ll see this often when styling headings — which tend to share the same general styling except, perhaps, for font-size.

h1,
h2,
h3,
h4,
h5,
h6 {
  color: hsl(25 80% 15%);
  font-family: "Poppins", system-ui;
}

Adding a line break between selectors can make things more legible. You can probably imagine how complex and messy this might get. Here’s one, for example:

section h1, section h2, section h3, section h4, section h5, section h6, 
article h1, article h2, article h3, article h4, article h5, article h6, 
aside h1, aside h2, aside h3, aside h4, aside h5, aside h6, 
nav h1, nav h2, nav h3, nav h4, nav h5, nav h6 {
  color: #BADA55;
}

Ummmm, okay. No one wants this in their stylesheet. It’s tough to tell what exactly is being selected, right?

The good news is that we have modern ways of combining these selectors more efficiently, such as the :is() pseudo selector. In this example, notice that we’re technically selecting all of the same elements. If we were to take out the four section, article, aside, and nav element selectors and left the descendants in place, we’d have this:

h1, h2, h3, h4, h5, h6, 
h1, h2, h3, h4, h5, h6,
h1, h2, h3, h4, h5, h6, 
h1, h2, h3, h4, h5, h6, {
  color: #BADA55;
}

The only difference is which element those headings are scoped to. This is where :is() comes in handy because we can match those four elements like this:

:is(section, article, aside, nav) {
  color: #BADA55;
}

That will apply color to the elements themselves, but what we want is to apply it to the headings. Instead of listing those out for each heading, we can reach for :is() again to select them in one fell swoop:

/* Matches any of the following headings scoped to any of the following elements.  */
:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) {
  color: #BADA55;
}

While we’re talking about :is() it’s worth noting that we have the :where() pseudo selector as well and that it does the exact same thing as :is(). The difference? The specificity of :is() will equal the specificity of the most specific element in the list. Meanwhile, :where() maintains zero specificity. So, if you want a complex selector like this that’s easier to override, go with :where() instead.

Nesting selectors

That last example showing how :is() can be used to write more efficient complex selectors is good, but we can do even better now that CSS nesting is a widely supported feature.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
120117No12017.2

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712717.2

CSS nesting allows us to better see the relationship between selectors. You know how we can clearly see the relationship between elements in HTML when we indent descendant elements?

<!-- Parent -->
<article>
  <!-- Child -->
  <img src="" alt="...">
  <!-- Child -->
  <div class="article-content">
    <!-- Grandchild -->
    <h2>Title</h2>
    <!-- Grandchild -->
    <p>Article content.</p>
  </div>
</article>

CSS nesting is a similar way that we can format CSS rulesets. We start with a parent ruleset and then embed descendant rulesets inside. So, if we were to select the <h2> element in that last HTML example, we might write a descendant selector like this:

article h2 { /* Styles */ }

With nesting:

article  {
  /* Article styles */

  h2 { /* Heading 2 styles */ }
}

You probably noticed that we can technically go one level deeper since the heading is contained in another .article-content element:

article  {
  /* Article styles */

  .article-content {
    /* Container styles */

    h2 { /* Heading 2 styles */ }
  }
}

So, all said and done, selecting the heading with nesting is the equivalent of writing a descendant selector in a flat structure:

article .article-content h2 { /* Heading 2 styles */ }

You might be wondering how the heck it’s possible to write a chained selector in a nesting format. I mean, we could easily nest a chained selector inside another selector:

article  {
  /* Article styles */

  h2.article-content {
    /* Heading 2 styles */
  }
}

But it’s not like we can re-declare the article element selector as a nested selector:

article  {
  /* Article styles */

  /* Nope! 👎 */
  article.article-element {
    /* Container styles */  

    /* Nope! 👎 */
    h2.article-content {
      /* Heading 2 styles */
    }
  }
}

Even if we could do that, it sort of defeats the purpose of a neatly organized nest that shows the relationships between selectors. Instead, we can use the ampersand (&) symbol to represent the selector that we’re nesting into. We call this the nesting selector.

article  {

  &.article-content {
    /* Equates to: article.article-content */
  }
}

Compounding selectors

We’ve talked quite a bit about the Cascade and how it determines which styles to apply to matching selectors using a specificity score. We saw earlier how an element selector is less specific than a class selector, which is less specific than an ID selector, and so on.

article { /* Specificity: 0, 0, 1 */ }
.featured { /* Specificity: 0, 1, 0 */ }
#featured { /* Specificity: 1, 0, 0 */ }

Well, we can increase specificity by chaining — or “compounding” — selectors together. This way, we give our selector a higher priority when it comes to evaluating two or more matching styles. Again, overriding ID selectors is incredibly difficult so we’ll work with the element and class selectors to illustrate chained selectors.

We can chain our article element selector with our .featured class selector to generate a higher specificity score.

article { /* Specificity: 0, 0, 1 */ }
.featured { /* Specificity: 0, 1, 0 */ }

articie.featured { /* Specificity: 0, 1, 1 */ }

This new compound selector is more specific (and powerful!) than the other two individual selectors. Notice in the following demo how the compound selector comes before the two individual selectors in the CSS yet still beats them when the Cascade evaluates their specificity scores.

Interestingly, we can use “fake” classes in chained selectors as a strategy for managing specificity. Take this real-life example:

.wp-block-theme-button .button:not(.specificity):not(.extra-specificity) { }

Whoa, right? There’s a lot going on there. But the idea is this: the .specificity and .extra-specificity class selectors are only there to bump up the specificity of the .wp-block-theme .button descendant selector. Let’s compare the specificity score with and without those artificial classes (that are :not() included in the match).

.wp-block-theme-button .button {
  /* Specificity: 0, 2, 0 */
}

.wp-block-theme-button .button:not(.specificity) {
  /* Specificity: 0, 3, 0 */
}

.wp-block-theme-button  .button:not(.specificity):not(.extra-specificity {
  /* Specificity: 0, 4, 0 */
}

Interesting! I’m not sure if I would use this in my own CSS but it is a less heavy-handed approach than resorting to the !important keyword, which is just as tough to override as an ID selector.


Combinators

If selectors are “what” we select in CSS, then you might think of CSS combinators as “how” we select them. they’re used to write selectors that combine other selectors in order to target elements. Inception!

The name “combinator” is excellent because it accurately conveys the many different ways we’re able to combine selectors. Why would we need to combine selectors? As we discussed earlier with Chained Selectors, there are two common situations where we’d want to do that:

  • When we want to increase the specificity of what is selected.
  • When we want to select an element based on a condition.

Let’s go over the many types of combinators that are available in CSS to account for those two situations in addition to chained selectors.

Descendant combinator

We call it a “descendant” combinator because we use it to select elements inside other elements, sorta like this:

/* Selects all elements in .parent with .child class */
.parent .child {}

…which would select all of the elements with the .child class in the following HTML example:

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>

  <div class="friend"></div>

  <div class="child"></div>
  <div class="child"></div>
</div>

See that element with the .friend classname? That’s the only element inside of the .parent element that is not selected with the .parent .child {} descendant combinator since it does not match .child even though it is also a descendant of the .parent element.

Child combinator

A child combinator is really just an offshoot of the descendant combinator, only it is more specific than the descendant combinator because it only selects direct children of an element, rather than any descendant.

Let’s revise the last HTML example we looked at by introducing a descendant element that goes deeper into the family tree, like a .grandchild:

<div class="parent">
  <div class="child"></div>
  <div class="child">
    <div class="grandchild"></div>
  </div>
  <div class="child"></div>
  <div class="child"></div>
</div>

So, what we have is a .parent to four .child elements, one of which contains a .grandchild element inside of it.

Maybe we want to select the .child element without inadvertently selecting the second .child element’s .grandchild. That’s what a child combinator can do. All of the following child combinators would accomplish the same thing:

/* Select only the "direct" children of .parent */
.parent > .child {}
.parent > div {}
.parent > * {}

See how we’re combining different selector types to make a selection? We’re combinating, dangit! We’re just doing it in slightly different ways based on the type of child selector we’re combining.

/* Select only the "direct" children of .parent */
.parent > #child { /* direct child with #child ID */
.parent > .child { /* direct child with .child class */ }
.parent > div { /* direct child div elements */ }
.parent > * { /* all direct child elements */ }

It’s pretty darn neat that we not only have a way to select only the direct children of an element, but be more or less specific about it based on the type of selector. For example, the ID selector is more specific than the class selector, which is more specific than the element selector, and so on.

General sibling combinator

If two elements share the same parent element, that makes them siblings like brother and sister. We saw an example of this in passing when discussing the descendant combinator. Let’s revise the class names from that example to make the sibling relationship a little clearer:

<div class="parent">
  <div class="brother"></div>
  <div class="sister"></div>
</div>

This is how we can select the .sister element as long as it is preceded by a sibling with class .brother.

/* Select .sister only if follows .brother */
.brother ~ .sister { }

The Tilda symbol (~) is what tells us this is a sibling combinator.

It doesn’t matter if a .sister comes immediately after a .brother or not — as long as a .sister comes after a brother and they share the same parent element, it will be selected. Let’s see a more complicated HTML example:

<main class="parent">
  
  <!-- .sister immediately after .brother -->
  <div class="brother"></div>
  <div class="sister"></div>

  <!-- .sister immediately after .brother -->
  <div class="brother"></div>
  <div class="sister"></div>
  <!-- .sister immediately after .sister -->
  <div class="sister"></div>

  <!-- .cousin immediately after .brother -->
  <div class="brother"></div>
  <div class="cousin">
    <!-- .sister contained in a .cousin -->
    <div class="sister"></div>
  </div>
</main>

The sibling combinator we wrote only selects the first three .sister elements because they are the only ones that come after a .brother element and share the same parent — even in the case of the third .sister which comes after another sister! The fourth .sister is contained inside of a .cousin, which prevents it from matching the selector.

Let’s see this in context. So, we can select all of the elements with an element selector since each element in the HTML is a div:

From there, we can select just the brothers with a class selector to give them a different background color:

We can also use a class selector to set a different background color on all of the elements with a .sister class:

And, finally, we can use a general sibling combinator to select only sisters that are directly after a brother.

Did you notice how the last .sister element’s background color remained green while the others became purple? That’s because it’s the only .sister in the bunch that does not share the same .parent as a .brother element.

Adjacent combinator

Believe it or not, we can get even more specific about what elements we select with an adjacent combinator. The general sibling selector we just looked at will select all of the .sister elements on the page as long as it shares the same parent as .brother and comes after the .brother.

What makes an adjacent combinator different is that it selects any element immediately following another. Remember how the last .sister didn’t match because it is contained in a different parent element (i.e., .cousin)? Well, we can indeed select it by itself using an adjacent combinator:

/* Select .sister only if directly follows .brother */
.brother + .sister { }

Notice what happens when we add that to our last example:

The first two .sister elements changed color! That’s because they are the only sisters that come immediately after a .brother. The third .sister comes immediately after another .sister and the fourth one is contained in a .cousin which prevents both of them from matching the selection.


Learn more about CSS selectors


References

The vast majority of what you’re reading here is information pulled from articles we’ve published on CSS-Tricks and those are linked up throughout the guide. In addition to those articles, the following resources were super helpful for putting this guide together.


CSS Selectors originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-selectors/feed/ 2 378745
CSS Container Queries https://css-tricks.com/css-container-queries/ https://css-tricks.com/css-container-queries/#comments Mon, 10 Jun 2024 16:12:50 +0000 https://css-tricks.com/?p=378111 The main idea of CSS Container Queries is to register an element as a “container” and apply styles to other elements when the container element meets certain conditions.


CSS Container Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Container queries are often considered a modern approach to responsive web design where traditional media queries have long been the gold standard — the reason being that we can create layouts made with elements that respond to, say, the width of their containers rather than the width of the viewport.

.parent {
  container-name: hero-banner;
  container-type: inline-size;

  /* or container: hero-banner / inline-size; */
}

}

.child {
  display: flex;
  flex-direction: column;
}

/* When the container is greater than 60 characters... */
@container hero-banner (width > 60ch) {
  /* Change the flex direction of the .child element. */
  .child { 
    flex-direction: row;
  }
}

Why care about CSS Container Queries?

  1. When using a container query, we give elements the ability to change based on their container’s size, not the viewport.
  1. They allow us to define all of the styles for a particular element in a more predictable way.
  1. They are more reusable than media queries in that they behave the same no matter where they are used. So, if you were to create a component that includes a container query, you could easily drop it into another project and it will still behave in the same predictable fashion.
  1. They introduce new types of CSS length units that can be used to size elements by their container’s size.

Registering Elements as Containers

.cards {
  container-name: card-grid;
  container-type: inline-size;

  /* Shorthand */
  container: card-grid / inline-size;
}

This example registers a new container named card-grid that can be queried by its inline-size, which is a fancy way of saying its “width” when we’re working in a horizontal writing mode. It’s a logical property. Otherwise, “inline” would refer to the container’s “height” in a vertical writing mode.

  • The container-name property is used to register an element as a container that applies styles to other elements based on the container’s size and styles.
  • The container-type property is used to register an element as a container that can apply styles to other elements when it meets certain conditions.
  • The container property is a shorthand that combines the container-name and container-type properties into a single declaration.

Some Possible Gotchas

Querying a Container

@container my-container (width > 60ch) {
  article {
    flex-direction: row;
  }
}
  • The @container at-rule property informs the browser that we are working with a container query rather than, say, a media query (i.e., @media).
  • The my-container part in there refers to the container’s name, as declared in the container’s container-name property.
  • The article element represents an item in the container, whether it’s a direct child of the container or a further ancestor. Either way, the element must be in the container and it will get styles applied to it when the queried condition is matched.

Some Possible Gotchas

Container Queries Properties & Values

Container Queries Properties & Values

container-name

container-name: none | <custom-ident>+;
Value Descriptions
  • none: The element does not have a container name. This is true by default, so you will likely never use this value, as its purpose is purely to set the property’s default behavior.
  • <custom-ident>: This is the name of the container, which can be anything, except for words that are reserved for other functions, including defaultnoneatno, and or. Note that the names are not wrapped in quotes.
  • Initial value: none
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: none or an ordered list of identifiers
  • Canonical order: Per grammar
  • Animation: Not animatable

container-type

container-type: normal | size | inline-size;
Value Descriptions
  • normal: This indicates that the element is a container that can be queried by its styles rather than size. All elements are technically containers by default, so we don’t even need to explicitly assign a container-type to define a style container.
  • size: This is if we want to query a container by its size, whether we’re talking about the inline or block direction.
  • inline-size: This allows us to query a container by its inline size, which is equivalent to width in a standard horizontal writing mode. This is perhaps the most commonly used value, as we can establish responsive designs based on element size rather than the size of the viewport as we would normally do with media queries.
  • Initial value: normal
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified by keyword
  • Canonical order: Per grammar
  • Animation: Not animatable

container

container: <'container-name'> [ / <'container-type'> ]?
Value Definitons

If <'container-type'> is omitted, it is reset to its initial value of normalwhich defines a style container instead of a size container. In other words, all elements are style containers by default, unless we explicitly set the container-type property value to either size or inline-size which allows us to query a container’s size dimensions.

  • Initial value: none / normal
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified
  • Canonical order: Per grammar
  • Animation: Not animatable

Container Length Units

Container Width & Height Units

UnitNameEquivalent to…
cqwContainer query width1% of the queried container’s width
cqhContainer query height1% of the queried container’s height

Container Logical Directions

UnitNameEquivalent to…
cqiContainer query inline size1% of the queried container’s inline size, which is its width in a horizontal writing mode.
cqbContainer query block size1% of the queried container’s inline size, which is its height in a horizontal writing mode.

Container Minimum & Maximum Lengths

UnitNameEquivalent to…
cqminContainer query minimum sizeThe value of cqi or cqb, whichever is smaller.
cqmaxContainer query maximum sizeThe value of cqi or cqb, whichever is larger.

Container Style Queries

Container Style Queries is another piece of the CSS Container Queries puzzle. Instead of querying a container by its size or inline-size, we can query a container’s CSS styles. And when the container’s styles meet the queried condition, we can apply styles to other elements. This is the sort of “conditional” styling we’ve wanted on the web for a long time: If these styles match over here, then apply these other styles over there.

CSS Container Style Queries are only available as an experimental feature in modern web browsers at the time of this writing, and even then, style queries are only capable of evaluating CSS custom properties (i.e., variables).

Browser Support

The feature is still considered experimental at the time of this writing and is not supported by any browser, unless enabled through feature flags.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
130NoNo127TP

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
127No12718.0

Registering a Style Container

article {
  container-name: card;
}

That’s really it! Actually, we don’t even need the container-name property unless we need to target it specifically. Otherwise, we can skip registering a container altogether.

And if you’re wondering why there’s no container-type declaration, that’s because all elements are already considered containers. It’s a lot like how all elements are position: relative by default; there’s no need to declare it. The only reason we would declare a container-type is if we want a CSS Container Size Query instead of a CSS Container Style Query.

So, really, there is no need to register a container style query because all elements are already style containers right out of the box! The only reason we’d declare container-name, then, is simply to help select a specific container by name when writing a style query.

Using a Style Container Query

@container style(--bg-color: #000) {
  p { color: #fff; }
}

In this example, we’re querying any matching container (because all elements are style containers by default).

Notice how the syntax it’s a lot like a traditional media query? The biggest difference is that we are writing @container instead of @media. The other difference is that we’re calling a style() function that holds the matching style condition. This way, a style query is differentiated from a size query, although there is no corresponding size() function.

In this instance, we’re checking if a certain custom property named --bg-color is set to black (#000). If the variable’s value matches that condition, then we’re setting paragraph (p) text color to white (#fff).

Custom Properties & Variables

.card-wrapper {
  --bg-color: #000;
}
.card {
  @container style(--bg-color: #000) {
    /* Custom CSS */
  }
}

Nesting Style Queries

@container style(--featured: true) {
  article {
    grid-column: 1 / -1;
  }
  @container style(--theme: dark) {
    article {
      --bg-color: #000;
      --text: #fff;
    }
  }
}

Specification

CSS Container Queries are defined in the CSS Containment Module Level 3 specification, which is currently in Editor’s Draft status at the time of this writing.

Browser Support

Browser support for CSS Container Size Queries is great. It’s just style queries that are lacking support at the time of this writing.

  • Chrome 105 shipped on August 30, 2022, with support.
  • Safari 16 shipped on September 12, 2022, with support.
  • Firefox 110 shipped on February 14, 2023, with support.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

ChromeFirefoxIEEdgeSafari
106110No10616.0

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712716.0

Demos!

Many, many examples on the web demonstrate how container queries work. The following examples are not unique in that regard in that they illustrate the general concept of applying styles when a container element meets a certain condition.

You will find plenty more examples listed in the References at the end of this guide, but check out Ahmad Shadeed’s Container Queries Lab for the most complete set of examples because it also serves as a collection of clever container query use cases.

Card Component

In this example, a “card” component changes its layout based on the amount of available space in its container.

Call to Action Panel

This example is a lot like those little panels for signing up for an email newsletter. Notice how the layout changes three times according to how much available space is in the container. This is what makes CSS Container Queries so powerful: you can quite literally drop this panel into any project and the layout will respond as it should, as it’s based on the space it is in rather than the size of the browser’s viewport.

Stepper Component

This component displays a series of “steps” much like a timeline. In wider containers, the stepper displays steps horizontally. But if the container becomes small enough, the stepper shifts things around so that the steps are vertically stacked.

Icon Button

Sometimes we like to decorate buttons with an icon to accentuate the button’s label with a little more meaning and context. And sometimes we don’t know just how wide that button will be in any given context, which makes it tough to know when exactly to hide the icon or re-arrange the button’s styles when space becomes limited. In this example, an icon is displayed to the right edge of the button as long as there’s room to fit it beside the button label. If room runs out, the button becomes a square tile that stacks the icons above the label. Notice how the border-radius is set in container query units, 4cqi, which is equal to 4% of the container’s inline-size (i.e. width) and results in rounder edges as the button grows in size.

Pagination

Pagination is a great example of a component that benefits from CSS Container Queries because, depending on the amount of space we have, we can choose to display links to individual pages, or hide them in favor of only two buttons, one to paginate to older content and one to paginate to newer content.

Articles & Tutorials

General Information

Container Size Query Tutorials

Container Style Queries

References


CSS Container Queries originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-container-queries/feed/ 3 378111
CSS Length Units https://css-tricks.com/css-length-units/ https://css-tricks.com/css-length-units/#comments Mon, 03 Jun 2024 19:22:02 +0000 https://css-tricks.com/?p=375815 A comprehensive guide covering nine types of lengths that CSS uses to size elements in terms of dimensions, space, time, and even sound.


CSS Length Units originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

Overview

Many CSS properties accept numbers as values. Sometimes those are whole numbers. Sometimes they’re decimals and fractions. Other times, they’re percentages. Whatever they are, the unit that follows a number determines the number’s computed length. And by “length” we mean any sort of distance that can be described as a number, such as the physical dimensions of an element, a measure of time, geometric angles… all kinds of things!

At the time of this writing, the CSS Values and Units Module Level 4 specification defines a bunch of different CSS units — and many of those are relatively new (this pun will make sense later).

Quick Reference

Absolute units
UnitName
cmCentimeters
mmMillimeters
QQuarter-millimeters
inInches
pcPicas
ptPoints
pxPixels
Font units
UnitRelative to…
emThe font size of the element, or its nearest parent container
exThe 0-height of the element’s font 
capThe cap height (the nominal height of capital letters) of the element’s font 
chThe width of the 0 character of the font in use
icThe average width of a full glyph of the font in use, as represented by the “水” (U+6C34) glyph
remThe font-size value that’s set on the root (html) element
lhThe line-height value that’s set on the element 
rlhThe line-height that’s set on the root (html) element 
vw1% of viewport’s width
vh1% of viewport’s height
vi1% of viewport’s size in the root element’s inline axis
vb1% of viewport’s size in the root element’s block axis
vminEqual to 1% of the vw or vh, whichever is smaller
vmaxEqual to 1% of the vw or vh, whichever is larger
Viewport units
vw1% of viewport’s width
vh1% of viewport’s height
vi1% of viewport’s size in the root element’s inline axis
vb1% of viewport’s size in the root element’s block axis
vmin1% of the vw or vh, whichever is smaller
vmax1% of the vw or vh, whichever is larger
Container units
UnitRelative to
cqw1% of a query container’s width
cqh1% of a query container’s height
cqi1% of a query container’s inline size
cqb1% of a query container’s block size
cqminThe smaller value of cqi or cqb
cqmaxThe larger value of cqi or cqb
Angle units
UnitDescription
degThere are 360 degrees in a full circle.
gradThere are 400 gradians in a full circle.
radThere are 2π radians in a full circle.
turnThere is 1 turn in a full circle.
Time units
UnitDescription
sThere are 60 seconds in a minute, but there is no unit for minutes.
msThere are 1,000 milliseconds in a second.
Fractional units
UnitDescription
frOne fraction of the free space in a grid container.
Resolution units
UnitDescription
dpiDots per inch
dpcmDots per centimeter
dppxxDots per pixel unit
Frequency units
UnitDescription
HzRepresents the number of occurrences per second
kHzOne kiloHertz is equal to 1000 Hertz.

Introduction

You’re going to see a lot numbers in CSS. Here are a few examples?

/* Integers */
1

/* Pixels */
14px

/* em */
1.5em

/* rem */
3rem

/* Percentage */
50%

/* Characters */
650ch

/* Viewport units */
100vw
80vh
50dvh

/* Container units */
100cqi
50cqb

While these all mean different things, they essentially do the same thing: define an element’s dimensions in CSS. We need units in CSS because they determine how to size elements on a page, whether it’s the height of a box, the width of an image, the font-size of a heading, the margin between two elements, how long an animation runs, etc. Without them, the browser would have no way of knowing how to apply numbers to an element.

So, what the heck is px? What’s up with this thing called rem? How are these different than other length units? The unit defines what type of number we’re dealing with, and each one does something different, giving us lots of ways to size things in CSS.


Types of numbers

You may think a number is just a number, and you’re not wrong. Numbers are numbers! But we can distinguish between a few different types of numbers, which is helpful context for discussing the different types of units we attach them to since “number” can mean, well, a number of different things.

  • Integers (literally a unit-less number, e.g. 3)
  • Numbers (same as an integer, only measured in decimals, e.g. 3.2)
  • Dimensions (either a number or integer with a unit, e.g. 3.2rem)
  • Ratios (the quotient between two divided numbers, e.g. 3/2)
  • Percentages (e.g. 3%)

Got that? They’re all numbers but with nuances that make them ever-so-slightly different.

From here, we can think of numbers in CSS as falling into two specific types of unitsabsolute and relative. Let’s start things off our deep dive on CSS length units by breaking those down.


Absolute units

An absolute unit is like Bill Murray in the movie Groundhog Day: it’s always the same. In other words, whatever the number is, that’s exactly how it computes in the browser regardless of how other elements are sized.

The most common absolute value you’ll see is the pixel value. It’s sort of hard to define, but a pixel is the smallest building block of a graphical display, like a computer screen. And it’s based on the resolution of the screen. So, if you’re on a super high-resolution screen, a pixel will be smaller than it would be on a low-resolution screen, as the resolution can pack more pixels into a smaller amount of space for higher clarity. But look at the example below. All of the boxes are sized with pixels, so you can get a sense of how large 50px is compared to 250px.

Absolute values are nice in that they are predictable. That could, however, change in some situations, particularly when it comes to zooming. If, say, a user zooms into a page using browser settings, then anything defined with an absolute value is going to increase its absolute size accordingly. So, if the font-size of a paragraph is set to 20px, the paragraph is going to be larger as the user zooms closer into the page. And because zooming is often used to make content more readable, using absolute values that retain their sizing could be a good approach for making pages more accessible by allowing users to zoom things up to a spot that more comfortable to read.

But then again, see Josh Collinsworth’s click-baity, but fantastic, post titled “Why you should never use px to set font-size in CSS” for an exhaustive explanation of how pixels behave when used to set the font-size of an element. It’s a great read to better understand the behavior and limitations of pixel units.

And, hey: pixels are only one of many types of absolute lengths that are available in CSS. In fact, we can group them by the types of things they measure:

Length units

Length units are a little funny because they can technically be either an absolute unit or a relative unit. But we’re discussing them in absolute terms at the moment and will revisit them when we get further along to relative length units.

A length is essentially a dimension, which is any integer proceeded by a unit, according to the list of types of numbers we looked at earlier. And when we talk about dimensions, we’re really talking about the physical size of an element.

UnitName
cmCentimeters
mmMillimeters
QQuarter-millimeters
inInches
pcPicas
ptPoints
pxPixels

What we’re looking at here are the types of units you might use see on a tape measure (e.g., cm and in) or in print design (e.g. pc and pt). They are what they are and what you see is what you get.

Angle units

Angle units are purely geometric. They’re good for setting shape dimensions — like a circle’s radius, setting the direction of a linear-gradient(), or setting the how much we want to rotate() something.

UnitNameDescriptionExample
degDegreesA full circle is equal to 360deg.rotate(180deg)
gradGradiensA full circle is equal to 400grad.rotate(200grad)
radRadiensA full circle is equal to (i.e., 2 × 3.14), or about 6.2832rad.rotate(3.14rad)
turnTurnsA full circle is 1turn, like a bicycle wheel making one full rotation.rotate(.5turn)
Time units

Time units are what you’d expect to find on a clock or watch, but only measure in seconds and milliseconds. Apparently the web cannot be measured in minutes, hours, days, weeks, months, or years. Perhaps we’ll get a new category of “calendar units” at some point, or maybe there’s no good use case for that sort of thing. 🤷‍♂️

UnitNameDescriptionExample
sSecondsOne full minute of time is equal to 60s.animation-duration: 2s
msMillisecondsOne full second of time os equal to 1000ms.animation-duration: 2000ms
Frequency units

You won’t see frequency units used very often and for good reason: they’re not supported by any browser at the time of this writing. But they’re specced to change sound frequency, such as a sound’s pitch. The best I can make of it as it currently stands is that frequencies can be used to manipulate an audio file with a higher or lower pitch measured in hertz and kilohertz.

UnitNameDescriptionExample
HzHertzMeasures the number of frequencies per second<source src="tubthumping.mp3" type="audio/mpeg" frequency="100Hz">
kHzKilohertzA value of 1Hz is equal to 0.001kHz.<source src="tubthumping.mp3" type="audio/mpeg" frequency="0.1kHz">

If you’re wondering what constitutes a “low” pitch from a “high” one, the spec explains it like this:

[W]hen representing sound pitches, 200Hz (or 200hz) is a bass sound, and 6kHz (or 6khz) is a treble sound.

Resolution units

Resolution is how many little dots are packed into a screen — such as the screen you’re looking at right now — where more dots per inch of space improves the clarity and quality of the display. The fewer dots there are, the more pixelated and blurry the display.

Why would you need something like this? Well, it’s great for targeting styles to specific screens that support certain resolutions in a media query.

img {
  max-width: 500px;
}

/* Double the resolution and above */
@media (min-resolution >= 2dppx) {
  img {
    max-width: 100%;
  }
}
UnitNameDescriptionExample
dpiDots per inchThe number of dots packed into one inch of space.@media
(min-resolution: 96dpi) {}
dpcmDots per centimeterThe number of dots packed into one centimeter of space.@media
(min-resolution: 960dpcm) {}
dppx (or x)Dots per pixelThe number of dots packed into one pixel of space.@media
(min-resolution: 1dppx) {}

Interestingly, the specification makes mention of an infinite value that is supported by resolution media queries for targeting screens without resolution constraints. It’s not so much of a “catch-all” value for targeting any sort of screen, but for cases when we’re using the media query range syntax to evaluate whether a certain value is greater than, less than, or equal to it:

For output mediums that have no physical constraints on resolution (such as outputting to vector graphics), this feature must match the infinite value. For the purpose of evaluating this media feature in the range contextinfinite must be treated as larger than any possible <resolution>. (That is, a query like (resolution > 1000dpi)will be true for an infinite media.)

W3C Media Queries Level 4 specification

Relative units

relative unit is extremely well-named because whatever value we use for a relative unit depends on the size of something else. Say we have an HTML element, a <div>, and we give it an absolute height value (not a relative one) of 200px.

<div class="box">
  I am 200 pixels tall
</div>
.box {
  height: 200px;
}

That height will never change. The .box element will be 200px tall no matter what. But let’s say we give the element a relative width (not an absolute one) of 50%.

<div class="box">
  I am 200 pixels tall and 50% wide
</div>
.box {
  height: 200px;
  width: 50%;
}

What happens to our box? It takes up 50%, or half, of the available space on the screen.

See that? Go ahead and open that demo in a new window and change the width of the screen. And notice, too, how the height never changes because it’s an absolute length unit in pixels. The width, meanwhile, is fluidly resized as “50% of the available space” changes with the width of the screen.

That’s what we mean when talking about computed values with relative numbers. A relative number acts sort of like a multiplier that calculates the value used to set a length based on what type of unit it is relative to. So, a value of 3rem is going to wind up becoming a different value when it is computed.

Percentages, like 50%, are only one kind of relative unit. We have many, many others. Once again, it’s helpful to break things out into separate groups to understand the differences just as we did earlier with absolute units.

Percentages

We’ve already discussed percentages in pretty good detail. What makes a percentage relative is that it computes to a number value based on the length of another element. So, an element that is given width: 25% in a container that is set to width: 1000px computes to width: 250px.

UnitNameRelative to…
%PercentThe size of the element’s parent container.
Font relative units

The em and rem units we looked at earlier are prime examples of relative units that you will see all over the place. They’re incredibly handy, as we saw, because changing the font-size value of an element’s parent or the <html> element, respectively, causes the element’s own font-size value to update in accordance with the updated value.

In other words, we do not need to directly change an element’s font-size when updating the font-size of other elements — it’s relative and scales with the change.

UnitNameRelative to…
emElementThe font-size value of the element’s parent container.
remRoot elementThe font-size value of the <html> element.
chCharacterThe width of the 0 character relative to the parent element’s font. The computed width may update when replacing one font with another, except for monospace fonts that are consistently sized.
rchRoot characterThe same thing as a ch unit except it is relative to the font of the root element, i.e. <html>.
lhLine heightThe line-height value of the element’s parent container.
rlhRoot element line heightThe line-height value of the <html> element.
capCapital letterThe height of a capital letter for a particular font relative to the parent element.
rcapRoot capital letterThe same measure as cap but relative to the root element, i.e. <html>.
icInternational characterThe width of a CJK character, or foreign glyph, e.g. from a Chinese font, relative to an element’s parent container.
ricRoot international characterThe same measure as ic but relative to the root element, i.e. <html>.
exX-heightThe height of the letter x of a particular font, or an equivalent for fonts that do not contain an x character, relative to the parent element.
rexRoot x-heightThe same measure as ex but relative to the root element, i.e. <html>.

Some of those terms will make more sense to typography nerds than others. The following diagram highlights the lines that correspond to relative font units.

Four lines drawn over a line of text illustrating the ascender height, x-height, baseline, and descender height.

So, at the expense of beating this concept into the ground, if width: 10ch computes to a certain number of pixels when using one font, it’s likely that the computed value will change if the font is swapped out with another one with either larger or smaller characters.

Viewport relative units
UnitNameRelative to…
vh / vwViewport Height / Viewport WidthThe height and width of the viewport (i.e., visible part of the screen), respectively.
vmin / vmaxViewport Minimum / Viewport MaximumThe lesser and greater of vh and vw, respectively.
lvh / lvwLarge Viewport Height / Large Viewport WidthThe height and width of the viewport when the device’s virtual keyboard or browser UI is out of view, leaving a larger amount of available space.
lvb / lviLarge Viewport Block / Large Viewport InlineThese are the logical equivalents of lvh and lvw, indicating the block and inline directions.
svh / svwSmall Viewport Height / Small Viewport WidthThe height and width of the viewport when the device’s virtual keyboard or browser UI is in view, making the amount of available space smaller.
svb / sviSmall Viewport Block / Small Viewport InlineThese are the logical equivalents of svh and svw, indicating the block and inline directions.
dvh / dvwDynamic Viewport Height / Dynamic Viewport WidthThe height and width of the viewport accounting for the available space changing if, say, the device’s virtual keyboard or browser UI is in view.
dvb / dviDynamic Viewport Block / Dynamic Viewport InlineThese are the logical equivalents of dvh and dvw, indicating the block and inline directions.
dvmin / dvmaxDynamic Viewport Minimum / Dynamic Viewport MaximumThe lesser and greater of dvh/dvb and dvw/dvi, respectively.

Ah, viewport units! When we say that something should be 100% wide, that means it takes up the full width of the contain it is in. That’s because a percentage unit is always relative to its nearest parent element. But a  viewport unit is always relative to the size of the viewport, or browser window. If an element has a viewport height of 100vh and a viewport width of 100vw, then it will be as tall and wide as the full browser window.

This can be a neat way to create something like a hero banner at the top of your website. For example, we can make a banner that is always one half (50vh) the height of the viewport making it prominent no matter how tall someone’s browser is. Change the CSS in the top-left corner of the following demo from height: 50vh to something else, like 75vh to see how the banner’s height responds.

There’s something else that’s very important to know when working with viewport units. You know how mobile phones, like iPhone or an Android device, have virtual keyboards where you type directly on the screen? That keyboard changes the size of the viewport. That means that whenever the keyboard opens, 100vh is no longer the full height of the screen but whatever space is leftover while the keyboard is open, and the layout could get super squished as a result.

That’s why we have the svh, lvh, and dvh units in addition to vh:

  • svh is equal to the “small” viewport height, which occurs when the keyboard is open.
  • lvh is equal to the “large” viewport height, which is when the keyboard is disabled and out of view.
  • dvh is a happy medium between svh and lvh in that it is “dynamic” and updates its value accordingly when the keyboard is displayed or not.
  • dvmin / dvmax is the greater ore lesser of dvh, respectively.

It’s a bit of a tightrope walk in some cases and a good reason why container queries and their units (which we’ll get to next) are becoming more popular. Check out Ahmed Shader’s article “New Viewport Units” for a comprehensive explanation about viewport units with detailed examples of the issues you may run into. You may also be interested in Sime Vidas’s “New CSS Viewport Units Do Not Solve The Classic Scrollbar Problem” for a better understanding of how viewport units compute values.

Container relative units
UnitNameEquivalent to…
cqwContainer query width1% of the queried container’s width
cqhContainer query height1% of the queried container’s height
cqiContainer query inline size1% of the queried container’s inline size, which is its width in a horizontal writing mode.
cqbContainer query block size1% of the queried container’s inline size, which is its height in a horizontal writing mode.
cqminContainer query minimum sizeThe value of cqi or cqb, whichever is smaller.
cqmaxContainer query maximum sizeThe value of cqi or cqb, whichever is larger.

Container units are designed to be used with container queries. Just as we are able to target a specific screen size with a media query, we can target the specific size of a particular element and apply styles using a container query.

We won’t do a big ol’ deep dive into container queries here. The relevant bit is that we have CSS length units that are relative to a container’s size. For example, if we were to define a container:

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

…then we’re watching that element’s inline-size — which is equivalent to width in a horizontal writing mode — and can apply styles to other elements when it reaches certain sizes.

.child-element {
  background: rebeccapurple;
  width: 100%;

  @container parent (width > 30ch) {
    .child-element {
      background: dodgeblue;
      width: 50cqi;
    }
  }
}

Try resizing the following demo. When the parent container is greater than 30ch, the child element will change backgrounds and shrink to one-half the parent container’s width, or 50cqi.


What about unit-less numbers?

Oh yeah, there are times when you’re going to see numbers in CSS that don’t have any unit at all — just a single integer or number without anything appended to it.

aspect-ratio: 2 / 1; /* Ratio */
column-count: 3; /* Specifies a number of columns */
flex-grow: 1; /* Allows the element to stretch in a flex layout */
grid-column-start: 4; /* Places the element on a specific grid line */
order: 2; /* Sets the order of elemnents in a flex or grid layout */
scale: 2; /* The elementis scaled up or down by a factor */
z-index: 100; /* Element is placed in a numbered layer for stacking */
zoom: 0.2;  /* The element zooms in or out by a factor */

This isn’t a comprehensive list of all the CSS properties that accept unit-less numeric values, but it is a solid picture of when you would use them. You’ll see that in most cases a unit-less number is an explicit detail, such as a specific column to position an element, a specific layer in a stacking context, a boolean that enables or disables a feature, or the order of elements. But note that anytime we declare “zero” as a number, we can write it with or without a prepended unit, as zero always evaluates to zero no matter what unit we’re dealing with.

border: 0; /* No border */
box-shadow: 0 0 5px #333; /* No shadow offset */
margin: 0; /* No margin */
padding: 0; /* No padding */

We can create our own custom units!

In some cases, we may want to work with a numeric value, but CSS doesn’t exactly recognize it as one. In these cases, the number is recognized as a “string” value instead, regardless of whether or not it contains alphabetical characters. That’s where we can use @property to create what’s called a “custom property” that evaluates a numeric value in a certain way.

Here’s a good example. There was a time when it was virtually impossible to animate a gradient that changes colors over time because to do so would require (1) a color function that allows us to change a color value’s hue (which we have with hsl()) and (2) being able to interpolate that hue value around the color spectrum, between a range of 0deg and 360deg.

Sounds simple enough, right? Define a variable that starts at 0 and then cycles through 360 degrees at the end of an animation. But this doesn’t work:

/* 👎 */
.element {
  --hue: 0;
  
  animation: rainbow 10s linear infinite;
  background: linear-gradient(hsl(--hue 50% 50%);
}

@keyframes rainbow {
  from { --hue: 0; }
  to { --hue: 360deg; }
}

That’s because CSS reads the variable as a string instead of a number. We have to register that variable as a custom property so that CSS aptly reads it as a numeric value.

@property --hue {
  syntax: "<number>";
  initial-value: 0;
  inherits: true;
}

There we go! Now that we’ve given CSS a hint that the --hue syntax is that of a <number>, we can animate away!

/* 👍 */
@property --hue {
  syntax: "<number>";
  initial-value: 0;
  inherits: true;
}

.element {
  --hue: 0;
  
  animation: rainbow 10s linear infinite;
  background: linear-gradient(hsl(--hue 50% 50%);
}

@keyframes rainbow {
  from { --huw: 0; }
  to { --hue: 360deg; }
}

Find a deeper explanation of this same example in “Interpolating Numeric CSS Variables” by Geoff Graham.


When to use one unit over another

This is super tricky because CSS is extremely flexible and there are no definitive or hard-and-fast rules for when to use a specific type of CSS length unit over another. In some cases, you absolutely have to use a specific unit because that’s how a certain CSS feature is specced, like using angle units to set the direction of a linear gradient:

.element {
  background: linear-gradient(
    135deg, red, blue;
  )
}

The same goes for the values we use in certain color functions, like using percentages to set the saturation and lightness levels in the hsl() function:

.element {
  background: hsl(0 100% 10%);
}

Speaking of color functions, we define alpha transparency with either an integer or number:

.element {
  background: hsl(0 100% 10% / 0.5); /* or simply .5 */
}

All that being said, many cases are going to be some degree of “it depends” but there are some instances where it makes sense to use a specific unit in your work.

Generally set font sizes in rem and em units

This way, you can set things up in a way where changing the font-size value of the <html> or a parent element updates the font sizes of their descendants.

html {
  font-size: 18px; /* Inherited by all other elements */
}

.parent {
  font-size: 1rem; /* Updates when the `html` size changes */
}

.child {
  font-size: 1em; /* Updates when the parent size changes */
}

Declare “zero” without units if you’d like

It’s not a big deal or anything, but leaving off the units shortens the code a smidge, and anytime we’re able to write shorter code it’s an opportunity for faster page performance. The impact may be negligible, but we’re able to do it since 0 always computes to 0, no matter what unit we’re working with.

Use container units for responsive design, where possible

Container queries in general are so gosh-darn great for responsive layouts because they look at the size of the container and let us apply styles to its descendants when the container is a certain size.

.parent {
  container: my-container / inline-size; /* Looks at width */
}

.child {
  display: flex;
  flex-direction: column;
  max-width: 100vh; /* 100% of the viewport */
}

/* When the container is greater than 600px wide */
@container my-container (width >= 600px) {
  .child {
    flex-direction: row;
    max-width: 50%; /* 50% of the parent elenent */
  }
}

So, if we going to size the .child element — or any of its children — it’s worth specifying sizes in relation to the container’s size with container units than, say, the viewport’s size with viewport units.

.parent {
  container: my-container / inline-size; /* Looks at width */
}

.child {
  display: flex;
  flex-direction: column;
  max-width: 100cqi; /* 100% of the container */
}

/* When the container is greater than 600px wide */
@container my-container (width >= 600px) {
  .child {
    flex-direction: row;
    max-width: 50cqi; /* 50% of the container */
  }
}

Use percentages when you’re unsure of the context

Yes, use container units for responsive design, but that only does you good if you know you are in the context of a container. It’s possible, though, that you use the same component in different places, and one of those places might not be a registered container.

In that case, go with a percentage value because percentages are relative to whatever parent element you’re in, regardless of whether or not it’s a container. This way, you can declare an element’s size as 100% to take up the full space of whatever parent element contains it.

The only word of caution is to note that we’re only basing the size on the parent. Meanwhile, container units can style any descendant in the container, no matter where it is located.

Viewport units are great for laying out containers

You may be thinking that viewport units are a bad thing since we’ve been advising against them in so many cases, like font sizing, but they are still incredibly useful, particularly when it comes to establishing a full-page layout.

I say “full-page” layout because container queries are the gold standard for sizing elements according to the space they have in their container. But if we’re working with a full page of containers, this is where viewport units can be used to establish a responsive layout at a higher level.

If the elements of individual containers look at their container for sizing information, then the sizing and placement of individual containers themselves probably ought to look at the viewport since it directly influences the amount of space on the page.

Examples

Element (em) and Relative element (rem) units

Let’s talk specifically about these two little buggers. We saw how a percentage unit calculates its size by the size of something else. em and rem units are sort of the same, but they are calculated based on the relative font size of specific elements.

Let’s start with em units and say we have an HTML element, a <div> with a .box class, and we set its font size to 20px. That means any text inside of that element is 20px.

Great, now what if we decide we need additional text in the box, and give it a relative font size of 1.5em?

See how a font size of 1.5em is larger than the 20px text? That’s because the larger text is based on the box’s font size. Behind the scenes, this is what the browser is calculating:

20px * 1.5 = 30px

So, the relative font size is multiplied by the pixel font size, which winds up being 30px.

And guess what? rem units do the exact same thing. But instead of multiplying itself by the pixel font size of the parent container, it looks at the pixel font size of the actual <html> element. By default, that is 16px.

/* This is the browser's default font size */
html {
  font-size: 16px;
}

.box {
  font-size: 1.5rem; /* 16px * 1.5 = 24px */
}

And if we change the HTML element’s font size to something else?

html {
  font-size: 18px;
}

.box {
  font-size: 1.5rem; /* 18px * 1.5 = 27px */
}
Character unit (ch)

The character unit (ch) is another is another unit relative to font size and, while it isn’t used all that often, it is amazingly great at sizing things based on the amount of content displayed in an element, because one character unit equals the width of one character of content. Here’s how I wrap my own head around it. If we have this in our HTML:

<p>The big brown dog lazily jumped over the fence.</p>

…and this CSS:

p {
  width: 10ch;
}

What we get is a paragraph that is 10 characters wide. That means the text will break after the tenth character, including spaces.

But note that the words themselves do not break. If the content is supposed to break after 10 characters, the browser will start the next line after a complete word instead of breaking the word into multiple lines, keeping everything easy to read.

If you’re wondering when you might reach for the ch unit, it’s absolutely boss at establishing line lengths that are more pleasant and legible, especially for long form content like this guide you’re reading.

Line height unit (lh)

The line-height unit (lh) looks at the line-height property value of the element’s containing (i.e., parent) element and uses that value to size things up.

.parent {
  line-height: 1.5;
}

.child {
  height: 3lh; /* 3 * 1.5 = 4.5 */
}

When would you use this? Personally, I find lh useful for setting an exact height on something based on the number of lines needed for the text. You can see this clearly demonstrated in Temani Afif’s “CSS Ribbon” effect that uses lh to establish dimensions for each row of text that makes for consistently-sized lines that adapt to whatever the parent element’s font-size value happens to be,


Absolute vs. Relative Units
Container Units
Viewport Units
Typography
Angle Units
Time Units
Resolution Units

CSS Length Units originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-length-units/feed/ 9 375815
Our Best Posts on Web Components https://css-tricks.com/our-best-posts-on-web-components/ Mon, 19 Oct 2020 21:56:43 +0000 https://css-tricks.com/?p=323764 A grouping of hand-selected posts from our site about Web Components. We've published a very useful article series from Caleb Williams, so that's here, but also sprinkled in some other informational and link posts on the subject.


Our Best Posts on Web Components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

A bit of backstory on why this page exists…

I made a fancy new Gutenberg block to insert posts into other posts, so this page is an example of using that (heavily) make a meta blog post about other blog posts. For the most part, topical pages like this are best served by tag pages. For example, our tag page for Web Components has more posts and is sortable in ways this is not. This is just a way to make a curated and hand-sorted grouping of posts, which is something we’ve done for a while with “Guide Collections”. But, we’re spinning down Guide Collections and redirecting them to tag pages and pages like this because they are easier to maintain.


Our Best Posts on Web Components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
323764