Tailwind CSS 3.4: dynamic viewports, :has(), and subgrid

Trace Tailwind 3.4 layout utilities through compiled CSS and a native form, with a pinned browser fixture for resizing, subgrid and missing source paths.
The utilities expose browser behavior
Tailwind CSS 3.4 was released on 19 December 2023 with dynamic viewport utilities, the has variant and subgrid support. This explainer is for frontend maintainers deciding which layout behavior must survive when those enhancements are absent.
The examples were checked on 13 October 2026 with Tailwind 3.4.19, Node 24.20.0, Playwright 1.63.0 and Chromium 153.0.8010.12. The downloadable fixture compiles the exact fragments and loads them in a synthetic page. It uses the v3 configuration model. It does not test Tailwind 4 or recommend a downgrade.
The distinction is between presentation and interaction: losing the selected border need not break a native radio group, while clipping a growing page can hide its final control. The fixture checks those boundaries separately rather than accepting a successful compilation as proof of usability.
Use minimum height for a page that can grow
min-h-dvh sets a minimum of 100 dynamic viewport units. Content may make the element taller. h-dvh sets its height instead. Dynamic units track the changing viewport, so they can also resize a page while browser controls move.
This stylesheet keeps a 100vh minimum, then applies min-h-dvh within a supports rule. The fixture places page-shell on the outer main and page-content on its article. At a 390px width, resizing from 640px to 420px changed the computed minimum accordingly. After adding long content, the final button remained reachable.
The negative control adds a fixed 100dvh height and overflow: clip to the same shell. It fails the unchanged last-control assertion. That demonstrates this particular clipping error. A fixed-height scroll region with a deliberate overflow policy is a different design, not something this fixture evaluates.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.page-shell {
min-height: 100vh;
display: flex;
flex-direction: column;
}
@supports (height: 100dvh) {
.page-shell {
@apply min-h-dvh;
}
}
.page-content {
flex: 1;
min-width: 0;
}
}
Keep the radio button as the source of truth
The has-[:checked] variant compiles a selector that matches a label containing a checked descendant. The native radio remains visible. The border follows its state rather than a second JavaScript selection variable.
In the browser run, ArrowRight moved focus and selection from Monthly to Yearly. Clicking Continue triggered the form submit handler, which read billing=yearly through FormData. Removing the checked-state classes failed the blue-border assertion. With enhancement rules removed instead, keyboard selection and local submission still worked, and the border stayed slate.
Keep complete utility strings in files included by content paths. The v3 scanner does not evaluate a class name assembled from fragments. The missing-content control uses an empty content list: compilation succeeds, but required selectors are absent and the source assertion fails before the browser starts.
<fieldset>
<legend>Billing frequency</legend>
<label class="block rounded border border-slate-400 p-4 has-[:checked]:border-blue-700">
<input type="radio" name="billing" value="monthly" checked />
Monthly
</label>
<label class="mt-2 block rounded border border-slate-400 p-4 has-[:checked]:border-blue-700">
<input type="radio" name="billing" value="yearly" />
Yearly
</label>
</fieldset>
Subgrid shares tracks across a nested element
A nested grid normally has independent tracks. Subgrid uses the parent tracks across the columns the child spans. Here col-span-2 places each description-list row across both parent columns. The supports variant enables grid-cols-subgrid only when the declaration is supported.
Both parent tracks have a zero minimum, and the labels can wrap. The value track gets twice the available share of the label track. In the recorded 390px layout, the tracks were about 124.7px and 249.3px, with both values starting at 140.65625px. A long synthetic translated label left the document width at 390px.
Without the subgrid enhancement, grid-cols-2 gave each row two equal 187px tracks. Both values then started at 203px: the fallback remained aligned across these rows but lost the parent ratio. Removing col-span-2 produced different value positions and failed the alignment assertion. The sample storage and support text describes a fictional plan, not an Ulties service offer.
<dl class="grid grid-cols-[minmax(0,1fr)_minmax(0,2fr)] gap-x-4 gap-y-2">
<div class="col-span-2 grid grid-cols-2 gap-x-4 supports-[grid-template-columns:subgrid]:grid-cols-subgrid">
<dt class="min-w-0 break-words">Storage</dt>
<dd class="min-w-0 break-words">10 GB in this example plan</dd>
</div>
<div class="col-span-2 grid grid-cols-2 gap-x-4 supports-[grid-template-columns:subgrid]:grid-cols-subgrid">
<dt class="min-w-0 break-words">Support</dt>
<dd class="min-w-0 break-words">Email during published service hours</dd>
</div>
</dl>
What the checks establish
The normal run records 30 observations, including computed tracks, focus, submitted form value, resizing and scrolling. These are observations within a synthetic fixture, not 30 independent browser compatibility tests. Four deliberately altered inputs must fail at their intended source, border, alignment or scrolling assertion. An unrelated crash is not accepted.
The fixture removes enhancement rules to simulate fallback behavior in one pinned Chromium version. It records emulated forced-colors behavior with visible native radios, not a physical high-contrast display or screen-reader test. Viewport resizing does not establish mobile browser-chrome or virtual-keyboard behavior. Form submission is intercepted locally. There is no server, payment processing or persistence.
I recommend testing your oldest supported browsers with actual content, zoomed text, browser controls and an open virtual keyboard. Inspect generated CSS first when a utility is absent. Then inspect support and cascade behavior. Use these utilities where they express the intended layout, and keep ordinary CSS when it makes that behavior easier to inspect.
Reproduce the fragments
The source bundle includes the three fragments, dependency lock, immutable browser-image reference, probe, host validator and captured evidence. With Python 3 and Docker available, extract it and run python3 verify.py in the extracted directory. Dependency retrieval occurs during image construction. The browser runs without external network access.
A successful run prints JSON with status=pass, one normal result and four expected failures. The host validator checks source and helper hashes, runtime versions, the mutated CSS and HTML, every observation and each failure marker. Containers are removed on exit. The dependency image is a reproducible cache, so rebuilding it remains necessary after local Docker resets.
Reproduce the examples
Exact fragments, locked dependencies, browser probe, validator and captured observations. Requires Python 3 and Docker.
Download the source and tests (.zip)
