One of the features that I have wanted to create for a long time is a card view with a list view option without using any Javascript (not my forte). Using Bricks Builder, you can use interactions and set attributes to change layouts, but that still uses Javascript under the hood.
Container Queries
The recent addition of container queries to modern browsers makes this easily possible because you can rearrange content based on the available space. I will not go in-depth on creating container queries as far better resources are available, including MDN and Kevin Powell. What I can share is that it is really simple to implement. My approach was to build everything out using Bricks and Automatic CSS as my cards, then target the styles with CSS to make the change.
%root% {
--aspect: 16/9;
display: grid;
}
/* Change the layout to a full width list */
@container layout (inline-size > 800px) {
%root% {
grid-template-columns: 5fr minmax(auto, 500px);
}
%root% .card-list__media {
--aspect: 16/9;
}
%root% .card-list__content-wrapper {
padding: var(--space-m);
justify-content: center;
}
%root% .card-list__media-wrapper {
order: 1;
}
%root% .card-list__lede {
max-width: 72ex;
}
%root% {
flex-grow: 1;
}
}
/* Change the layout to accomodate a chenge with the grid is not full */
@container layout (inline-size < 800px) {
%root% {
grid-template-columns: 2fr 1fr;
}
%root% .card-list__content-wrapper {
padding: var(--space-m);
justify-content: center;
}
%root% .card-list__media-wrapper {
order: 1;
}
%root% {
flex-grow: 1;
}
}
/* Normal grid layout */
@container layout (inline-size < 500px) {
%root% {
grid-template-columns: 1fr;
}
%root% .card-list__media-wrapper {
order: -1;
}
%root% .card-list__media {
--aspect: 16/9;
}
%root% {
flex-grow: 0;
justify-content: start;
}
}As you can see, it is fairly simple code, and what this is saying is when there is more than 600px available (random number), give me a wide card (list) layout, but less than 600px, give me a stacked card layout. I combined this with the Automatic CSS variable grid to control the min-width of a card.

Referencing the image above:
- This is where the :has() logic exists for the variable-grid –min value.
- This is the main container for the list and has variable-grid class assigned.
- The is the element where the container is defined {container: layout / inline-size;}, which is the element affected by the variable-grid class. This is the query loop.
- This is the container query’s defined container (layout in CSS above).
- The internal content gets moved around when available space is limited.
Switching Layouts
The next challenge is to give the user the ability to switch between layouts. I originally had buttons with interactions, but I hated the look and the reliance on Javascript. Enter Kevin Geary once again.
Creating a Toggle
As soon as I saw the title of the latest video from Kevn — “Create an Accessible Content Switch … From Scratch … In a Page Builder … With NO Javascript!“— I knew this was calling my name. It did not disappoint. While I don’t have a need for a pricing table, the ability to use this to toggle my display options was spot on.
After watching the video a second time and building the switch from the ground up, I was stuck trying to get the display switch to actually change the available space. It took a couple of iterations to realize that I needed to take the :has() selector a level higher. Here is the final selector code that changes the min value of the variable-grid.
%root%:has(#grid-layout:checked) .card-list__cards {
--min: calc(var(--content-width) / 5);
}
%root%:has(#list-layout:checked) .card-list__cards {
--min: var(--content-width);
}What I love about this approach is that it uses the intrinsic features of the browser without relying on Javascript.
Here is my full CSS for the toggle.
.display-buttons input[type="radio"],
.display-buttons legend {
block-size: 0;
inline-size: 0;
position: absolute;
inset-inline-start: -99999px;
}
.display-buttons {
--button-padding: .25em;
--button-label-padding: .75em 1.25em;
--border-radius: 50vw;
--label-color: var(--base-dark);
position: relative;
isolation: isolate;
border-radius: var(--border-radius);
line-height: 1;
padding: var(--button-padding);
border: 1px solid var(--base-trans-20);
}
.display-buttons::before {
position: absolute;
content: "";
inset-inline-start: var(--button-padding);
inset-block-start: 50%;
z-index: -1;
inline-size: calc(50% - var(--button-padding));
block-size: calc(100% - var(--button-padding)*2);
border-radius: var(--border-radius);
background-color: var(--accent);
translate: var(--indicator-position, 0%)-50%;
transition: translate 250ms ease-out;
}
.display-buttons:has(#grid-layout:checked)::before {
--indicator-position: 0%;
}
.display-buttons:has(#list-layout:checked)::before {
--indicator-position: 100%;
}
.display-buttons__option-label:has(:checked) {
--label-color: var(--white);
opacity: 1;
font-weight: 700;
}
.display-buttons__option-label {
padding: var(--button-label-padding);
margin-block-end: 0;
cursor: pointer;
color: var(--label-color);
opacity: var(--label-opacity, .7);
font-weight: 600;
}
Leave a Reply