Responsive Components: Present & Future

Media Queries:
Why We Want More

Media Queries change CSS based on browser

  • Screen dimensions (height, width)
  • Screen resolution / pixel density
  • Device orientation / rotation
  • User preferences (contrast, motion)

But, what about the size of the element itself?

First, figure out what size the element will be in a given responsive layout.

Then use multiple MQs to toggle CSS changes on & off.

.widget {
/* narrow 1col layout: narrow version CSS */
}
@media (min-width: 480px) and (max-width: 959px) {
.widget {
/* wide 1col layout: wide version CSS */
}
}
@media (min-width: 960px) and (max-width: 1199px) {
.widget {
/* narrow 2col layout: narrow version CSS */
}
}
@media (min-width: 1200px) {
.widget {
/* wide 2col layout: wide version CSS */
}
}
<main class="page-content">
<div class="widget"></div>
</main>

<aside class="page-sidebar">
<div class="widget"></div>
</aside>

Current Techniques

CSS

Flexbox Wrapping

  • Uses flex-wrap & flex-basis adjust layout depending on container size
  • Source: Geoffrey Crofte

Holy Albatross

  • Uses flex-basis & calc() to toggle between column & row layouts depending on container size
  • Source: Heydon Pickering (original, update)
.parent {
--container-width: 720px;

display: flex;
flex-wrap: wrap;
}

.child {
flex-grow: 1;
flex-basis: calc((var(--container-width) - 100%) * 999);
}

CSS Grid

.grid {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(200px, 1fr));
}

Clamping for font-size, etc

  • Use CSS clamp() function (optionally with min() & max()) to create responsive font-size, line-height, etc.
  • Relies on viewport size, not component
  • Source: Pedro Rodgriguez, David Atanda

JS

ResizeObserver

  • Use a JS API to watch an element’s size and add/remove classes as needed
  • Source: MDN
const ro = new ResizeObserver(entries => {
for (let e of entries) {
const size = Array.isArray(e.borderBoxSize)
? e.borderBoxSize[0]
: e.borderBoxSize;
const action = (size.inlineSize >= 480)
? 'add'
: 'remove';
e.target.classList[action]('wide');
}
});
ro.observe(document.getElementById('box'));

Resize Observer is supported in all modern browsers (but not IE & pre-Chromium Edge), covering 92.2% of global usage.

ResizeObserver polyfill

Avoid Memory Leaks

ro.observe(el)

ro.unobserve(el)

Future: Container Queries

What about Element Queries?

But Loops…

.element {
width: 400px;
}

.element:query(min-width: 300px) {
width: 200px;
}

Solution:
Container Queries

1. Create a Container

.page-content,
.page-sidebar
{
container: size layout style;
container-type: inline-size;
}

Disables content-based sizing

* Spec still in WIP

2. Style Children

.widget {
/* narrow container: narrow version CSS */
}
@container (min-width: 480px) {
.widget {
/* wide container: wide version CSS */
}
}

2. Style Children

Each .widget queries its potential containers.

3. Name Containers (Optional)

.page-content {
container-name: main;
}
.page-content__gallery {
container-name: gallery;
}
@container main (min-width: 480px) {
.widget { /* wide `main` container */ }
}
@container gallery (min-width: 480px) {
.widget { /* wide `gallery` container */ }
}

Browser Support

Chrome ... Canary ... behind a flag

Can I use...

Polyfill

Jonathan Neal’s cqfill

More Info

What Can I Do?

  • Use the polyfill
  • Follow the csswg spec & join the discussion
  • Upvote browser implementation trackers as available

Thanks, Y’all!

James Steinbach

Senior Engineer
at auth0

jdsteinbach

Twitter | GitHub | Blog