nilshendriks.com

Adaptive Button Groups

Published on:

Building button groups that adapt gracefully across various layouts is often a challenge. Media queries typically target the viewport size, which doesn’t always reflect the needs of specific components. With CSS container queries, components can adjust based on the space they actually have inside their container, not the overall screen size.

Here’s a quick POC using container queries to create a button group that adapts based on available container space.

Why Container Queries?

Media queries are great for controlling layouts globally, but they don’t handle components nested inside complex layouts very well. Container queries are different-they give more control by letting components adapt to their own container’s dimensions. This leads to more flexible, self-contained components that don’t require endless breakpoints in your CSS.

The Button Group

  1. HTML structure

The button group consists of two buttons inside a wrapper element. This extra div is needed because, as of now, container queries don’t allow direct styling of the container itself, which to me seems a bit of an odd limitation.

<div class="button-group__container">
    <div class="button-group">
        <button class="button">Cancel</button>
        <button class="button button--primary">Submit</button>
    </div>
</div>

The .button-group__container is the parent that controls the layout based on its width. It acts as the container query’s target.

  1. CSS Container Queries

Here’s where the container query does its thing. By default, the buttons are displayed in a row using flexbox, but once the container’s width drops below 640px, the buttons stack vertically.

.button-group {
    display: flex;
    gap: 8px;
}

.button {
    padding: 8px 16px;
    border: 1px solid #ccc;
    background-color: #f5f5f5;
    cursor: pointer;
}

.button--primary {
    background-color: #007bff;
    color: white;
    border: none;
}

@container (min-width: 640px) {
    .button-group {
        flex-direction: row;
    }
}

@container (max-width: 640px) {
    .button-group {
        flex-direction: column;
    }
}

The @container (min-width: 640px) rule is currently being used, though it feels somewhat arbitrary at this stage. It works, but it might need further investigation to determine a more suitable breakpoint. In this case, 640px felt like a reasonable threshold where the layout would start needing adjustment.

Use Case: Grid Layouts

The button group also works inside more complex layouts like grids. When placed inside a grid with columns of varying widths, the container query adjusts the button group based on the column size, not the overall screen width:

<div class="grid grid--columns-75perc-25perc">
    <div class="grid__item">
        <h3>Column 1</h3>
        <div class="button-group__container">
            <div class="button-group">
                <button class="button">Cancel</button>
                <button class="button button--primary">Send</button>
            </div>
        </div>
    </div>
    <div class="grid__item">
        <h3>Column 2</h3>
        <div class="button-group__container">
            <div class="button-group">
                <button class="button">Make an appointment</button>
                <button class="button button--primary">Add to cart</button>
            </div>
        </div>
    </div>
</div>

This approach keeps the button group adaptable no matter how much space is available, without adding new breakpoints or changing the grid structure.

Thoughts on Container Queries

Container queries are definitely a game-changer for component-based layouts. Instead of worrying about media queries and breakpoints tied to the viewport, components can adjust based on the space they have. However, there are a few quirks-like the inability to style the container directly, which requires adding an extra wrapper div. Also, using arbitrary values like 640px for the container width might not always be ideal, and it’s worth experimenting with more realistic breakpoints based on your use case.

Final Thoughts

This adaptive button group is just a small example of what container queries can do. With more control over how components react to their environment, it’s possible to build truly flexible, responsive layouts that don’t depend on viewport size alone. If you haven’t already, it’s worth diving into container queries and seeing how they can simplify your responsive designs.

The full code for this POC is available in the GitHub repo. Give it a try, and let’s see where container queries take us next!