slides.oddbird.net/cq/front/

CSS Containers. What Do They Know?

@ Front Conference

2009 @media Queries

2011-2022 Selector Queries
Element Queries
Container Queries

Containers don’t know anything.

– Browsers

Container queries will never be possible on the web. They would cause infinite layout loops.

– Browsers, A Paraphrase (circa 2020)

The fact we can control a paper page is really a limitation of that medium.

– John Allsopp, 2000

@media prefers-reduced-motion

@supports container-type: inline-size

Animation of twitter-like posts appearing

We’re designing dynamic content with unknown collaborators on an infinite and unknowable canvas, across operating systems, interfaces, writing-modes, & languages

– me

Every box has An Intrinsic Size

defined by content

Some boxes have An Extrinsic Size

defined by containers/attributes/etc

🤯 Makes Queries Recursive

💥 Size » Query » Change » Resize 💥

Intrinsic Web Design

The responsive web keeps evolving…

Responsive Web Design Forces Everything Fluid

2010s – Using % for everything

Intrinsic Web Design Combines Fluid & Fixed

2020s – Using intrinsic size of elements

  1. Truly Two-Dimensional Layouts
  2. Combine Fluid & Fixed
  3. Stages of Squishiness
  4. Nested Contexts
  5. Expand & Contract Content
  6. Media Queries, As Needed
  1. Nested Contexts ???

  2. Expand & Contract Content ???

Progressive Enhancement

/* explicit fallbacks, rarely needed */
@supports not (container: name) { /* … */ }

What about the 🧐 infinite layout loops?

Well… yeah

(*some restrictions apply)

We can’t change The Container Being Queried

CSS Containment

contain: size | layout | style | paint;

Size containment Removes Intrinsic Sizing

We can only Measure The Axis We Contain

Use size containers With Overflowing Containers

contain: inline-size layout style;

container-type: inline-size;
.element-to-query {
container-type: inline-size; /* or size */
}
@media (min-width: 40em) {
.card { /* … */ }
h2 { /* ... */ }
}
@container (min-width: 40em) {
.card { /* … */ }
h2 { /* ... */ }
}
@container (width > 40em) {
.card { /* … */ }
h2 { /* ... */ }
}
@container (inline-size > 40em) {
.card { /* ... */ }
h2 { /* ... */ }
}
@container (20em > inline-size > 40em) {
.card { /* ... */ }
h2 { /* ... */ }
}

Also Recommended… Name Your Containers

main {
container-type: inline-size;
container-name: layout main;
}
main {
container: layout main / inline-size;
}

Names can be Like Classes or IDs

(shared or unique – establish conventions!)

@container layout (min-width: 40em) {
.conditional { /* … */ }
}

@container main (min-width: 40em) {
.conditional { /* … */ }
}

Finding Containers

  1. For each matched element
  2. Find the nearest ancestor that has…
    • Any required container name
    • Any required container types

Containers Can’t Self-Query

(That would introduce loops!)

Always Measuring an Ancestor

(can’t change what you measure!)

Bonus! Container Queries Measure Actual Styles

Bonus! Computed Values On the Container Element

Grid Tracks & Flex Sizing?

No element to measure…

For legacy reasons… No Default Containers

/* extrinsic size, from the viewport */
html, body { block-size: 100%; }
/* root container */
html { container: root / size; }
/* body as scroller */
body { overflow: auto; }

also… Container Query Units

cqw | cqh | cqi | cqb | cqmin | cqmax

Default unit container The Small Viewport

Style Queries

@container style(--colors: invert) {}

Style Queries… Only Custom Properties*

* for now

Style Queries… Only Custom Properties*

* for now **
** but maybe forever?

Always Queries Direct Parent

Unless you query a specific container-name

???

@container state(stuck) {}
@container state(snapped) {}
@container state(overflowing) {}

Things Containers Know:

  1. Their Size (if contained)
  2. Custom Property Values
  3. Relative Values (like em)
  4. Maybe Some States?? (tbd)