skip to content

Responsive Design

Making one stylesheet work across every viewport, zoom level, and input type. Interviewers use responsive questions to see whether you reach for breakpoints reflexively or reason from the content — and whether you know container queries exist.

part ofCSSoverview, primer and where to startread it →
on this pageshow

explore

questions

26

In CSS, how do you make an element a query container named card and apply styles only when that container is at least 400px wide?

level: juniorimportance: must knowfreq 55%

answer

  1. two declarations plus one at-rule
  2. container-type first, name optional
  3. shorthand order is name then type
  4. styles land on descendants
  5. min-width or the range form

basics

~20 s

Set container-type: inline-size and container-name: card on the wrapper, or the shorthand container: card / inline-size, then put the rules inside @container card (min-width: 400px). Those rules style the container's descendants, not the container itself.

solid answer

~40 s

Two declarations and one at-rule. On the wrapper you write `container-type: inline-size` to make it queryable and `container-name: card` to label it, or the shorthand `container: card / inline-size`. Then the component's rules go inside `@container card (min-width: 400px) { … }`. The name is optional — an unnamed `@container (min-width: 400px)` matches the nearest ancestor query container — but naming is useful when containers nest and you want to skip past the closest one. Conditions also accept range syntax, so `@container card (width >= 400px)` is equivalent. The critical detail is directionality: the rules inside the block apply to **descendants** of the container, never to the container element itself, so `container-type` belongs on a wrapper and the selectors inside target the children.

code

css · 15 lines
css
.card-slot {
  container: card / inline-size;
}

.card {
  display: grid;
  gap: 1rem;
}

@container card (min-width: 400px) {
  .card {
    grid-template-columns: 8rem 1fr;
    align-items: start;
  }
}

go deeper

for a junior

Be able to write the pattern from memory: container-type on a wrapper, optional container-name, then an @container block whose selectors target the children.

for a middle

Explain when a name is worth adding, how an unnamed query resolves to the nearest ancestor container, and why the range syntax and min-width forms are interchangeable.

for a senior

Show where the container wrapper belongs in a component's markup contract so the component stays portable, and how you detect support or degrade when the at-rule is ignored.

for a principal

Own the naming vocabulary across a design system — which slots are containers, what they are called, and how nested containers are kept from making component styles unpredictable.

