Before we get started let's try to visualize the problem for which container queries were created in the first place.
For the sake of simplicity let's assume we're trying to create a Responsive Card component for our design system which will change the world in some way or end world hunger or something, I don't know ,pick your thing (lol).
This card component looks something like this on wide screens
And like this in the case of mobile screens
And the code for this is pretty straightforward, we write some media queries, and based on the screen width we just set the flex-direction from column to row (in the mobile-first approach ).
But here's the catch, one massive assumption we take in case of responsive designs is that we think in terms of components but we write the code based on screen sizes which aren't really how they work in production-grade applications right?
Your UI doesn't care about the screen size it only cares about the layout (How it looks in your component tree ).
let's consider this layout for a minute
To recreate this I'm using React with tailwind but the technology could be used with any framework of your choice.
The code for the Card component is something like
import React from 'react';
const Card = () => {
return (
<div className=' shadow-lg max-w-[800px] flex flex-col md:flex-row overflow-hidden bg-white border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700'>
<a href='#' className=''>
<img
className='rounded-t-lg'
src='https://flowbite.com/docs/images/blog/image-1.jpg'
alt=''
/>
</a>
<div className='p-5'>
<a href='#'>
<h5 className='mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white'>
Noteworthy technology acquisitions 2021
</h5>
</a>
<p className='mb-3 font-normal text-gray-700 dark:text-gray-400'>
Here are the biggest enterprise technology acquisitions of 2021 so
far, in reverse chronological order.
</p>
<a
href='#'
className='inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800'
>
Read more
<svg
aria-hidden='true'
className='w-4 h-4 ml-2 -mr-1'
fill='currentColor'
viewBox='0 0 20 20'
xmlns='http://www.w3.org/2000/svg'
>
<path
fill-rule='evenodd'
d='M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z'
clip-rule='evenodd'
></path>
</svg>
</a>
</div>
</div>
);
};
export default Card;
And the code for the layout is like
import Card from './components/Card/Card';
function App() {
return (
<div className='App p-10 flex flex-col gap-5 justify-center'>
<Card />
<div className='gap-5 max-w-[100vw] flex flex-col'>
<div className='flex flex-col md:flex-row gap-5'>
<Card />
<Card />
</div>
<div className='flex flex-col md:flex-row gap-5'>
<Card />
<Card />
</div>
</div>
</div>
);
}
export default App;
Now let's try to shrink the viewport very slowly (Like really slow)
If you're not cheating! at some point, your layout would look something like
And that's just ugly .......!
And the reason is although your Card is responsive it isn't aware of its surroundings and where it's being used, And that is the very reason why Container queries were introduced.
This is based on the css containment specs,
which says and I quote
CSS containment provides a way to isolate parts of a page and declare to the browser these parts are independent of the rest of the page in terms of styles and layout.
And for this example we also have a surprise, tailwind's latest version now supports container queries with a new plugin called
Let's try to fix our code by segregating the layout into different containers
So our app's code is now like
import Card from './components/Card/Card';
function ContainerApp() {
return (
<div className='App p-10 flex flex-col gap-5 justify-center'>
<div className='@container/main'>
<Card />
</div>
<div className='gap-5 max-w-[100vw] flex flex-col'>
<div className='flex @container/grid flex-col md:flex-row gap-5'>
<Card />
<Card />
</div>
<div className='flex @container/grid flex-col md:flex-row gap-5'>
<Card />
<Card />
</div>
</div>
</div>
);
}
export default ContainerApp;
If you carefully notice we added two different containers having names main and grid, which will point to two different types of cards.
And yes tailwind supports named containers as well !!!
So our modified version of the card component is now
import React from 'react';
const ContainerCard = () => {
return (
<div className=' shadow-lg max-w-[800px] flex flex-col @[620px]/main:flex-row @[1224px]/grid:flex-row overflow-hidden bg-white border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700'>
<a href='#' className=''>
<img
className='rounded-t-lg'
src='https://flowbite.com/docs/images/blog/image-1.jpg'
alt=''
/>
</a>
<div className='p-5'>
<a href='#'>
<h5 className='mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white'>
Noteworthy technology acquisitions 2021
</h5>
</a>
<p className='mb-3 font-normal text-gray-700 dark:text-gray-400'>
Here are the biggest enterprise technology acquisitions of 2021 so
far, in reverse chronological order.
</p>
<a
href='#'
className='inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800'
>
Read more
<svg
aria-hidden='true'
className='w-4 h-4 ml-2 -mr-1'
fill='currentColor'
viewBox='0 0 20 20'
xmlns='http://www.w3.org/2000/svg'
>
<path
fill-rule='evenodd'
d='M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z'
clip-rule='evenodd'
></path>
</svg>
</a>
</div>
</div>
);
};
export default ContainerCard;
As you can see at the top level we just added the classNames
@[620px]/main:flex-row : which says for Cards that are defined inside the main container , if the top level main container's width is more than 620px , the flex-direction would change to row
@[1224px]/grid:flex-row : which says for Cards that are defined inside the grid container, if the top-level grid container's width is more than 1224px, the flex-direction would change to row
and the two sets of styles are completely independent of each other
So after the changes, your layout should look something like this
this gives you the ability to make your components aware of the layout which is insane
The support for container queries is spreading like wildfire, it's gonna be the new age of writing Component-driven layouts, and container queries are gonna be the tool that would pave the way.
I'm including the working Stackblitz for reference
The code for this demo is at
Feel free to follow me on other platforms as well
Top comments (0)