The backdrop is ruled with the root element's baselines, one every 1rlh. Body text with nothing else in it sits on these lines by itself. Adding headings, images of arbitrary height, quotes with a border and other elements knocks the text that follows off the lines, and two neighbouring cards stop matching line for line.
block-step: 1rlh margin-box start rounds the outer size of such blocks up to a multiple of the step, and the text after them lands on the lines again, the same way in both columns. No browser supports it yet, so the technique switcher adds three ways of getting the same result today next to the native mode: a script, a scroll-driven animations hack, and calc-size(). What each of them costs is described below the columns.
Rhythm technique
Rhythm and images
Even the first headings of the two columns, by taking a different number of lines, already knock the paragraphs after them off the rhythm, which is especially visible next to the neighbouring column. Further down one column has an illustration and the other a quote, and their heights differ.
The image above is fitted to the rhythm by the same rule: nine lines plus 1cap, bottom on a baseline, the excess cropped.
A subheading further down
The subheading is set in a different size, and its line is taller than a body line. Without rounding it also shifts everything after it by a fraction of a line.
Rhythm and headings that wrap onto two lines
The heading of this card takes two lines, the heading of the neighbouring one takes one. That alone is enough for the columns to drift apart from here on.
An image should stand with its bottom on a baseline, and its height should be a whole number of lines plus the cap height. Then the rectangle of the image and the rectangle of the text opposite feel the same size.
The eye reads the top of a text block along the cap line: almost every first line starts with a capital, and ascenders often support that line too. That is why the image gets 1cap rather than 1ex, and the excess is cropped with cover fitting.
A subheading somewhere else
The image on the left follows this rule: three lines plus 1cap, bottom on a baseline. Its outer size is deliberately not a multiple of the step, short by exactly the distance from the top of the line to the baseline, and rounding makes up that remainder. For a floated image the cost of an error is higher: until the remainder is made up, both the lines beside it and the lines below it land on different baselines.
no rhythm
Plain CSS: headings, images and quotes have their own heights and margins. This is also how block-step would look in a browser that does not understand it.
native: block-step
block-step: 1rlh margin-box start on every rhythm breaker, one line and no wrappers. So far it only works in a WebKit build with the cssRhythmicSizingEnabled flag on; in other browsers this mode changes nothing.
script
The same computation in JavaScript: a ResizeObserver plus a recalculation of margins on every resize and after fonts load. The script reads --block-step-size, --block-step-insert, --block-step-align and --block-step-round from the block, measures the outer size of the box together with its own margins, and distributes the difference by the rules of the editor's draft. This is what authors have to write today, and at the same time an “implementation” the arithmetic of the WPT tests is checked against.
scroll-driven animations
Not a line of JavaScript in the layout. The height of every rhythm breaker is measured by a view timeline: two animations on the same timeline with different ranges yield two numbers, and the difference between them is the size of the element. The subject is the block's own ::before, stretched over its whole padding-box. Then round(up, …) rounds the size up to 1rlh, and the difference goes into the margins: below, or split evenly between the two sides, depending on --bs-align. The price:
the block is forced into overflow: hidden and position: relative, and its ::before is taken by the subject;
replaced elements cannot use the trick: <img> has no pseudo-element and needs a wrapper, which is why the images on this page sit inside figure;
the padding-box is what gets measured, so the block's own margins and block borders have to be declared as variables by hand, and also because the hack sets the margins itself and would overwrite them;
it does not understand keywords like center: the share has to be multiplied by the length right inside calc(). Inserting the extra as padding is impossible altogether, since the subject is stretched over the padding-box and the padding would end up in its own measurement;
precision is limited by the “resolution” of the measurement, about 0.1px;
it takes a lot of code, involving @property, timeline-scope, view timelines and round(); scroll-driven animations are not Baseline yet, and Firefox ships them disabled by default.
calc-size()
block-size: calc-size(auto, round(up, size, 1rlh)) rounds the block's own height up to a multiple of the step. No wrappers and no script, but it still falls short of block-step:
only the content height is rounded, not the outer size: the block's own margin and padding have to be written into the formula as known constants (round(up, size + 0.7rlh, 1rlh) - 0.7rlh), otherwise a whole extra step is added to the rounded height and the gaps between blocks double; there is no equivalent of block-step-insert: margin-box;
the extra ends up inside the box rather than in the margins. Placing the content inside the way block-step-align would is possible through align-content, with the same keyword. But on a box with a background or a border the difference would stay visible: with block-step-insert: margin-box the extra stays outside the background, here it is under it;
an explicit height breaks replaced elements: an image with calc-size() gets stretched, so it needs a wrapper instead;
calc-size() itself currently exists only in Chromium, and it has been submitted to Interop 2027 as a separate proposal too.