## The three pieces A working container query always has three parts: an element that opts in as a container, an optional name for it, and an `@container` rule whose selectors target that container's descendants. ```css /* 1 + 2: the wrapper opts in and takes a name */ .card-slot { container-type: inline-size; container-name: card; } /* 3: the component's rules, conditional on the container's width */ @container card (min-width: 400px) { .card { grid-template-columns: 8rem 1fr; } } ``` The shorthand collapses the first two declarations, with the name before the slash and the type after it: ```css .card-slot { container: card / inline-size; } ``` The order matters and is easy to invert from memory — it is `name / type`, matching the way `@container <name> (<condition>)` reads. ## container-type: what to pass - `inline-size` — queryable on the inline axis (width in a horizontal writing mode). This is what you want almost always. - `size` — queryable on both axes; only sound when the element has a definite size in both, otherwise it collapses. - `normal` — the default; not a size query container. ## The name is optional Without a name, `@container (min-width: 400px)` resolves against the **nearest ancestor query container** of each element the inner selectors match. That is usually what you want, and unnamed queries keep components portable — the component does not need to know what its slot is called. Names earn their keep when containers nest. Consider a card container inside a page-region container: an unnamed query inside the card will always hit the card. If a rule needs the outer region's width, name the containers and query the outer one explicitly: ```css .region { container: layout-region / inline-size; } .card-slot { container: card / inline-size; } @container layout-region (min-width: 60rem) { … } /* skips past the card */ ``` A container may carry several names (`container-name: card sidebar-slot;`), which lets one element answer to more than one query vocabulary. ## Writing the condition The condition grammar mirrors media queries. Both of these are the same query: ```css @container card (min-width: 400px) { … } @container card (width >= 400px) { … } ``` Range syntax also supports two-sided ranges — `@container card (400px <= width < 700px)` — and conditions combine with `and`, `or` and `not`, plus parentheses for grouping. Because the queried axis is inline under `container-type: inline-size`, height-based conditions such as `min-height` will not match; those require `container-type: size` on a container with a definite height. Inside a container query you can also use container-relative length units, which resolve against the query container rather than the viewport; they are a topic of their own but they pair naturally with these rules. ## The direction of application This is the part that trips people up. The block styles **descendants of the container**, not the container. This does not work: ```css /* broken: .card is the container and can never match its own query */ .card { container-type: inline-size; } @container (min-width: 400px) { .card { grid-template-columns: 8rem 1fr; } } ``` An element matching its own size query would create a circular dependency — the rule could change the width the query just measured — so the specification simply excludes it. The fix is structural: keep a wrapper as the container and style the component inside it. ```html <div class="card-slot"> <article class="card"> … </article> </div> ``` In practice, most component libraries add exactly one wrapper element per placement slot for this reason, and the component itself carries only `@container` rules — which is what makes it portable across pages. ## Feature detection If you need to branch on support, `@supports (container-type: inline-size) { … }` is the correct test. In most products this is no longer necessary; the usual approach is to write the narrow layout as the base styles so an unsupporting browser simply keeps it.

  • What happens if you omit container-name and just write @container (min-width: 400px)?
    The query resolves against the nearest ancestor query container of whatever the inner selectors match. That is the common case and keeps a component portable, because it does not have to know the name of the slot it was placed in. Names are only needed to reach past a closer container to an outer one.
  • Can one element be given more than one container name?
    Yes — `container-name` accepts a space-separated list, such as `container-name: card sidebar-slot;`. The element then matches queries written against either name. It is useful when the same wrapper participates in a component-level vocabulary and a layout-level one.
  • Why does @container card (min-height: 300px) never match a container declared with container-type: inline-size?
    Inline-size containment only makes the inline axis queryable, so block-axis conditions cannot be evaluated and the query does not match. Querying height requires `container-type: size`, which contains both axes — and that is only safe when the container has a definite height, otherwise it collapses.

saying these in an interview costs you the question

  • Writes the shorthand as container: inline-size / card
  • Applies the @container rules to the container element itself
  • Omits container-type and expects @container to work
  • Thinks container-name alone makes an element queryable
  • Queries min-height against an inline-size container

context

open as a page

What does the CSS clamp() function do with its three arguments, and how would you use it to make a heading's font-size scale with the viewport?

level: juniorimportance: must knowfreq 70%

basics

~20 s

clamp(MIN, PREFERRED, MAX) returns the preferred value when it lies between the bounds, MIN when it falls below, and MAX when it rises above. font-size: clamp(1.5rem, 4vw, 3rem) scales a heading with the viewport but never below 1.5rem or above 3rem.

open as a page

What does "mobile-first" mean in a CSS stylesheet, and why does it favour min-width media queries over max-width ones?

level: juniorimportance: must knowfreq 76%

basics

~10 s

Mobile-first means the unconditional base rules describe the narrowest layout, and min-width media queries add complexity as the viewport grows. Because narrow screens are the default, the stylesheet accumulates enhancements instead of overrides.

open as a page

What does the tag <meta name="viewport" content="width=device-width, initial-scale=1"> do, and what happens to a responsive page on a phone if it is omitted?

level: juniorimportance: must knowfreq 70%

basics

~20 s

It tells a mobile browser to make the layout viewport the device's own width at 100% zoom, instead of a wide fallback of roughly 980 CSS pixels. Without it, a phone lays the page out wide and shrinks the whole thing down.

open as a page

Why can a reusable card component not be made truly responsive with CSS @media queries alone, and what do container queries change?

level: middleimportance: must knowfreq 68%

basics

~20 s

@media queries only test the viewport, so one card gets the same breakpoint in a wide main column and in a narrow sidebar. Container queries let a component respond to the size of its own container instead.

