11

I have a shipping form with a "billing address same as shipping" checkbox. Toggling it should collapse the billing fields and shift the layout. I was about to reach for JS class toggling out of habit.

Turns out :has() handles the whole thing: the form restyles itself based on the checkbox's :checked state as a pure CSS selector. I wrote a prompt that forces the parent-selector approach and forbids JS for the layout change, keeping JS only for actual data logic. The layout is now declarative and there's no state to get out of sync.

One worry: is :has() performant enough to drive a big form's layout, or am I setting a trap for a huge page?

THE PROMPT
Restyle a form layout purely with CSS `:has()` based on control state, no JavaScript for the layout change.
- Given a form with a checkbox `#same-as-shipping` and a `.billing` fieldset: when the box is checked, collapse `.billing` and expand the shipping column to full width.
- Implement with `form:has(#same-as-shipping:checked) .billing { ... }` and a sibling rule for the grid change. Do NOT toggle classes with JS for this.
- Collapse animation: transition `grid-template-rows` 0fr/1fr (not max-height), plus opacity. Respect `prefers-reduced-motion`.
- Keep it accessible: the collapsed fieldset should be `hidden`/`inert` when not needed so it's out of the tab order, and the checkbox has a real `<label>`.
- Also show a `:has(:invalid)` rule that flags the submit button state without JS.
Output HTML + CSS. Add a one-line note on where `:has()` could get expensive and how to scope it (e.g. anchor it to the form, not a broad ancestor).
:has() plus 0fr/1fr rows means an entire interactive form with zero JS state. We really are living in the future.pixelpusher 2 months ago
add a comment

1 Answer

7

Love this pattern but the accessibility detail matters: when you collapse .billing, use hidden or inert rather than just visually hiding it, otherwise a screen reader and keyboard still land on fields the user can't see. Your prompt already asks for inert, which many people miss. On perf: anchor :has() to the form as you said and you're fine; it gets costly when the :has() is on html/body and has to re-evaluate the whole tree.

Your Answer