Using Container Query Units Relative to an Outer Container

Ana Tudor Ana Tudor on

Recently, Matt Wilcox posted on Mastodon:

The fact you can’t specify which container for container query units is a ballache. The moment you have nested containers you’re [screwed]; because if you want the calculated gap from the row’s container; but you’re inside a nested container… tough. Your units are wrong. And you can’t just say “no; not relative to this container; relative to the named outer container!”

First off, if you’re not familiar with container queries and container query units, you can check out one of the many resources on the topic, for example this interactive guide by Ahmad Shadeed, which I believe is the most recent out of all the detailed ones I’ve seen. As always, the date of the resources used is important for web stuff, especially since these units in particular have changed their name since they were first proposed and we got an early draft of the spec.

Now, the problem at hand: let’s say we have an .inner-container inside an .outer-container – they are both made to be containers:

[class*='container'] { container-type: size }Code language: CSS (css)

We want any .inner-child of the .inner-container to be able to use length values set in container query units relative to the .outer-container (more precisely, to its content-box dimensions). The problem is, if we do something like this (a 20cqw light blue strip at the start of the gradient going towards 3 o’clock):

.inner-child {
  background: linear-gradient(90deg, #0a9396 20cqw, #0000)
}Code language: CSS (css)

… then the 20cqw value is 20% (a fifth) of the content-box width of the .inner-container. This can be seen below, where we have purple guidelines 20% of the width apart.

Screenshot illustrating how a background sized to cqw on the child of the inner container is a fifth of the inner container's width.
what 20cqw represents

But what we want is for that 20cqw value to be 20% of the content-box width of the .outer-container.

Strictly for the queries themselves, we could do something like this:

.outer-container { container: outer/ size }
.inner-container { container: inner/ size }

@container outer (min-width: 500px) {
  .inner-child { background: darkorange }
}Code language: CSS (css)

This allows us to set certain styles on the .inner-child elements based on where the width of the .outer-container (which isn’t the nearest container for .inner-child) is situated relative to the 500px threshold.

But we cannot do something like this to specify which container should be the one that the query units used on .inner-child are relative to:

.inner-child {
  /* can't do this */
  background: linear-gradient(90deg, #0a9396 outer 20cqw, #0000)
}Code language: CSS (css)

Nor can we do this:

.inner-child {
  /* can't do this either */
  --s: outer 20cqw;
  background: linear-gradient(90deg, #0a9396 var(--s), #0000)
}Code language: CSS (css)

However, we are getting closer!

What if we move the --s variable uspstream? After all, a 20cqw length value set on the .inner-container is 20% of the content-box width of its nearest container, which is the .outer-container. This would mean our code becomes:

[class*='container'] { container-type: size }

.inner-container {
  --s: 20cqw;
  background: 
    repeating-linear-gradient(45deg, #bb3e03 0 5px, #0000 0 1em) 
      0/ var(--s) no-repeat
}

.inner-child {
  background: 
    linear-gradient(90deg, #0a9396cc var(--s), #0000)
}Code language: CSS (css)

We also give the .inner-container a similar background restricted to 20cqw from the left along the x axis and make the .inner-child semi-transparent, just to check if the --s values overlap (which is what we want, --s being 20% or a fifth of the .outer-container width). However, this fails, as it can be seen below:

Screenshot. Both the inner container and its child have a background sized to 20cqw. However, the container query units are relative to the outer container only for the inner container, the container query units used on its child being still relative to the inner container (one fifth of its content-box width).
screenshot of result

For the .inner-container the 20cqw of the --s is taken to be 20% of the content-box width of its nearest container, .outer-container (dashed dark blue boundary). However, for the .inner-child, the 20cqw of the --s aren’t taken to mean the same value. Instead, they are taken to mean 20% of the .content-box width of the .inner-container (dotted dark red boundary).

Boo!

But what happens if we also register --s?

@property --s {
  syntax: '<length>';
  initial-value: 0px;
  inherits: true
}Code language: CSS (css)

Bingo, this works!

Screenshot. Both the inner container and its child have a background sized to 20cqw, the container query units being relative to the outer container.
desired result

I hope you’ve enjoyed this little trick.

Where would you use this?

Wanna learn modern CSS layout?

Frontend Masters logo

We have an in-depth course on Advanced CSS Layouts that digs into the big stuff like grid and flexbox, and then more like custom properties and calc(), using them for advanced layouts and practical solutions.

7-Day Free Trial

Leave a Reply

Your email address will not be published. Required fields are marked *

Did you know?

Frontend Masters Donates to open source projects. $363,806 contributed to date.