Building Responsive React Components Using @container and : has()

The Modern Way of Building Responsive Components

Manusha Chethiyawardhana
Bits and Pieces

--

Building Responsive React Components Using @container and : has()

As developers, we ensure that our web application looks good and is accessible on all devices. UI/UX designers frequently develop many creative, responsive component designs to please clients and users. However, these designs can sometimes be challenging to implement.

Fortunately, with growing browser and styling capabilities, it is becoming easier to develop better responsive components. Container queries and the :has() parent selector are two such significant new additions to the styling world. These two CSS features are now making their way into the browsers. They are currently supported by the latest version of Google Chrome and the Safari technology preview.

This article will discuss how to use these two new CSS additions to develop responsive components in React.

@container — A Query for Responsive Containers

Media queries are currently the preferred method for most developers to create responsive components. Nevertheless, media queries can only change an element’s size based on the device viewport size. Then, what if you want to define responsive sizes based on the size of the container it is placed in? The solution is the container queries.

Cards, form fields, and navigation bars are common design elements that change layout based on the width of their container. You can improve the reusability of these components and save a lot of development time by defining container queries for them.

Defining Container Queries

Before defining container queries, you must first set the container-type property on the parent container. Possible values for container-type are size, inline-size, block-size, style, and state.

Let’s say you have a component with elements arranged in a single horizontal line. For example, consider a typical card with an image and text. Then, we can create a React component for the card, as shown below.

export default function Card(props) {
const {text, imgHeight, imgSrc, alt} = props;
return (
<div className="card">
<div>
<img height={imgHeight} src={imgSrc} alt={alt}/>
</div>
<div>
{text}
</div>
</div>
);
}

This card component can be placed on various containers of varying widths. Let’s set the container-type to inline-size to change the component's styles based on the container's width. This process creates a query container for dimensional queries on your container's inline axis. For example, if the class name of your container is card-container, the styling is as follows.

.card-container {
container-type: inline-size;
}

Following that, you can use @container to define the styles for the card components. Take a look at the example below.

@container (max-width: 600px) {
.card {
flex-direction: column;
gap: 12px;
}
}
React Container Query Component Example
Source: https://codesandbox.io/s/container-query-r9hfei?file=/src/styles.css

As shown in the example, when the container width is reduced, the styles of the card component change in response to the container query. To test the responsive components, you can use the CSS resize property. For instance, setting resize: horizontal; and overflow: auto; in the container of the preceding example allows the width to be changed.

You can also use the container-name property to target a specific container.

.card-container {
container-name: card-container;
}

@container card-container (max-width: 600px) {
.card {
flex-direction: column;
gap: 12px;
}
}

These card component styles will now only be added within the card-container container. Visit this link to see the complete example code.

:has() — The Relational (Parent) Selector

In CSS, selecting the parent of an element based on its children is now possible. :has() is a CSS pseudo-class that accepts one or many elements as arguments. It is also known as the parent selector, but it is much more.

The basic syntax for :has() is shown below.

parent:has(child) {
// custom styles for parent
}

Using :has() in an Application

An application, for instance, may include card components with or without titles. Let’s improve our previous card component by having a title in these conditions. We can add a <h4> element to the code of our card component.

export default function Card(props) {
const {text, imgHeight, imgSrc, alt, title} = props;
return (
<div className="card">
<div>
<img height={imgHeight} src={imgSrc} alt={alt}/>
</div>
<div>
{title && <h4>{title}</h4>}
<div>
 {text}
</div>
</div>
</div>
);
}

You can now use the :has() selector to define different styles for the card component with the title.

.card:has(h4) {
align-items: start;
}

The align-items: start would only be applied to cards with the <h4> element as a child. Assume we also want to define distinct styles for the image in cards with titles. You can accomplish this easily by using the :has() selector, as shown below.

.card:has(h4) img {
// custom styles for image
}

You can also chain:has() selectors and use other pseudo-classes within :has().

.card:has(>div):has(>h4) { ... }
.form-group:has(:focus) { ... }

Note : The :has() selector cannot be nested; :has() is not valid within :has(). And pseudo-elements are not allowed within :has().

Using :has() with Container Queries

You can also use the :has() selector within container queries. We can improve the responsive styles even further by combining :has() with our card component's container query.

Consider our card example and add the code below to center the image in the card with the title when the container width is less than 600 pixels.

@container (max-width: 600px) {
.card:has(h4) {
align-items: center;
}
}
React Container Query with :has() Example
Source: https://codesandbox.io/s/container-query-with-has-j34kfi?file=/src/styles.css

As shown in the preceding example, we can define separate responsive styles for the same React component by combining :has() and @container. You can apply the same principles to create more responsive React components.

Conclusion

The days of writing media queries may be numbered. Container queries will eventually take over the role of creating responsive components due to their added benefits. Another extremely useful new CSS pseudo-class is the :has() selector. You can combine it with various other selectors to create novel styles. We can easily develop fully responsive applications by combining these two CSS features with React's ability to create modular components.

I hope all browsers will soon support container queries and the :has() pseudo-class. Then hopefully, this article will assist you in overcoming some of the challenges you may face when developing responsive components.

Thank you for reading, and happy coding!

Build composable web applications

Don’t build web monoliths. Use Bit to create and compose decoupled software components — in your favorite frameworks like React or Node. Build scalable and modular applications with a powerful and enjoyable dev experience.

Bring your team to Bit Cloud to host and collaborate on components together, and speed up, scale, and standardize development as a team. Try composable frontends with a Design System or Micro Frontends, or explore the composable backend with serverside components.

Give it a try →

Bit

--

--