<!-- Generated by scripts/gen-skill.mjs from src/showcases/registry.ts. Do not edit by hand. -->

# Modern CSS and HTML, by how safe it is to ship

Tiers come from the `web-features` Baseline dataset at build time, not from
anyone’s opinion. Accessible implementations of every entry below are in
`css-snippets.md`. Live versions: https://accessible-by-default.dev/showcase.html

## Baseline: widely available

Safe to ship unguarded.

### Container queries

Components respond to the space they actually get instead of the viewport.

**Accessibility payoff:** Layouts that adapt to their space survive 400% zoom and squeezed sidebars alike — low-vision users get reflow (1.4.10) for free.

**Feature test (not needed to ship):** `@supports (container-type: inline-size)`

**Topics:** layout

- [MDN: container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries)

### Container query units

The other half of container queries: cqi units inside clamp() scale type and spacing in proportion to the component’s own space, with no breakpoints and no broken in-between states.

**Accessibility payoff:** Spacing in cqi gives text more room in a narrow container, even at 400% zoom; for type, a rem term in the middle and a maximum within 1.5 times the minimum let zoom double the text in every engine.

**Feature test (not needed to ship):** `@supports (width: 1cqi)`

**Topics:** layout, typography

- [MDN: container query length units](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_size_and_style_queries#container_query_length_units)

### :has() relational selector

Style an element from its descendants’ state — something no upward selector can do.

**Accessibility payoff:** :has() styles states the DOM already knows — no JS class-toggling that can drift from the real, announced state.

**Feature test (not needed to ship):** `@supports (selector(:has(a)))`

**Topics:** interaction

- [MDN: :has()](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)

### Count-aware layouts (quantity queries)

The chat-app photo bundle, in pure CSS: the grid counts its own children with exact-count quantity queries — :has(> :nth-child(3):last-child) reads "exactly three" — and re-composes its grid-template-areas per count.

**Accessibility payoff:** Count-aware layout keeps items at usable sizes however many a CMS delivers — target size (2.5.8) survives real content.

**Feature test (not needed to ship):** `@supports (selector(:has(> :nth-child(2))))`

**Topics:** layout

- [MDN: :has()](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)
- [A List Apart: Quantity Queries for CSS](https://alistapart.com/article/quantity-queries-for-css/)

### Subgrid

Nested grids adopt their parent’s tracks, so card internals align across siblings no matter how long the content gets — the kind of alignment that used to require fixed heights or JS.

**Accessibility payoff:** Aligned tracks across cards keep visual order and reading order identical — what a screen reader announces matches what eyes scan.

**Feature test (not needed to ship):** `@supports (grid-template-rows: subgrid)`

**Topics:** layout

- [MDN: subgrid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid)

### Sliding selection indicator

A modern way to visualize selection: one pill that travels between options instead of fading per-item.

**Accessibility payoff:** The moving highlight is a native radio group underneath — arrow keys, focus, and announcements come from the platform; only the paint animates.

**Feature test (not needed to ship):** `@supports (selector(:has(*)))`

**Topics:** interaction, motion

- [MDN: :has()](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)

### Scroll snap

A horizontal strip where each card locks to centre as you scroll — scroll-snap-type on the track, scroll-snap-align on the cards.

**Accessibility payoff:** Snap points give wheel, touch, and keyboard scrolling the same stopping places — no JS carousel intercepting arrow keys.

**Feature test (not needed to ship):** `@supports (scroll-snap-type: x mandatory)`

**Topics:** scroll

- [MDN: CSS scroll snap](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll_snap)

### :user-valid / :user-invalid

Validation styling with manners — these pseudo-classes match only after the user has actually interacted with a field, so an empty required form isn’t flagged red on first paint.

**Accessibility payoff:** Errors wait until someone has actually been in the field — no red flags shouting at screen-magnifier users before they’ve typed a character.

**Feature test (not needed to ship):** `@supports (selector(:user-valid))`

**Topics:** forms

- [MDN: :user-invalid](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid)

## Baseline: newly available

Interoperable across current engines, but older installs are still out there. Guard it, and give the fallback a real path.

### text-wrap: balance / pretty

Let the browser decide line breaks.

**Accessibility payoff:** Better line breaks with zero extra markup — nothing for assistive tech to trip on, and headings stay balanced at every zoom level.

**Guard:** `@supports (text-wrap: balance)`

**Topics:** typography

- [MDN: text-wrap](https://developer.mozilla.org/en-US/docs/Web/CSS/text-wrap)

### Popover attribute

A real actions menu wired by id alone — the platform hands you Esc-to-close, light (click-outside) dismiss, top-layer rendering, and aria-expanded toggling for free.

**Accessibility payoff:** Esc-to-close, light dismiss, and aria-expanded come with the attribute — the keyboard traps of hand-rolled dropdowns never exist.

**Guard:** No guard needed.

**Topics:** interaction

- [MDN: popover](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API)

### Native error squiggles

text-decoration-line: spelling-error and grammar-error draw the platform’s own squiggle — the marking every visitor already knows from their spell checker, rendered by the engine on real text.

**Accessibility payoff:** Error marking in the OS’s own convention — it tracks wrapped lines, survives zoom, and stays visible in forced colors where painted backgrounds are erased.

**Guard:** `@supports (text-decoration-line: spelling-error)`

**Topics:** typography, forms

- [MDN: text-decoration-line](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration-line)

### contrast-color()

The browser picks a contrasting text color for any background — user-generated colors, theming APIs, data-viz palettes stay readable without contrast math.

**Accessibility payoff:** The browser computes the readable label color for any background — text contrast becomes a guarantee, not a design-review catch.

**Guard:** `@supports (color: contrast-color(red))`

**Topics:** theming

- [MDN: contrast-color()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/contrast-color)

### Contrast-safe theming

One engine, many themes.

**Accessibility payoff:** Every derived palette flips text to black or white against its seed — a theme cannot accidentally ship unreadable text (1.4.3 by construction).

**Guard:** `@supports (color: oklch(from red l c h))`

**Topics:** theming

- [MDN: relative colors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors)
- [MDN: contrast-color()](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/contrast-color)

### Contrast-clamped theme picker

The same engine, made interactive: pick an accent and drag its lightness, and a pure-CSS clamp snaps it out of the “muddy middle” where no black/white label could reach WCAG AA.

**Accessibility payoff:** User color choices are clamped into the contrast-safe range — personalization without letting anyone pick their way into failure.

**Guard:** `@supports (color: oklch(from red l c h))`

**Topics:** theming, forms

- [MDN: relative colors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors)
- [WCAG: Contrast (Minimum) 1.4.3](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)

### Container style queries · non-color cues

Query a container’s custom-property value (not just its size) to restyle descendants with no prop drilling.

**Accessibility payoff:** Non-color cues switch on from one inherited flag — meaning stops resting on color alone (1.4.1) with no per-component code.

**Guard:** No guard needed.

**Topics:** theming

- [MDN: style queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_size_and_style_queries)
- [WCAG: Use of Color (1.4.1)](https://www.w3.org/WAI/WCAG22/Understanding/use-of-color.html)

### shape() responsive clipping

Responsive, keyword-based clip paths (lines, arcs, curves) that can use percentages and custom properties — unlike path(), which is frozen SVG coordinates.

**Accessibility payoff:** The decorative clip never touches the text flow — zoom, reflow, and screen readers meet ordinary paragraphs underneath.

**Guard:** `@supports (clip-path: shape(from 0 0, line to 100% 0))`

**Topics:** layout

- [MDN: shape()](https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape/shape)

### content-visibility

content-visibility: auto lets the browser skip rendering sections until they approach the viewport, so long pages paint fast with no virtual-scrolling JS.

**Accessibility payoff:** Fast first paint without hiding anything from anyone: skipped sections stay findable and announceable — and a lighter main thread is assistive-tech responsiveness, because the accessibility tree is walked on that same thread.

**Guard:** `@supports (content-visibility: auto)`

**Topics:** scroll, layout

- [MDN: content-visibility](https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility)
- [web.dev: content-visibility](https://web.dev/articles/content-visibility)

### @starting-style

Animate an element in from display:none — entry transitions with no JS — and out via transition-behavior: allow-discrete.

**Accessibility payoff:** Entry and exit animate without JS timers — and display:none keeps closed panels truly gone from the accessibility tree, not just faded.

**Guard:** `@supports (transition-behavior: allow-discrete)`

**Topics:** motion

- [MDN: @starting-style](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style)

### field-sizing: content

A textarea that grows and shrinks with its content — no scrollHeight measuring, no resize listeners, no JS.

**Accessibility payoff:** Inputs grow with their content — screen-magnifier users see their whole answer instead of text scrolling out of a fixed box.

**Guard:** `@supports (field-sizing: content)`

**Topics:** forms

- [MDN: field-sizing](https://developer.mozilla.org/en-US/docs/Web/CSS/field-sizing)

### CSS zoom

The newly-interoperable zoom property magnifies an element AND reflows the layout around it — like the browser’s own zoom — where transform: scale() only repaints bigger and overlaps its neighbours.

**Accessibility payoff:** zoom scales a component the way real users magnify — a one-line testing lens for reflow and text-resize criteria.

**Guard:** `@supports (zoom: 2)`

**Topics:** layout

- [MDN: zoom](https://developer.mozilla.org/en-US/docs/Web/CSS/zoom)

### Custom Highlight API

Paint ranges of text from JS via ::highlight() — styled in CSS, with no wrapper elements inserted and the accessibility tree left untouched.

**Accessibility payoff:** Highlights paint without wrapping spans — the text a screen reader reads is untouched by the visual layer.

**Guard:** Feature-detect in JS — no CSS condition expresses it.

**Topics:** typography

- [MDN: CSS Custom Highlight API](https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API)

### Invoker commands

command and commandfor on a plain <button> open, close, and toggle dialogs and popovers declaratively — the last onclick glue for the top layer, gone.

**Accessibility payoff:** Dialog and popover wiring with zero script means zero chances to forget the focus handling — the button says what it does, and the browser does it.

**Guard:** Feature-detect in JS — no CSS condition expresses it.

**Topics:** interaction

- [MDN: command attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#command)
- [Open UI: Invokers](https://open-ui.org/components/invokers.explainer/)

### View Transitions

Animate between two DOM states by giving shared elements a view-transition-name and wrapping the change in startViewTransition() — the browser tweens position, size, and cross-fade with no FLIP math or animation library.

**Accessibility payoff:** Cross-page continuity as pure enhancement — reduced-motion users get an ordinary navigation, and no JS router is required at all.

**Guard:** Feature-detect in JS — no CSS condition expresses it.

**Topics:** motion

- [MDN: View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)

### ::details-content

A JS accordion re-implements what <details> gives away free: button semantics, the announced expanded state, keyboard support.

**Accessibility payoff:** The disclosure stays a real <details> — expanded state announced free of charge — while motion is layered on as pure enhancement, gone under reduced motion.

**Guard:** `@supports (selector(::details-content))`

**Topics:** interaction, motion

- [MDN: ::details-content](https://developer.mozilla.org/en-US/docs/Web/CSS/::details-content)

## Limited availability

Not interoperable yet. Progressive enhancement only — the page must be complete without it.

### Anchor positioning

Tether a popover to the element that opened it, in pure CSS.

**Accessibility payoff:** Tethered UI repositions instead of clipping at viewport edges — magnified and small-screen users keep the attached panel on screen.

**Guard:** `@supports (anchor-name: --a)`

**Topics:** layout, interaction

- [MDN: anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)
- [Interop 2026 dashboard](https://wpt.fyi/interop-2026)

### Anchor-positioned tooltip

A hover/focus hint tethered to its trigger with anchor positioning, flipped below it by position-try-fallbacks when the top of the viewport would cut it off, with no positioning JS.

**Accessibility payoff:** Position fallbacks flip the tooltip below its trigger when the top of the viewport would clip it; it opens on keyboard focus as well as hover (2.1.1) and stays hoverable and persistent (1.4.13).

**Guard:** `@supports (anchor-name: --a)`

**Topics:** interaction

- [MDN: anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)
- [WCAG: Understanding 1.4.13](https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html)

### Scroll-driven animations

Animations driven by scroll position instead of time, with no scroll listeners.

**Accessibility payoff:** A global duration reset cannot stop a scroll timeline, so each reveal is gated on prefers-reduced-motion: no-preference, and people with vestibular disorders get stillness that actually holds (2.3.3, AAA).

**Guard:** `@supports (animation-timeline: scroll())`

**Topics:** scroll, motion

- [MDN: scroll-driven animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll-driven_animations)
- [WCAG 2.3.3 Animation from Interactions](https://www.w3.org/WAI/WCAG22/Understanding/animation-from-interactions.html)

### Rounded polygon()

An arrow tag used to be a rotated-box hack or an exported image — and the export took real text with it.

**Accessibility payoff:** Labels that used to ship as images of text stay real text (1.4.5) — zoom reflows it, translation translates it, themes re-ink it, screen readers just read it.

**Guard:** `@supports (clip-path: polygon(round 8px, 0 0, 100% 0, 50% 100%))`

**Topics:** layout, typography

- [MDN: polygon()](https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape/polygon)
- [CSS Tip: rounded shapes (Temani Afif)](https://css-tip.com/rounded-shapes/)

### corner-shape

Squircles, scoops, and notches as real border geometry — corner-shape reshapes the corners border-radius rounds, so the border, the shadow, and the default focus ring all trace the exotic outline exactly.

**Accessibility payoff:** Fancy corners without clip-path means the keyboard user’s focus ring survives intact — geometry the ring can trace instead of a cut that slices it off.

**Guard:** `@supports (corner-shape: squircle)`

**Topics:** layout, interaction

- [MDN: corner-shape](https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape)

### Typed attr()

attr() with types — read data attributes as lengths, colors, or numbers in any property, not just content.

**Accessibility payoff:** The styling reads the same attribute assistive tech could expose — one source of truth for the visual and the announced value.

**Guard:** `@supports (width: calc(attr(data-value type(<number>), 0) * 1%))`

**Topics:** layout

- [MDN: attr()](https://developer.mozilla.org/en-US/docs/Web/CSS/attr)

### Customizable <select>

A native <select> opted into full CSS styling with appearance: base-select — the control, the dropdown, and rich option content (status dots, descriptions, optgroups, a disabled option), plus a trigger that tints with the selection — all without leaving the platform.

**Accessibility payoff:** Styled options on a real <select> — the keyboard model, mobile pickers, and screen-reader semantics stay native instead of ARIA-rebuilt.

**Guard:** `@supports (appearance: base-select)`

**Topics:** forms

- [MDN: Customizable select](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select)

### Scroll-state queries

Container queries that react to how an element sits in a scroller: scroll-state(snapped), scroll-state(stuck) and scroll-state(scrollable).

**Accessibility payoff:** Edge hints that show only while there is more to scroll give overlay-scrollbar and zoomed-in readers a cue that never lies, with no scroll listener holding up assistive tech.

**Guard:** `@supports (container-type: scroll-state)`

**Topics:** scroll

- [MDN: scroll-state queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_conditional_rules/Container_scroll-state_queries)

### Pure-CSS carousel

Scroll buttons and marker dots generated entirely by CSS — focusable, named (the content slash-alt syntax is mandatory craft here), and auto-disabling at the ends.

**Accessibility payoff:** The generated buttons and dots are focusable and named — and without support it stays a keyboard-scrollable region, never a dead widget.

**Guard:** `@supports (selector(::scroll-marker))`

**Topics:** scroll, interaction

- [MDN: ::scroll-marker](https://developer.mozilla.org/en-US/docs/Web/CSS/::scroll-marker)

### Dialog & popover dismissal

Newer overlay ergonomics: a popover="hint" toggletip (show + dismiss for free) and a <dialog closedby="none"> takeover that Esc and outside-clicks can’t dismiss, for the rare must-decide moment.

**Accessibility payoff:** showModal() brings focus trapping, Esc, and an inert background from the platform — the three things hand-rolled modals get wrong.

**Guard:** `@supports (selector(:open))`

**Topics:** interaction

- [MDN: dialog closedby](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby)
- [MDN: :open](https://developer.mozilla.org/en-US/docs/Web/CSS/:open)

### Interest invokers (interestfor)

Hover, keyboard focus, or touch long-press invokes a popover from one HTML attribute — link previews and rich tooltips with the delays, Esc dismissal, and accessibility mapping supplied by the browser, no tooltip library.

**Accessibility payoff:** Hover previews with built-in delays, keyboard parity, and Esc — the hover-only tooltip failures of 1.4.13, handled by an attribute.

**Guard:** `@supports (interest-delay: 0s)`

**Topics:** interaction

- [MDN: interest-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/interest-delay)
- [Chrome Status: Interest Invokers](https://chromestatus.com/feature/4530756656562176)

### reading-flow: flex-visual

order, row-reverse, and grid placement move things visually while keyboard focus and screen-reader order stay stuck on the DOM — the classic focus-order trap.

**Accessibility payoff:** Focus and screen-reader order follow the layout users actually see — visual reordering stops breaking Focus Order (2.4.3) and Meaningful Sequence (1.3.2).

**Guard:** `@supports (reading-flow: flex-visual)`

**Topics:** layout, interaction

- [MDN: reading-flow](https://developer.mozilla.org/en-US/docs/Web/CSS/reading-flow)
