How to build place-anywhere blocks with container queries

By Ian Svoboda on June 10, 2026

Container queries let your blocks and template parts adapt to wherever they’re placed, no media queries required. In this post I’ll show you three practical ways to use them, from basic queries to fluid typography with container units.

If you’ve ever built a card that looked great in the content area and then watched it fall apart the moment someone dropped it into a sidebar, you know the problem: when you build with blocks and template parts, you don’t control where things end up. That’s kind of the whole point of building with blocks. Editors can (and will) place your components anywhere1.

Media queries can’t save you here because they only know one thing: the size of the viewport. They have no idea how much space your block actually has. A card sitting in a 300px sidebar on a desktop screen gets desktop styles, even though it’s living in phone-sized real estate.

Container queries fix this. Instead of asking how big the screen is, you ask how big the space around your element is. Once you start thinking this way, your blocks and template parts become truly portable: build the component once, drop it anywhere, and it just works.

I covered the fundamentals of container queries in 4 new essential modern CSS features for WordPress Development, so I won’t repeat all of that here.

The short version is: you declare an element as a container using the container-type property (you almost always want inline-size, which queries the width), and then you write @container rules that apply styles to things inside that container.

One important rule to remember: you can’t style the container itself from its own query, only its contents.

This post is about actually doing things with container queries. We’ll build one component and use it to walk through three techniques: a standard (unnamed) container query, named containers, and container query units. By the end, you’ll have a card you can drop into any part of your site without writing a single media query.

Example: a CTA card pattern

Our running example is a simple call-to-action card: an image, a heading, a short paragraph, and a button. In block markup terms, that’s a Group block containing an Image, Heading, Paragraph, and Buttons block.

While I’m building this as a pattern, you could just make a group block and customize that block and it’s children accordingly. The outer Group gets a custom class of cta-card via the Advanced panel in the block settings sidebar.

HTML
<!-- wp:group {"className":"cta-card"} -->
<div class="wp-block-group cta-card"><!-- wp:image -->
<figure class="wp-block-image"><img src="https://picsum.photos/id/180/640/480" alt=""/></figure>
<!-- /wp:image -->

<!-- wp:group -->
<div class="wp-block-group"><!-- wp:heading -->
<h2 class="wp-block-heading">Level up your theme skills</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Get practical, modern WordPress theme development tips sent straight to your inbox.</p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons"><!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button">Get the mini-course</a></div>
<!-- /wp:button --></div>
<!-- /wp:buttons --></div>
<!-- /wp:group --></div>
<!-- /wp:group -->

Before we can query anything, we need a container. But as noted in the intro, the card can’t be its own container for size queries. You can only target a container’s contents, not the container itself. The container needs to be a parent of the card. In this case we need to make the parent block of our CTA card a container2.

You could do something like assign a container to all blocks, but this may not always be what you want. Also, if you need container behavior around a block that’s not part of that list, you’d have to update those styles to include it.

The simple meet-in-the-middle approach is to use an “opt-in” class that has to be added to something before it gets the style.

CSS
.cq-container {
	container-type: inline-size;
}

You then add the class to a block that goes around the other block you’re targeting and now that element is a container.

If you open up your dev tools and inspect one of these elements, you should see a little container pill next to it in the element tree (this is a quick way to confirm your container declaration actually took).

Why not make all blocks a container by default?

When you make an element a container, the browser needs a reliable way to calculate it’s size independently of it’s children. When the browser looks at the container element to tell how big it is, the size is checked as if the element was empty.

You can read more about this in the CSSWG Spec for the Containment Module.

Use case 1: stack and unstack with a standard container query

The classic card problem: when there’s room, the image should sit beside the text. When there isn’t, everything should stack vertically. Let’s make the stacked version the default and unstack when the card has at least 480px to work with:

CSS
.cta-card {
	display: grid;
	gap: 1.5rem;
}

@container (width >= 480px) {
	.cta-card {
		align-items: center;
		grid-template-columns: 200px 1fr;
	}
}

Notice there’s no container name in that query. When you write an unnamed @container rule, the browser finds the nearest ancestor of the matching element that’s been declared as a container and measures that. Our card in the main content area measures against .entry-content.

The same card dropped into a Column with the .cq-container class measures against the column. Same CSS, no extra work.

And that’s the payoff: drop this card into the post content and it displays horizontally. Drop it into a narrow sidebar column and it stacks. No block variations, no extra classes, no “sidebar version” of the template part. The card looks at the space it’s been given and responds.

Screenshot of the CTA card pattern displaying in different layouts based on it's parent container's size

Use case 2: named containers

The unnamed query works great when your page has one obvious container. In the real world, pages are messier. Once you’ve declared the content wrapper and columns and maybe a wrapper Group as containers, “nearest ancestor” starts being a guess instead of a decision. The card in a column responds to the column, which is usually what you want, until the day it isn’t.

Here’s a concrete scenario: say the card sits inside a Columns block, but you want its layout to respond to the page-level content width rather than the column it happens to be in (maybe you always want it horizontal on wide pages, even when it’s sharing a row). With an unnamed query you’re stuck, because the column is the nearest container and it wins automatically.

Names fix this. But in order to set a name you need to specify it along with the container type that we already set before. For a block that appears once (or only a few times) per page, setting the styles on it directly isn’t a bad idea. But to keep things fully opt-in, we’ll continue to use our cq-container class and then add some other styles to specify a name based on what element that class is added to.

CSS
.cq-container {
	container-type: inline-size;
	
	&.wp-block-post-content {
	  container-name: content;
	}
	
	&.wp-block-column {
	  container-name: column
	}
}

This adds a container name to the core/post-content or core/column block that you’ve declared as a container. Now you can be explicit about which container you’re measuring:

CSS
/* Responds to the column the card sits in */
@container column (width >= 480px) {
	.cta-card {
		grid-template-columns: 200px 1fr;
	}
}

/* Responds to the page-level content width, even inside a column */
@container content (width >= 1024px) {
	.cta-card .wp-block-heading {
		letter-spacing: -0.02em;
	}
}

When you name a container in the query, the browser skips past any closer containers until it finds an ancestor with a matching name. The column never even enters the conversation for that second rule.

My recommendation: once you have more than one container on a page, name all of them. It costs you nothing and it makes every query self-documenting (you can read @container column six months from now and know exactly what it measures).

Use case 3: fluid sizing with cqi units

You’ve probably used vw units before to scale typography with the viewport. Container query units do the same thing, except they’re relative to the container instead of the screen. The one you’ll reach for most is cqi: one cqi equals 1% of the container’s inline size, which is its width in horizontal writing modes (this pairs naturally with the inline-size container type we’ve been using all along).

This is where the card gets fun. Our heading currently has a fixed font size, which means it’s proportionally huge in the sidebar and a little lost in a full-width footer placement. One rule fixes both:

CSS
.cta-card .wp-block-heading {
	font-size: clamp(1.25rem, 4cqi + 0.5rem, 2.5rem);
}

Breaking down that clamp(): the heading never shrinks below 1.25rem, never grows beyond 2.5rem, and in between it scales with the container at 4% of its width (the + 0.5rem keeps the scaling curve from starting too small, and mixing in a rem value also means the text still responds to user font size preferences). In the sidebar you get tidy, proportional type. In a full-width footer CTA you get big, confident type. Same heading, same rule.

And cqi isn’t just for text. Anything that takes a length can scale with the container, which is how you make the whole component feel proportional rather than just the type:

CSS
.cta-card {
	gap: clamp(1rem, 2cqi, 1.5rem);
	padding: clamp(1rem, 3cqi, 2.5rem);
}

Now the card breathes more in larger spaces and tightens up in smaller ones. There are sibling units too (cqw, cqh, cqmin, cqmax), but cqi covers the vast majority of real-world cases. I’ve linked a full reference in Further Reading if you want more details on these new units.

Where this fits in your theme

A few practical notes on wiring this into an actual theme or your blocks:

Container queries need to go in a stylesheet. You can’t include these in the Additional CSS area of a block theme. Those styles are written to JSON and certain CSS will be stripped out before it’s saved.

Let’s also be honest about when not to reach for a container query. If an element always spans the full viewport (a site header, a full-width hero), the container and the viewport are the same thing, and a media query is the simpler tool. Use container queries for the components that move; use media queries for the page-level scaffolding.

One last teaser: everything in this post queries a container’s size, but there’s a newer cousin called style queries (@container style(...)) that lets you apply styles based on a container’s custom property values instead. Browser support is still settling, so I’m leaving them out of scope here, but there’s a link below if you’re curious.

Wrapping up

We built one CTA card template part and made it genuinely portable with three techniques: an unnamed container query for the stack/unstack layout, named containers for explicit control when containers nest, and cqi units for type and spacing that scale with the space they’re given. Drop the card in a sidebar, a column, or a full-width footer and it handles itself.

If you give this a try in your own theme, I’d love to hear how it goes.

Happy coding!

Further reading

  1. No matter how much you plan for it, users will do things in unexpected ways. As Dr. Ian Malcolm once put it: life finds a way (and so will your users). Try to build things in a durable way that works outside of ideal usage conditions. ↩︎
  2. Arguably, you could just add an extra group around it and make that part of the pattern, but that’s a whole other HTML element just to add a container. My preference is to keep the markup minimal wherever possible, which can also help your pages load faster on larger sites with a lot of content. ↩︎


Leave a Reply

Your email address will not be published. Required fields are marked *