v157 · css · scroll snap · writing modes

Writing-mode hand-off

scroll-snap-align speaks logical axes (block, inline); scrollers move on physical ones (x, y). The spec resolves a snap area’s logical alignment into physical axes using its nearest scroll container’s writing mode — and the new propagation step hands any unconsumed axis up the tree as a physical requirement, so an ancestor applies it in its own coordinate space regardless of writing-mode differences. The resolution half works in every current Chrome; this page lets you drive it.

The two-axis resolution playground below uses only shipped scroll snap, so it works in this browser today. The hand-off step itself — a single-axis container passing the unconsumed physical axis to an ancestor — needs the Chrome 157 behaviour; see the propagation probe for the live detection, and the flag steps on that page.

Resolution playground

One 3×3 grid of snap areas in a two-axis scroll-snap-type: both mandatory scroller. Flip the scroller’s writing mode and alignment, then press measure: the readout reports the physical (x, y) offsets the browser actually settled on, so you can watch the same logical declaration land on different physical axes.

writing-mode:
scroll-snap-align:
Not measured yet. The measure button sets scrollLeft/scrollTop 33px off a cell corner and reports the physical position mandatory snap pulls it back to.

What propagation hands upward

In the Chrome 157 model, resolution happens once, at the nearest scroll container, and the leftover axis travels as a physical requirement:

Logical-to-physical hand-off for a card inside a single-axis lane
lane writing-modelane scrollscard’s inline alignmentcard’s block alignment
horizontal-tbx (inline)consumed by the lane (x)propagates as a physical y requirement
vertical-rly (inline)consumed by the lane (y)propagates as a physical x requirement

An ancestor that scrolls the propagated physical axis applies the alignment directly in its own coordinate space — even if its own writing mode differs. That is why the spec resolves logical values before the hand-off instead of re-interpreting them per ancestor.

the code

/* The playground scroller — all shipped syntax */
.snapbox { overflow: auto; scroll-snap-type: both mandatory; }
.cell    { scroll-snap-align: start; }  /* logical: block + inline */

/* vertical-rl flips which physical axis "inline" means — the same
   declaration now snaps x where it used to snap y, and vice versa. */
.snapbox { writing-mode: vertical-rl; }

see also