DEV Community

Cover image for CSS Container 📦 Queries for Responsive and Reusable Components
Azubuike Chibueze Golden
Azubuike Chibueze Golden

Posted on

CSS Container 📦 Queries for Responsive and Reusable Components

Table of content

  1. Introduction
  2. Using a container query
  3. Why should I use a container query?
  4. Creating a reusable component with container query
  5. Using the container query responsive unit
  6. Conclusion
  7. 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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

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>
Enter fullscreen mode Exit fullscreen mode
* {
  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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)