DEV Community

Cover image for let's learn container queries and how to do them in tailwind.
Akashdeep Patra
Akashdeep Patra

Posted on

let's learn container queries and how to do them in tailwind.

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

Desktop Card

And like this in the case of mobile screens

Mobile Card

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

Desktop Layout

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Messed up layout

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

@tailwindcss/container-queries

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;

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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

Container demo

this gives you the ability to make your components aware of the layout which is insane

Support doc

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

Oldest comments (0)