CSS Container 📦 Queries for Responsive and Reusable Components
Tags: css, webdev, containers, tutorial
Published: 2023-08-30 16:51:29

## Table of content
1. [Introduction](#introduction)
2. [Using a container query](#using-a-container-query)
3. [Why should I use a container query?](#why-should-i-use-a-container-query)
4. [Creating a reusable component with container query](#creating-a-reusable-component-with-container-query)
5. [Using the container query responsive unit](#using-the-container-query-responsive-unit)
6. [Conclusion](#conclusion)
7. [Resources](#resources)

## Introduction
In CSS3, we have seen the use of Media Query.
The rise of responsive and fluid design brought flexbox, grid system and then the media query.

**Media query** is a CSS at-rule that makes  the style of an element depend on the size of the viewport. It comes in very handy in developing responsive interfaces.

```
@media only screen and (max-width: 440px) {
  .main {
    flex-direction: column;
  }
}
```
A **container query** is also a CSS at-rule, but it depends on the size of a parent element. The styling of an element depends on the size of its parent container and not the viewport.

## Using a container query
The container query just recently became stable in all major browsers. So, it can be implemented in your code.
[**_Visit caniuse to see supported browsers._**](https://medium.com/r/?url=https%3A%2F%2Fcaniuse.com%2Fcss-container-queries)

> For unsupported browsers, CSS flex and grid can be used as fallbacks to get a similar effect to the container query.

```
@container card (max-width: 853px) {
  .card {
    flex-direction: column;
    width: 80%;
    margin-bottom: 10px;
  }
  .card-header {
    width: 100%;
  }
  .card-body p {
    font-size: clamp(8px, 5cqi, 18px);
  }
}
```

Container query comes with some properties.
* container-type: It is used on the container element to determine what type of container it will be. It accepts any of the following values.
 1. normal: The element is not a query container for any container size. However, it is still valid because it can be used to apply styles to the element
 2. inline-size: The query will be based on the inline dimensions of the container. Applies layout, style, and inline-size containment to the element
 3. size: The query will be based on the inline and block dimensions of the container. Applies layout, style, and size containment to the container.
```
.card-container {
  container-type: size | inline-size | normal;
}
```

* container-name: It is used to name a containment context. The name could then be used in the @container query to target a particular container.
```
.card-container {
  container-name: cardContainer;
}
```

* Container query also provides some relative units that can be used in the containment context to style a containing element.
They include:
cqi: 1% of the container's inline size
cqb: 1% of the container's block size
cqh: 1% of the container's height
cqw: 1% of the container's width
cqmax: largest between cqi and cqb
cqmin: smallest between cqi and cqb

These relative units are just like the viewport's relative units.

## Why should I use a container query?
* Create reusable components with their own styles
* Style elements based on their size and not the browser viewport
* Create fluid typography
* Use new responsive units provided in the container query

## Creating a reusable component with container query
Elements can be made reusable using the container query. An element can be used in two different containers and styled to respond to the size of those containers.

![card component used in different containers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvhzewaxvxo7fmqishq3.png)

In the image above, the card component is reusable. In the sidebar, it is vertically stacked while in the main content, it is horizontally stacked. It is responding to the size of its container.

Below are code samples that produced the interface in the image above.

```
<!-- card container sidebar component -->
    <div class="aside card-container">

      <!-- card1  -->
      <div class="card">
        <div class="card-header"></div>
        <div class="card-body">
          <p>
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas,
            hic!
          </p>
          <button>Click</button>
        </div>
      </div>

      <!-- card2 -->
      <div class="card">
        <div class="card-header"></div>
        <div class="card-body">
          <p>
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas,
            hic!
          </p>
          <button>Click</button>
        </div>
      </div>
    </div>

    <div class="container">
      <div class="header">
        Cool resorts
      </div>

      <!-- card container component -->
      <div class="main card-container">

        <!-- card1 -->
        <div class="card">
          <div class="card-header"></div>
          <div class="card-body">
            <p>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas,
              hic!
            </p>
            <button>Click</button>
          </div>
        </div>

        <!-- card2 -->
        <div class="card">
          <div class="card-header"></div>
          <div class="card-body">
            <p>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas,
              hic!
            </p>
            <button>Click</button>
          </div>
        </div>

        <!-- card3 -->
        <div class="card">
          <div class="card-header"></div>
          <div class="card-body">
            <p>
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas,
              hic!
            </p>
            <button>Click</button>
          </div>
        </div>

      </div>

    </div>
```


```
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

:root {
  --size: 2;
}

body {
  display: grid;
  grid-template-columns: 20% auto;
  grid-template-rows: 100vh;
}

.container {
  width: 100%;
}

.aside {
  border-right: 2px solid grey;
  height: 100%;
  padding: 10px;
}

.header {
  text-align: center;
}

.main {
  display: flex;
  gap: 20px;
  width: 100%;
  padding: 20px;
}

.card-container {
  container-type: size;
  container-name: cardContainer;
}

.card {
  width: calc(100% / var(--size));
  font-size: 16px;
  padding: 5px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  height: fit-content;
  border: 2px solid black;
}

.card-header {
  width: calc(100% / var(--size));
  height: 90px;
  background-color: black;
}

.card-body button {
  margin-top: 10px;
  border-radius: 5px;
  border: none;
  padding: 5px 10px;
  background-color: black;
  color: white;
}

@container cardContainer (max-width: 853px) {
  .card {
    flex-direction: column;
    width: 80%;
    margin-bottom: 10px;
  }
  .card-header {
    width: 100%;
  }
  .card-body p {
    font-size: clamp(8px, 5cqi, 18px);
  }
}

@media only screen and (max-width: 440px) {
  .main {
    flex-direction: column;
  }
}
```

A containment context is created **_"container-name: cardContainer "_** and the container type is defined as **_"container-type: size"_**.
The @container  is set to when the max-width of the container is 853px. When the width falls within this range, the styles in the @container at-rule take effect.

The card element is responsive to the size of the container (card-container) and not the viewport.

If the width of the container is less than or equal to 853px, the card element's flex-direction becomes a column and the width increases, the size of the card-header element also increases in width.

The same card element is used in the aside and the main element but it is styled differently because the size of the sidebar is different from the size of the main element.

## Using the container query responsive unit
In the @container at-rule the responsive unit, **_cqi_**, is used on the font size of the card body's paragraph. A **clamp** function was used to clamp whatever the unit will be between 8px and 18px. Note that the **clamp()** function is not a container query feature.
If the container's inline size grows, the font size of the paragraph increases because it is always 5% of the container's inline size. But it will never grow above 18px. Also, if the container's inline size shrinks, the font size of the paragraph decreases, but not less than 8px.

## Conclusion
The container query is a recent CSS at-rule used to query elements like the media query. However, the container query depends on the size of elements rather than the browser viewport. It was recently made stable in major browsers (Chrome/Edge 106, Safari 16, and Firefox 110).

It gives more possibility for elements to have independent stylings depending on their sizes.

## Resources
* [Mozilla Developer Network - Container query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries)
* [caniuse.com - container query](https://caniuse.com/css-container-queries)
* [web.dev - stable container query](https://web.dev/cq-stable/)