open as a page

In CSS, how do you express AND, OR, and NOT across @media conditions, and what does the leading only keyword do?

level: middleimportance: must knowfreq 58%

basics

~20 s

AND is the and keyword between parenthesised conditions. OR is a comma-separated list of whole queries, with an explicit or keyword available in newer syntax. NOT negates an entire query. The legacy only keyword hides a query from pre-Level-3 browsers and otherwise does nothing.

open as a page

In CSS, what is the difference between px, em, and rem, and why can nested em values compound unexpectedly?

level: middleimportance: must knowfreq 78%

basics

~20 s

px is absolute. em resolves against the element's own computed font-size — or the parent's, when you are setting font-size itself — so nested em font sizes multiply. rem always resolves against the root element's font-size, so it never compounds.

open as a page

In CSS, which media feature reports the user's light/dark appearance preference, and how do you use it to theme a page?

level: juniorimportance: should knowfreq 55%

basics

~20 s

The prefers-color-scheme media feature reports the operating system's appearance setting and matches either light or dark. Author one theme as the default, override the colour values inside @media (prefers-color-scheme: dark), and set color-scheme so browser-rendered UI follows too.

open as a page

In CSS, why does width: min(100%, 60ch) behave like a maximum width rather than a minimum one?

level: middleimportance: should knowfreq 50%

basics

~20 s

min() returns the smallest of its arguments at any moment, so it acts as a ceiling: the element is capped at 60ch and shrinks to 100% of its container when that is narrower. The function name describes the comparison performed, not the design intent.

open as a page

In a CSS media query, how does the range syntax such as @media (width >= 40rem) differ from @media (min-width: 40rem), and what problem does it solve?

level: middleimportance: should knowfreq 45%

basics

~20 s

They match identically, but the range syntax adds strict comparisons. min-width and max-width are always inclusive, so complementary breakpoints overlap by one pixel; range syntax writes width >= 40rem and width < 40rem, which partition the axis exactly, and supports two-sided ranges in one query.

open as a page

In a mobile-first CSS file, why must min-width media query blocks appear in ascending order of their width values?

level: middleimportance: should knowfreq 50%

basics

~20 s

At a wide viewport every min-width query with a smaller threshold matches simultaneously. Media queries add no specificity, so among equally specific matches the last one in source order wins — which means the widest breakpoint must be written last.

open as a page

How do you choose the breakpoint values for a responsive CSS layout, and why are device categories like "tablet" a poor basis for them?

level: middleimportance: should knowfreq 58%

basics

~20 s

Derive breakpoints from the content: widen the browser slowly and add a breakpoint at each width where the design actually stops working. Device categories are a poor basis because real viewport widths form a continuum that no fixed device list covers.

open as a page

In CSS, why does `height: 100%` on an element often have no effect while `width: 100%` works, and what do percentage paddings resolve against?

level: middleimportance: should knowfreq 58%

basics

~20 s

A percentage height needs a definite height on the containing block; when that ancestor is auto-height, the percentage behaves as auto. Widths work because block layout always gives a definite inline size. Percentage padding and margin resolve against the containing block's inline size, on every side.

open as a page

In CSS, what does container-type: inline-size do to an element besides making it a query container?

level: seniorimportance: should knowfreq 40%

basics

~20 s

It applies layout, style and inline-size containment. The element's inline size stops depending on its contents, and it becomes a stacking context and a containing block for absolutely and fixed-positioned descendants. Block size still grows with content.

open as a page

A CSS @container (min-width: 400px) block never applies even though the element is clearly wider than 400px on screen. What are the likely causes?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Usually no ancestor declared container-type, or the element being styled is itself the container — an element cannot match its own query. Other causes: a name that matches no ancestor, a nearer container shadowing the intended one, or a height condition against an inline-size container.

open as a page

