skip to content

Your team wants to use `subgrid` for card alignment in a shared component library, but the support matrix still includes browsers without it. How would you approach adopting it?

level: principalimportance: nice to knowfreq 22%

answer

  1. enhancement, not compatibility
  2. detect the declaration, not the property
  3. one layout plus a small override
  4. the coupling outlives the fallback
  5. schedule the deletion

basics

~20 s

Ship a layout that is already acceptable without subgrid, then upgrade inside @supports (grid-template-columns: subgrid). Subgrid buys alignment refinement, not function, so the fallback should look deliberate rather than broken, and the upgrade should add rules instead of duplicating the layout.

solid answer

~50 s

Treat subgrid as a progressive enhancement of an already-working layout. The baseline is a nested grid or column layout inside each card that gives correct order, equal card heights from the default stretch, and a footer at the card's bottom — a layout nobody would call broken. Then add a single `@supports (grid-template-columns: subgrid)` block that spans the card across the parent's rows and switches its rows to `subgrid`, so the internal bands line up too. Keep the upgrade additive: the fallback rules stay untouched, and the supports block only changes what has to change, so there is no second copy of the layout to keep in sync. Also decide the coupling question — subgridding ties a card's internals to its parent's row template, so the library must document that contract or expose the row template as part of the component's API. Finally, revisit the support matrix on a schedule; subgrid is broadly supported now and the fallback is code you eventually delete.

code

css · 18 lines
css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  grid-template-rows: auto 1fr auto;
  gap: 1rem;
}

.card {
  display: grid;
  grid-template-rows: auto 1fr auto;
}

@supports (grid-template-columns: subgrid) {
  .card {
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

go deeper

for a junior

Know that @supports lets you write a rule that applies only where a feature exists, and that the plain rules outside it are what older browsers get.

for a middle

Explain why the test must name a property and a value, and structure the CSS so the enhanced block overrides a few declarations rather than restating the layout.

for a senior

Judge whether the fallback is acceptable on its own terms, decide how it gets verified so it does not rot, and name a concrete condition for deleting it.

for a principal

Own the API consequence: subgridding couples a library component to its consumer's grid template, so decide whether the library ships both sides, documents the contract, or keeps the enhancement opt-in.

## Frame it as enhancement, not compatibility The first judgment call is what subgrid is actually providing. In the card case it is *alignment of internal bands* across siblings, on top of a layout that already works. That makes it a textbook progressive enhancement: the page is fully usable without it, and better with it. Frame it that way and the whole plan follows — you do not need parity, you need a baseline nobody would file a bug against. Contrast this with a case where the feature is load-bearing (a layout that is unreadable without shared tracks). There, the honest answer is not "fall back"; it is either redesign so the feature is optional, or accept the older browsers are out of scope. Recognising which case you are in is the part a lead is expected to get right. ## The mechanism: feature detection `@supports` tests a declaration, so it detects the value, not just the property: ```css /* baseline — works everywhere */ .card { display: grid; grid-template-rows: auto 1fr auto; } /* upgrade */ @supports (grid-template-columns: subgrid) { .card { grid-row: span 3; grid-template-rows: subgrid; } } ``` Testing `grid-template-columns: subgrid` while applying `grid-template-rows: subgrid` is fine and common — a browser that supports the keyword supports it on both axes. Note also that the parent's `grid-template-rows: auto 1fr auto` can live in the baseline harmlessly, because in a non-subgrid browser the cards simply occupy one row each. ## Keep the diff, not a second layout The expensive mistake is maintaining two layouts: one inside `@supports not (...)` and another inside `@supports (...)`. Every future change then has to be made twice, and the two drift. Write the fallback as the unconditional rules and let the supports block override only the two or three declarations that differ. If you find the supports block growing past a handful of declarations, that is a signal the baseline design and the enhanced design are actually different designs, and you should reconsider one of them. ## The coupling cost, which outlasts the support question Subgrid makes a component's internals depend on its parent's track template: the card only works if it is placed in a grid that declares the three bands and if the card spans them. For a shared library that is a real API surface. Options, roughly in order of how much you are prepared to constrain consumers: - Ship the container and the card as a matched pair, so the library owns both sides of the contract. - Ship the card standalone but document the required row template and span, and keep it functional (unaligned) when dropped into any other grid. - Expose the enhancement behind an opt-in class or a custom property so consumers who cannot provide the outer grid never get the coupled behaviour. This question does not go away when every browser supports subgrid, which is why it deserves more attention than the fallback does. ## Testing and removal Decide up front how you verify the fallback: if nobody ever renders the non-subgrid path, it will rot. Either pin a check in visual review, or accept the risk explicitly. And set a review point — a note in the component, a ticket, whatever the team actually reads — for dropping the fallback when the support matrix moves. Subgrid shipped in Firefox 71, Safari 16, and Chrome/Edge 117 in September 2023, so for most products the fallback is already dead code carried out of habit; deleting it is a real simplification, not a risk. ## What a strong answer sounds like It covers: classify the feature as enhancement or load-bearing; detect with `@supports` on the declaration; keep the upgrade additive; name the parent-child coupling as the durable cost; and commit to removing the fallback rather than carrying it forever. A weak answer stops at "use `@supports`" and never mentions the maintenance or the coupling.

  • Why test `@supports (grid-template-columns: subgrid)` rather than checking for grid support?
    `@supports` evaluates a full declaration, so a property-only test would pass in every grid-capable browser, including ones that do not understand the `subgrid` value. Testing the property-value pair is the only way to detect the value itself. The axis in the test does not matter — support for the keyword is not per-axis.
  • When is subgrid the wrong thing to enhance with, even if it works?
    When it couples a component to a parent template it cannot rely on. A card that only aligns correctly inside a grid declaring three specific rows is no longer a standalone component. If consumers control the container, either ship the container with it, or keep the enhancement opt-in so the default drop-anywhere behaviour stays intact.
  • How do you keep a `@supports` fallback from becoming a maintenance burden?
    Keep the fallback as the unconditional rules and let the supports block override only the declarations that differ, so there is one layout plus a small diff rather than two layouts. Then attach a review point for deleting it once the support matrix moves; a fallback with no removal plan is carried indefinitely.

saying these in an interview costs you the question

  • Writing two full layouts, one per @supports branch
  • Testing only the property name rather than the property-value pair
  • Treating subgrid as unsupported when it has shipped everywhere current
  • Ignoring that subgrid couples a component to its parent's tracks
  • Keeping a fallback forever with no plan to delete it

context