Plan step 32, found by the Browser suite. Content padding (16dp by default
since containment.md C-12) shrinks every keyline near the ends, so large
items resting at the start or the end carry a small inset. Bring-into-view
took any inset over half a pixel for "not fully open", so focusing or
pressing a large item at rest scrolled the row to it. The focal keyline's
own inset at the current scroll is the threshold now. The press test also
checks that focusing and pressing an open item moves nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 32, found by the Browser suite. Since containment.md C-18 put
the tab stop on each item, the item itself matches the selector for
interactive controls, so every press on an item read as a press on a
control inside it and never scrolled a partly hidden item into focus. A
mouse press still worked through the focus it gives the item; an activation
that clicks without focusing, as a test or assistive technology does, did
nothing. Only a control other than the item counts now.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 23, containment.md § Missing ("Carousel: the uncontained
multi-aspect-ratio layout"). `<x-carousel layout="multi-aspect">` is the
layout M3 added in November 2025: each `<x-carousel-item aspect="…">`
keeps its own ratio at the row's fixed height, held inside M3's 9:16 to
16:9 range (a square by default), with 16dp leading padding, 8dp gaps,
the extra-large corner and uncontained (default) scrolling.
The keyline machinery does not fit it: an Arrangement counts items of
one size each, and every snap position and mask follows from that size.
So the layout is a plain flex row, unmasked, and carousel.js only
measures each item's resting position off the DOM, which keeps the
previous/next buttons, the arrow keys, Home/End and bring-into-view
working. The header and SKILL.md say so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 19, containment.md C-11. The full-screen layout was another
horizontal row with a 28px corner and a mask, where M3's "shows one
edge-to-edge large item at a time and scrolls vertically". It is now a
vertical scroll-snap column: items fill the row with no corner and no
mask, 16dp apart, no end padding, the previous/next buttons point up and
down and the arrow keys are Up and Down. The row is capped at the 840px
medium window, which with the portrait rule in the header comment is as
far as CSS can hold M3's "compact and medium, portrait only". The ported
FullScreenCarouselStrategy keylines go with it: nothing measures them
any more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 19, containment.md C-18. The row was the focusable `region`
and the items were not focusable at all, which is the thing M3's
accessibility page draws a Don't for: "use Tab to place initial focus on
the first carousel item", "avoid focusing on the carousel container".
Each item is now `tabindex="0"` with the focus ring drawn inside it, the
row is out of the tab order, and from a focused item the arrows move one
item (moving focus with them), Home and End go to the ends, and Space or
Enter opens one that is not fully in view.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 19, containment.md C-12 and C-25. `padding` defaulted to 0
where M3's specs table gives every layout 16dp of leading and trailing
padding — leading only for `uncontained`, none for the edge-to-edge
`full-screen` — so the default is 16 and each layout now says which
ends it applies to (`data-padding`, `data-padding-end`). The row gains
the 8dp above and below that goes with it, without shortening the
items. The overlay label is `inverse-on-surface` instead of a literal
`white`, which no scheme or contrast profile follows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 12, containment.md C-05. Only the parallax half of M3's rule
was honoured: the mask was still written every frame, so items kept
growing and shrinking between the keylines. Under reduced motion the
inset, the shift and the label fade are now all zero, so every item
stays at the strategy's large size and the keylines only decide where
the row snaps. `--material-carousel-pin` went with it: with no mask
there is nothing to pin the content to.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
CI's WebKit twice left the carousel on its first item after Next. A
scroll started by the buttons or keys that comes to rest anywhere but
its item is now sent there once more; a scroll the person starts
themselves cancels that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
Multi-browse, hero, uncontained and full-screen layouts on a native
scroll-snap row, with Compose's keyline maths masking each item as it
scrolls. Completes Phase 5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy