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

Use Tailwind 3.4's layout utilities with working fallbacks, visible native controls, and tests for mobile resizing and content scanning.
The utilities expose browser behavior
Tailwind CSS 3.4 was released on 19 December 2023. It added utilities for dynamic viewport sizing, relational selectors, subgrid, and other CSS capabilities. These examples target the 3.4 configuration model, not Tailwind 4's setup.
A utility existing in the framework does not make its underlying CSS work in every browser you support. Decide which behavior must remain usable without the enhancement. A pricing choice can still work without a highlighted card; a fixed-height panel with unreachable controls is a functional failure.
Start with representative content and the oldest browsers in the project's support policy. Include a long translated label, zoomed text, and a mobile viewport with browser controls visible. Those cases reveal more than a screenshot of three equal-length cards.
Use minimum height for a page that can grow
min-h-dvh follows the dynamic viewport while allowing content to exceed it. h-dvh fixes the height and therefore needs a deliberate overflow strategy. For a content page, a minimum is often the easier starting point.
The CSS below is intended for an existing Tailwind 3.4 build. It keeps a 100vh fallback and applies the dynamic utility only when the browser recognizes dvh. The selector must be present on the page; this is stylesheet source, not code to run in the browser console.
@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 lets a label react to its checked descendant. In this example the selected border is an enhancement to a visible native radio group. There is no separate JavaScript selected-card state to synchronize with the form.
The markup belongs in a template scanned by Tailwind. Keep full utility strings intact; the v3 scanner does not evaluate a string assembled from fragments. The group remains usable if the browser ignores :has(), because the native checked indicator still conveys selection.
<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
Use subgrid when a child layout needs to align to the parent's tracks. The nested grid must span the relevant parent columns; simply adding grid-cols-subgrid to an unrelated element cannot align separate layouts.
This two-column description list gives each row the parent's label and value tracks. The supports variant preserves an independent two-column fallback. Without subgrid, rows remain readable but labels need not share the same column width.
<dl class="grid grid-cols-[max-content_minmax(0,1fr)] 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>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>Support</dt>
<dd class="min-w-0 break-words">Email during published service hours</dd>
</div>
</dl>
Verify the generated result
Inspect generated CSS when a utility appears to do nothing. First confirm that the template is included in content paths and that the exact class string is present. Then check whether the browser supports the declaration and whether another rule wins in the cascade.
On mobile, scroll until browser controls expand and collapse, rotate the device, and open the virtual keyboard. Dynamic viewport units do not remove the need to test keyboard and scrolling behavior. For radio choices, check focus visibility and forced-colors mode as well as checked styling.
Adopt the utilities where they remove a real layout workaround. Keep ordinary CSS where it expresses the component's behavior more clearly; Tailwind 3.4 does not require every selector to become a long class string.