How do you compute the vw plus rem preferred value inside clamp() so a font-size lands exactly on 16px at a 320px viewport and 24px at a 1280px viewport?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Treat it as a straight line through two points: slope is (24 - 16) / (1280 - 320) = 0.00833, or 0.833vw, and the intercept is 16 - 0.00833 x 320 = 13.33px = 0.833rem. That gives font-size: clamp(1rem, 0.833rem + 0.833vw, 1.5rem).

open as a page

Why is sizing body text with font-size: 4vw an accessibility problem, and how does a clamp() with a rem term fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Viewport units ignore the reader's browser font-size preference and text-only zoom, so 4vw text will not enlarge for someone who needs it bigger. Putting rem back into the value — clamp(1rem, 0.9rem + 1vw, 1.5rem) — restores that control while keeping the fluid behaviour.

open as a page

In CSS, how do the hover, any-hover, pointer and any-pointer media features differ, and how would you use them to adapt an interface to touch versus mouse input?

level: seniorimportance: should knowfreq 40%

basics

~20 s

hover and pointer describe the primary input mechanism; any-hover and any-pointer match if any available input qualifies. pointer reports none, coarse, or fine. Use them to gate hover-only affordances and to enlarge hit targets for coarse pointers, instead of inferring touch from viewport width.

open as a page

Before releasing a responsive page, why is stepping through a few DevTools device presets not enough verification, and what would you do instead?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Presets sample a handful of points on a continuous range, so bugs between them go unseen, and emulation still renders with the desktop engine. Sweep the full width range with real content, test text zoom and orientation, then confirm on real hardware.

open as a page

In CSS, why does a `height: 100vh` panel get clipped on mobile browsers, and how do the svh, lvh, and dvh units address it?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Mobile browsers size vh against the large viewport, with retractable toolbars assumed hidden, so 100vh exceeds the visible area while the toolbars are shown. svh is the small (toolbars shown) viewport, lvh the large one, and dvh tracks the current value as toolbars retract.

open as a page

In CSS, if a user raises their browser's default font-size preference, what happens to text sized in px versus rem — and is px text still accessible?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Text sized in px ignores the browser's default font-size preference entirely; rem and em text scales with it because the root font-size inherits that preference. Page zoom still enlarges px text, so px is not unusable — it just ignores one of the two resize mechanisms users have.

open as a page

You own the shared CSS for a design system used by many teams: how do you decide how many breakpoints it defines, and what does each additional one cost?

level: principalimportance: should knowfreq 33%

basics

~20 s

Keep the shared set small — typically three or four — because every breakpoint multiplies across the whole component inventory as states to design, review, document and test. Local one-off adjustments belong inside a component, not in the global scale.

open as a page

In CSS, what does the `ch` unit measure, and why is `max-width: 65ch` a common way to limit line length?

level: juniorimportance: nice to knowfreq 24%

basics

~20 s

1ch is the advance width of the character 0 (zero) in the element's own font, so it scales with that font and its size. It approximates a character width, which makes it a convenient unit for capping the line length of body text.

open as a page

What is a CSS style query written as @container style(--variant: promo), and how does it differ from a size container query?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

A style query tests the computed value of a property on the query container rather than the container's size. Every element is a style query container by default, so no container-type declaration is needed, and shipping browsers currently support only custom properties in the condition.

open as a page

Why is the declaration font-size: clamp(1rem, 1rem+2vw, 2rem) invalid CSS, and what is the minimal fix?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

CSS math grammar requires whitespace around the + and - operators, so 1rem+2vw fails to parse and the whole declaration is dropped. The fix is spacing alone: clamp(1rem, 1rem + 2vw, 2rem). No calc() wrapper is needed, because each clamp() argument is already a math expression.

open as a page

What does @media print change about how a page is styled in CSS, and which properties control page breaks and printed backgrounds?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

@media print applies only when a page is rendered to paper or PDF, where content is paginated instead of scrolled. Use break-inside, break-before and break-after to control pagination, @page for margins and sheet size, and print-color-adjust: exact to keep backgrounds the browser would otherwise drop.

open as a page