Tailwind CSS v4 Container Queries — Container-Based Responsive Design
In this tutorial, you will learn about Tailwind CSS v4 Container Queries. We cover key concepts, practical examples, and best practices to help you master this topic.
Tailwind CSS v4 container queries are built-in without plugins, using the @container CSS at-rule and container query prefixes (@sm:, @md:, @lg:) for styling based on element container width.
What You'll Learn
You will learn how to define containers, use container query variants, create container-aware responsive components, combine container queries with other variants, and customize container sizes.
Why It Matters
Container queries enable truly reusable components that adapt to their parent's width, not the viewport. DodaTech's dashboard widgets use container queries to rearrange layouts regardless of where they are placed on the page.
Real-World Use
Doda Browser's widget system uses container queries so weather widgets, news cards, and analytics panels rearrange their internal layout based on their container width in any dashboard configuration.
flowchart LR
A[Dark Mode] --> B[Container Queries]
B --> C[@container]
B --> D[@sm @md @lg]
B --> E[Custom Sizes]
B --> F[Composition]
style B fill:#38bdf8,stroke:#0284c7,color:#fff
style C fill:#22c55e,stroke:#16a34a,color:#fff
Defining Containers
@import "tailwindcss";
/* @container directive is built-in - no configuration needed */
<!-- Define a container with @container class -->
<div class="@container max-w-4xl mx-auto p-4">
<!-- Children use container query variants -->
<div class="grid grid-cols-1 @sm:grid-cols-2 @md:grid-cols-3 gap-4">
<div class="bg-blue-100 p-4 rounded">Item 1</div>
<div class="bg-blue-200 p-4 rounded">Item 2</div>
<div class="bg-blue-300 p-4 rounded">Item 3</div>
</div>
</div>
Expected output: The grid changes columns based on the @container width, not the viewport width. @sm activates at 40rem container width.
Container Query Variants
<div class="@container p-4 bg-gray-50 rounded-lg">
<div class="
flex flex-col
@sm:flex-row
@md:items-center
@lg:gap-8
gap-4
">
<div class="w-full @sm:w-32 @md:w-40 bg-blue-100 p-4 rounded">
Sidebar
</div>
<div class="flex-1">
<h2 class="text-lg @md:text-xl @lg:text-2xl font-bold">Responsive Title</h2>
<p class="text-gray-600 mt-2 @md:mt-4 text-sm @md:text-base">
Content that adapts to container width.
</p>
<div class="flex flex-col @sm:flex-row gap-2 mt-4">
<button class="bg-blue-600 text-white px-4 py-2 rounded">Action</button>
<button class="border px-4 py-2 rounded @lg:px-6">Secondary</button>
</div>
</div>
</div>
</div>
Expected output: The card layout, text size, spacing, and button sizing all respond to the container's width using @sm:, @md:, @lg: prefixes.
Custom Container Sizes
@import "tailwindcss";
@theme {
/* Custom container query breakpoints */
--container-xs: 20rem;
--container-sm: 30rem;
--container-md: 40rem;
--container-lg: 50rem;
--container-xl: 60rem;
/* Custom name */
--container-narrow: 35rem;
--container-wide: 55rem;
}
<div class="@container max-w-2xl mx-auto p-4">
<div class="
grid grid-cols-1
@xs:grid-cols-1
@narrow:grid-cols-2
@wide:grid-cols-4
gap-4
">
<div class="bg-blue-100 p-4 rounded">Custom breakpoints</div>
<div class="bg-blue-200 p-4 rounded">Based on @theme values</div>
<div class="bg-blue-300 p-4 rounded">@narrow at 35rem</div>
<div class="bg-blue-400 p-4 rounded">@wide at 55rem</div>
</div>
</div>
Expected output: Container query prefixes use the breakpoints defined in @theme. @narrow: and @wide: become available as custom container query prefixes.
Combining Container Queries with Variants
<div class="@container p-4">
<div class="
bg-white dark:bg-gray-800
@sm:dark:bg-gray-700
@md:hover:shadow-lg
@lg:focus:scale-105
p-6 rounded-lg transition-all
">
<h3 class="font-bold">Combined Variants</h3>
<p class="text-gray-600 dark:text-gray-300 mt-2">
Container queries work with dark: hover: focus: and other variants.
</p>
</div>
</div>
Expected output: Container query prefixes combine with other variants: @sm:dark:bg-gray-700 applies dark background only when the container is at least sm size.
Nested Containers
<div class="@container p-4 border rounded-lg">
<h2 class="text-xl font-bold @md:text-2xl">Outer Container</h2>
<div class="grid grid-cols-1 @md:grid-cols-2 gap-4 mt-4">
<!-- Each card can be its own container -->
<div class="@container p-4 bg-gray-50 rounded-lg">
<div class="flex flex-col @sm:flex-row gap-3">
<div class="w-full @sm:w-16 h-16 bg-blue-100 rounded-lg"></div>
<div>
<h3 class="font-bold @sm:text-lg">Nested card</h3>
<p class="text-sm text-gray-600">This card has its own container context.</p>
</div>
</div>
</div>
<div class="@container p-4 bg-gray-50 rounded-lg">
<div class="flex flex-col @sm:flex-row gap-3">
<div class="w-full @sm:w-16 h-16 bg-green-100 rounded-lg"></div>
<div>
<h3 class="font-bold @sm:text-lg">Independent responsive</h3>
<p class="text-sm text-gray-600">Each container context is independent.</p>
</div>
</div>
</div>
</div>
</div>
Expected output: Nested containers have independent container query contexts. Each card's layout adapts based on its own container width, not the outer container.
Container Query @apply
@import "tailwindcss";
.card-base {
@apply p-4 rounded-lg bg-white dark:bg-gray-800;
}
.card-responsive {
@apply card-base;
@apply @sm:flex @sm:gap-4 @md:p-6 @lg:p-8 @lg:text-lg;
}
<div class="@container">
<div class="card-responsive">
<div class="w-full @sm:w-24 h-24 bg-blue-100 rounded-lg"></div>
<div class="flex-1">
<h3 class="font-bold">Card with @apply</h3>
<p class="text-gray-600">Container query variants work inside @apply too.</p>
</div>
</div>
</div>
Expected output: @apply rules can include container query variant prefixes, enabling component classes with built-in container responsiveness.
Common Mistakes
1. Forgetting @container on Parent
Container query prefixes only work inside elements with the @container class. Without it, @sm:, @md:, @lg: do nothing.
2. Adding @container to the Element Being Styled
@container goes on the parent element. Children use @sm: to respond to the parent's container width.
3. Confusing @sm with sm:
@sm: is container-based. sm: is viewport-based. They can coexist but target different sizing contexts.
4. Not Setting a Container Type
Custom containers may need explicit container-type: container-type: inline-size. Use @container class which sets this automatically.
5. Nesting Without Consideration
Deeply nested containers create multiple contexts. Each @container creates a new containment context, overriding parent contexts.
Practice Questions
How do you define a container in v4? Add the
@containerclass to the parent element. Children use @sm:, @md:, @lg: prefixes.What does @sm: reference in container queries? The container's width, not the viewport. @sm activates at 40rem container width by default.
How do you customize container query breakpoints? Define
--container-sm,--container-md, etc. in @theme.Can container queries combine with dark mode? Yes:
@sm:dark:bg-gray-800applies dark background when container is sm+ in dark mode.Do container queries work in @apply? Yes:
@apply @sm:flex @sm:gap-4;includes container query variants.
Challenge
Build a reusable dashboard widget component with: @container parent, grid layout using @sm: and @md:, responsive typography with @lg:, nested container for child cards, and combined dark mode variants.
FAQ
Mini Project
Build a responsive widget system: 3 widget types (weather, news, stats) each using @container for layout. Widgets rearrange their internal layout based on container width. Include a dashboard that places widgets in different-sized containers.
What's Next
Now master New Utilities in v4 for the latest utility additions. Then explore Custom Themes for creating complete design systems.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro