Close Menu
    Facebook X (Twitter) Instagram
    • About
    • Privacy Policy
    • Contact Us
    Tuesday, April 14
    Facebook X (Twitter) Instagram
    codeblib.comcodeblib.com
    • Web Development

      Conditional CSS Styling with @container

      April 13, 2026

      Your Shopify Theme Is Holding You Back

      April 11, 2026

      Building a Headless Shopify Store with Next.js 16: A Step-by-Step Guide

      October 28, 2025

      Dark Mode the Modern Way: Using the CSS light-dark() Function

      October 26, 2025

      The CSS if() Function Has Arrived: Conditional Styling Without JavaScript

      October 24, 2025
    • Mobile Development

      The Future of Progressive Web Apps: Are PWAs the End of Native Apps?

      November 3, 2025

      How Progressive Web Apps Supercharge SEO, Speed, and Conversions

      November 2, 2025

      How to Build a Progressive Web App with Next.js 16 (Complete Guide)

      November 1, 2025

      PWA Progressive Web Apps: The Secret Sauce Behind Modern Web Experiences

      October 31, 2025

      Progressive Web App (PWA) Explained: Why They’re Changing the Web in 2025

      October 30, 2025
    • Career & Industry

      AI Pair Programmers: Will ChatGPT Replace Junior Developers by 2030?

      April 7, 2025

      The Rise of Developer Advocacy: How to Transition from Coding to Evangelism

      February 28, 2025

      Future-Proofing Tech Careers: Skills to Survive Automation (Beyond Coding)

      February 22, 2025

      How to Build a Compelling Developer Portfolio: A Comprehensive Guide

      October 15, 2024

      The Future of Web Development: Trends to Watch in 2025

      October 15, 2024
    • Tools & Technologies

      The Future of AI Browsing: What Aera Browser Brings to Developers and Teams

      November 24, 2025

      Gemini 3 for Developers: New Tools, API Changes, and Coding Features Explained

      November 22, 2025

      Google Gemini 3 Launched: What’s New and Why It Matters

      November 19, 2025

      A Deep Dive Into Firefox AI Features: Chat Window, Shake-to-Summarize, and More

      November 18, 2025

      10 Tasks You Can Automate Today with Qoder

      November 16, 2025
    codeblib.comcodeblib.com
    Home»Web Development»Conditional CSS Styling with @container
    Web Development

    Conditional CSS Styling with @container

    No more JS media-query hacks. Container queries step-by-step — build a responsive product card that adapts to its parent, not the viewport.
    codeblibBy codeblibApril 13, 2026No Comments15 Mins Read
    Conditional CSS Styling with @container
    Conditional CSS Styling with @container
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    You’ve built the perfect product card. Image on the left, details on the right, price badge sitting neatly in the corner. You test it in the main content area and it’s gorgeous. Then your designer drops it into a 280px sidebar and everything falls apart — text overflows, the image squishes, and the whole layout looks like it was designed by someone who hates users.

    So you reach for @media (max-width: 768px). But here’s the problem: the sidebar is narrow on desktop too. The viewport is 1440px wide, your card has 280px of space, and your media query has absolutely no idea about any of that. It only knows about the window.

    Some developers solve this with ResizeObserver, toggling CSS classes from JavaScript whenever a component changes size. It works, but it’s fragile, adds JavaScript to a purely visual problem, and frankly, CSS shouldn’t need a babysitter.

    CSS container queries are the fix. They let a component ask “how much space do I actually have?” rather than “how big is the browser window?” — and adjust its own layout accordingly. They’ve been fully supported across all major browsers since early 2023, and as of 2026, they sit at roughly 96% global coverage. There’s no good reason not to be using them.

    In this post, you’ll learn exactly how they work — from the core mental model through the full syntax — and you’ll build a responsive product card that handles every context it’s dropped into without a single line of JavaScript.

    The Problem with Viewport Thinking

    For most of the web’s history, “responsive design” meant “responsive to the browser window.” Media queries let you write rules like “when the screen is wider than 768px, show a two-column layout” — and for a long time, that was enough.

    It stopped being enough when we moved to component-based development. A card component might appear in a full-width hero section, a three-column product grid, a two-column comparison layout, a narrow sidebar, and a modal — all on the same page. Each context gives the card a different amount of horizontal space. But media queries only know one thing: how wide the viewport is.

    Media queries ask: “How big is the screen?” Container queries ask: “How much space does my parent give me?” Those are entirely different questions with entirely different answers.

    The classic workaround is to create multiple component variants — .card--small, .card--large — and apply them via JavaScript based on computed widths. Or you use ResizeObserver to watch a container and toggle classes. Both approaches work, but they’re indirection: you’re solving a CSS layout problem with JavaScript scaffolding.

    Container queries eliminate that scaffolding entirely. The styling logic lives in CSS, where it belongs.

    What Are CSS Container Queries?

    The core idea is simple: you register an element as a “container,” and then write conditional CSS rules that fire when that container hits certain dimensions. Children of the container can inspect it and adapt their own styles accordingly.

    It’s the same mental model as media queries, but scoped to an element rather than the viewport. Once you internalize that shift, the syntax feels immediately familiar.

    Container queries vs. media queries — who wins?

    Both. They solve different problems and work best in combination.

    Use CaseRight ToolWhy
    Page-level grid: 1 column → 3 columns@mediaThis is genuinely about the viewport
    Sidebar visibility on mobile@mediaGlobal layout decision
    Product card stacked → horizontal@containerComponent reacts to its own space
    Navigation items collapse@containerNav adapts to its wrapper, not screen
    OS dark mode preference@mediaSystem preference, not element size
    Image srcset / sizes@mediaBrowser picks images before CSS runs

    The mental model that works: page layout = media queries; component layout = container queries. Use media queries to arrange the boxes on the page, then let container queries handle what happens inside each box.

    Where browser support stands

    Container size queries — the kind we’ll be using throughout this post — have been stable across Chrome (v106+), Safari (v16+), Firefox (v110+), and Edge (v106+) since 2023. According to the 2025 State of CSS survey, 41% of developers have used them at least once, with awareness climbing to 86%. Style queries and scroll-state queries are newer and have patchier support, but we’ll touch on those at the end.

    The Syntax, Demystified

    Container queries involve exactly two steps: declare a container, then query it. Neither step is complicated, but there are a few gotchas worth knowing before you write your first one.

    Step 1 — Declaring a container with container-type

    You must explicitly opt an element into being a container. CSS doesn’t make every element queryable by default — for performance reasons, containment is opt-in.

    /* The most common value — queries against width (inline axis) */
    .card-wrapper {
      container-type: inline-size;
    }
    
    /* Both width AND height — requires explicit height to avoid collapse */
    .fixed-panel {
      container-type: size;
      height: 400px;
    }

    Use inline-size in almost every case. The size value queries both axes but requires the container to have an explicit height — skip it unless you specifically need to query vertical space.

    Step 2 — Naming containers (and when you must)

    /* Longhand */
    .sidebar {
      container-type: inline-size;
      container-name: sidebar;
    }
    
    /* Shorthand — name first, then type */
    .sidebar {
      container: sidebar / inline-size;
    }

    When you leave out a name, @container queries match the nearest ancestor with containment applied. Add a name whenever you have nested containers and need to target a specific one.

    Step 3 — Writing @container queries

    /* Anonymous — matches nearest container ancestor */
    @container (min-width: 400px) {
      .card {
        display: flex;
      }
    }
    
    /* Named — matches 'sidebar' container specifically */
    @container sidebar (min-width: 300px) {
      .nav-item {
        font-size: 0.9rem;
      }
    }
    
    /* Modern range syntax — works in all supporting browsers */
    @container (width >= 400px) {
      .card {
        flex-direction: row;
      }
    }

    The shorthand: container: name / type

    The container shorthand lets you declare both name and type in one line. Name comes first, then a slash, then the type:

    .card-wrapper {
      container: product-card / inline-size;
    }

    Tip: CSS nesting lets you place @container queries right inside your component’s ruleset, keeping all related styles together. This is a great pattern for component-based CSS architecture.

    Build It — A Responsive Product Card

    Let’s build a product card that handles three layouts automatically: fully stacked (narrow containers like sidebars), side-by-side with a small image (medium containers like grid cells), and full horizontal layout with a large image (wide contexts like featured sections). No JavaScript, no component variants, no duplicate CSS.

    The HTML structure

    <!-- 
      The wrapper is the container.
      The card itself is what we style.
      These must be DIFFERENT elements —
      a container cannot query itself.
    -->
    <div class="card-wrapper">
      <article class="product-card">
        <div class="card-image">
          <img src="product.jpg" alt="Blue canvas sneaker">
        </div>
        <div class="card-body">
          <span class="card-category">Footwear</span>
          <h2 class="card-title">Canvas Lo-Top</h2>
          <p class="card-desc">Classic court silhouette, updated with a lightweight sole.</p>
          <div class="card-footer">
            <span class="card-price">$89</span>
            <button class="card-cta">Add to cart</button>
          </div>
        </div>
      </article>
    </div>

    Base styles — mobile-first, stacked

    Start with the stacked, narrow layout as the default. This is what the card looks like when dropped into any container less than 400px wide.

    /* ── Declare the container ─────────────────────── */
    .card-wrapper {
      container: product-card / inline-size;
    }
    
    /* ── Base card styles (narrow / stacked) ──────── */
    .product-card {
      display: grid;
      grid-template-rows: auto 1fr;
      background: #fff;
      border-radius: 10px;
      overflow: hidden;
      box-shadow: 0 2px 12px rgba(0,0,0,.08);
    }
    
    .card-image img {
      width: 100%;
      height: 200px;
      object-fit: cover;
      display: block;
    }
    
    .card-body {
      padding: 16px;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    
    .card-category {
      font-size: 0.7rem;
      text-transform: uppercase;
      letter-spacing: .08em;
      color: #888;
    }
    
    .card-title {
      font-size: clamp(1rem, 4cqi, 1.4rem); /* fluid — more on this below */
      font-weight: 700;
      line-height: 1.2;
    }
    
    .card-desc {
      font-size: 0.875rem;
      color: #555;
      display: none; /* hidden by default, revealed at wider sizes */
    }
    
    .card-footer {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-top: auto;
    }
    
    .card-price {
      font-size: 1.1rem;
      font-weight: 700;
    }
    
    .card-cta {
      background: #111;
      color: #fff;
      border: none;
      border-radius: 6px;
      padding: 8px 16px;
      font-size: 0.8rem;
      cursor: pointer;
    }

    The first breakpoint — going horizontal at 400px

    When the container is at least 400px wide, switch to a side-by-side layout with a smaller thumbnail. The description also appears at this point — there’s enough room to show it without it feeling cramped.

    @container product-card (width >= 400px) {
    
      .product-card {
        grid-template-rows: unset;
        grid-template-columns: 140px 1fr;
      }
    
      .card-image img {
        height: 100%; /* fills the left column */
        min-height: 160px;
      }
    
      .card-body {
        padding: 20px;
      }
    
      .card-desc {
        display: block; /* reveal at this breakpoint */
      }
    
    }

    The second breakpoint — full layout at 600px

    In a wide context — main content area, featured section, full-width placement — the image gets more space, the title grows, and the button becomes more prominent.

    @container product-card (width >= 600px) {
    
      .product-card {
        grid-template-columns: 260px 1fr;
      }
    
      .card-image img {
        min-height: 220px;
      }
    
      .card-body {
        padding: 28px;
        gap: 12px;
      }
    
      .card-title {
        font-size: 1.5rem;
      }
    
      .card-cta {
        padding: 10px 24px;
        font-size: 0.875rem;
      }
    
    }

    That’s the complete card. Drop .card-wrapper into any layout — a three-column grid, a sidebar, a modal, a hero section — and the card adapts without any additional CSS, JavaScript, or component variants.

    Key insight: The breakpoints (400px, 600px) refer to the container’s width, not the viewport. A card in a 300px sidebar on a 1920px screen uses the stacked layout. The same card in a 650px main column uses the full horizontal layout. That’s the whole point.

    Container Query Units — Your New Best Friends

    Container queries don’t just let you write conditional blocks — they come with a set of length units that let child elements size themselves proportionally to their container, similar to how vw and vh work for the viewport.

    UnitMeaning
    cqw1% of the container’s width
    cqh1% of the container’s height
    cqi1% of the container’s inline size (usually width)
    cqb1% of the container’s block size (usually height)
    cqminSmaller of cqi or cqb
    cqmaxLarger of cqi or cqb

    When to use cqi over cqw

    According to CSS-Tricks, cqi and cqb are the logical equivalents of cqw and cqh — meaning they automatically flip to the correct axis in vertical writing modes. In horizontal writing mode (the default for most western languages), cqi and cqw are identical. But if you’re building for international audiences with vertical text, cqi does the right thing automatically. It’s a small habit worth getting into.

    Combining container units with clamp()

    The real power comes from pairing these units with clamp() for fluid typography that scales relative to the container rather than the viewport:

    /* Title scales between 1rem and 2rem based on container width */
    .card-title {
      font-size: clamp(1rem, 4cqi, 2rem);
    }
    
    /* Padding that breathes with available space */
    .card-body {
      padding: clamp(12px, 3cqi, 32px);
    }
    
    /*
      The same component in a 200px container and a 600px container
      will scale its typography proportionally —
      no manual breakpoint juggling needed.
    */

    Important: Container units cannot measure the element they’re applied to — only its container. If you apply width: 50cqw to .card-wrapper, it looks at the parent of .card-wrapper, not itself. This is by design (circular references would be a nightmare), but it trips people up.

    Common Pitfalls (and How to Avoid Them)

    Container queries have a handful of genuine gotchas. Knowing them ahead of time saves significant debugging time.

    Container collapse with container-type: size. When you use size instead of inline-size, CSS severs the container’s relationship with its children for height calculation. Without an explicit height, the container collapses to zero. Josh W. Comeau calls this “the impossible problem”: you can’t measure something whose size depends on what you’re measuring. The fix is almost always to use inline-size instead, which only severs the width relationship and lets height grow naturally with content.

    A container cannot query itself. You always need a wrapper element. The container element is the measuring stick; its children are what get styled. This is why the product card HTML above uses .card-wrapper as the container and .product-card as the styled element.

    No CSS custom properties inside query conditions. This is a common trap: @container (min-width: var(--breakpoint)) is invalid and silently ignored. Container query conditions must use raw values. Store your breakpoint values in comments if you want to document them.

    Flexbox content collapse. When a flex item becomes a container, its children can lose their intrinsic size information. You’ll need to give flex items either explicit sizes or ensure they have intrinsic sizing to avoid content collapse inside container queries.

    Nested containers match the nearest ancestor. If you drop a component with container queries inside an element that also has container-type, the queries will match that inner container — not the outer one you might have intended. Use named containers to be explicit about which ancestor you’re querying.

    Inline elements can’t be containers. You can’t use container-type on a span unless you change its display to something non-inline. Any block-level or flex/grid element works fine.

    Container Queries + Media Queries — The Right Mental Model

    The most productive way to think about this isn’t “container queries are replacing media queries.” It’s that they complete the picture. You need both, and they operate at different scopes.

    Media queries own the page shell. They’re the right tool for deciding how many grid columns exist, whether a sidebar is visible, global typography scale adjustments, and OS-level preferences like dark mode or reduced motion. These are decisions that genuinely belong at the viewport level.

    Container queries own the components. Once you’ve established the page layout with media queries, container queries let each component adapt to whatever space it receives. The card doesn’t care that there are three columns — it cares how wide its column is.

    /* Media query: page-level layout decision */
    @media (min-width: 900px) {
      .product-grid {
        grid-template-columns: repeat(3, 1fr);
      }
    }
    
    /* Container query: component-level decision */
    .card-wrapper {
      container: product-card / inline-size;
    }
    
    @container product-card (width >= 400px) {
      .product-card {
        grid-template-columns: 140px 1fr;
      }
    }
    
    /*
      The grid arranges columns at the page level,
      and each card adapts to whatever column width it gets.
      Both tools, doing different jobs.
    */

    One area where media queries still strictly win: image loading. The browser processes srcset and sizes attributes before CSS runs, so container queries don’t affect which image file gets fetched. If your product card image needs to load different resolutions based on context, you’ll still write that logic with @media in your sizes attribute.

    Progressive Enhancement and Fallbacks

    Container size queries sit at around 96% global support as of early 2026. For most projects that’s enough to use them directly. For the remaining 4%, a graceful fallback strategy is simple.

    The base styles you write first — the stacked, narrow layout — work everywhere. Container query enhancements layer on top only where supported. That’s progressive enhancement built in by default.

    For more control, use @supports:

    /* Base styles work for all browsers */
    .product-card {
      display: grid;
    }
    
    /* Container query enhancements for supporting browsers */
    @supports (container-type: inline-size) {
      .card-wrapper {
        container: product-card / inline-size;
      }
    
      @container product-card (width >= 400px) {
        .product-card {
          grid-template-columns: 140px 1fr;
        }
      }
    }

    In browsers that don’t support container queries, the card stays stacked — a perfectly usable layout. Browsers that do support them get the adaptive behaviour. No JavaScript polyfills required.

    What’s Coming Next — Style Queries and Scroll-State Queries

    Container size queries are the stable, production-ready foundation. But the container query spec includes two more variants worth knowing about.

    Style queries let you apply styles based on the computed CSS properties of a container — including custom properties. This enables things like theme-aware components that read a --theme custom property from their parent and adapt accordingly, without JavaScript. Custom property style queries work in most evergreen browsers today; querying standard CSS properties has broader support coming soon.

    :root {
      --card-theme: light;
    }
    
    .promo-section {
      --card-theme: dark;
      container-type: inline-size;
    }
    
    /* Card detects its parent's theme without a class toggle */
    @container style(--card-theme: dark) {
      .product-card {
        background: #111;
        color: #fff;
      }
    }

    Scroll-state queries are newer still, landing in Chromium-based browsers in early 2025. They let you style a container’s children based on scroll state — whether the container is currently stuck (as a sticky element), snapped, or scrollable. A navigation bar that adds a shadow only when stuck is the canonical use case, and it’s genuinely elegant CSS.

    The 2025 State of CSS survey shows style and scroll-state queries still sit at very low adoption (7% and under 1% respectively), largely because developers are still catching up with the more mature size queries. But awareness is growing fast, and both features are clearly where the spec is heading.

    Putting It All Together

    Container queries solve a problem that media queries structurally cannot: components that know their own available space. The product card you built in this post will slot into any layout — sidebar, grid cell, featured hero, modal — and adapt correctly, with no JS, no component variants, and no media-query magic numbers that only make sense in one specific context.

    The migration path is low-risk and incremental. Start by identifying components in your codebase that appear in multiple layout contexts — cards, widgets, navigation elements. Add container-type: inline-size to their parent wrappers (it has no visual effect on its own). Then convert those components’ responsive rules from @media to @container, adjusting breakpoint values since you’re now measuring component width, not viewport width.

    Keep page-level decisions — grid column counts, sidebar visibility, global font scale — in media queries. Let container queries handle everything inside the grid cells.

    The mental model shift is small. The payoff — genuinely portable, context-aware components — is significant.

    Further Reading

    • MDN — Container Queries
    • CSS-Tricks — CSS Container Queries Guide
    • Josh W. Comeau — A Friendly Introduction to Container Queries
    • Ahmad Shadeed — An Interactive Guide to CSS Container Queries
    • web.dev — Container Queries
    • LogRocket — Container Queries in 2026
    • CSS-Tricks — Container Query Units: cqi and cqb
    css new update
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    Unknown's avatar
    codeblib

    Related Posts

    Your Shopify Theme Is Holding You Back

    April 11, 2026

    Building a Headless Shopify Store with Next.js 16: A Step-by-Step Guide

    October 28, 2025

    Dark Mode the Modern Way: Using the CSS light-dark() Function

    October 26, 2025

    The CSS if() Function Has Arrived: Conditional Styling Without JavaScript

    October 24, 2025

    Voice Search Optimization for Web Developers: Building Voice-Friendly Websites in the Age of Conversational AI

    October 20, 2025

    Voice Search Optimization: How AI Is Changing Search Behavior

    October 19, 2025
    Add A Comment

    Comments are closed.

    Categories
    • Career & Industry
    • Editor's Picks
    • Featured
    • Mobile Development
    • Tools & Technologies
    • Web Development
    Latest Posts

    React 19: Mastering the useActionState Hook

    January 6, 2025

    Snap & Code: Crafting a Powerful Camera App with React Native

    January 1, 2025

    Progressive Web Apps: The Future of Web Development

    December 18, 2024

    The Future of React: What React 19 Brings to the Table

    December 11, 2024
    Stay In Touch
    • Instagram
    • YouTube
    • LinkedIn
    About Us
    About Us

    At Codeblib, we believe that learning should be accessible, impactful, and, above all, inspiring. Our blog delivers expert-driven guides, in-depth tutorials, and actionable insights tailored for both beginners and seasoned professionals.

    Email Us: info@codeblib.com

    Our Picks

    Conditional CSS Styling with @container

    April 13, 2026

    Your Shopify Theme Is Holding You Back

    April 11, 2026

    The Future of AI Browsing: What Aera Browser Brings to Developers and Teams

    November 24, 2025
    Most Popular

    A Deep Dive Into Firefox AI Features: Chat Window, Shake-to-Summarize, and More

    November 18, 2025

    10 Tasks You Can Automate Today with Qoder

    November 16, 2025

    How Qoder’ Quest Mode Replaces Hours of Dev Work

    November 15, 2025
    Instagram LinkedIn X (Twitter)
    • Home
    • Web Development
    • Mobile Development
    • Career & Industry
    • Tools & Technologies
    © 2026 Codeblib Designed by codeblib Team

    Type above and press Enter to search. Press Esc to cancel.