v155 · css · box model
margin-trim
Children carry margins so they sit apart from each other. The first and last one then push against the container's padding, which nobody asked for — so every stylesheet grows a :first-child { margin-block-start: 0 }. margin-trim moves that decision to the container, where it belongs: the container says which edges to trim, and the children keep their margins.
concepts
-
Trim explorer
Every value on a live container, with the gaps measured from the rendered boxes rather than described. Switch between block and inline flow to see which edges each keyword actually affects.
-
The workarounds it retires
Four ways stylesheets have solved this —
:first-childresets,:last-childresets, the owl selector, negative container margins — each running side by side against the same markup, with what each one breaks. -
Multicol columns
Where it earns its keep: in a multi-column container the first element in every column needs the trim, and no selector can express "first in column" because the browser decides where columns break.
why it shipped
The :first-child reset is a workaround with three known failure modes. It targets the first element, which may not be the first one that is visible once something is display: none. It has to be repeated for every component that composes children. And it cannot express "first in this column" or "first after a fragmentation break", because those positions are decided during layout, long after selectors have matched.
Putting the control on the container fixes all three: the browser trims the margin at the edge it is actually laying out, whenever and wherever that edge turns out to be. Note that the spec previously extended this to flex and grid containers and no longer does — this is block containers and multicol.
the API
.prose {
padding: 24px;
margin-trim: block; /* trim block-start and block-end */
}
/* One edge only. */
.stack { margin-trim: block-end; }
/* Inline flow, for a row of inline-level children. */
.tags { margin-trim: inline; }
The children are untouched: they keep declaring the margin that separates them from a sibling, and the container removes it only where it meets the container's own edge.