Repository navigation
Layout
Layout is a pure function of the styled tree and the viewport: styles in, an immutable fragment tree out, in page coordinates. Painting consumes fragments only.
- Block containers stack their block children; inline content is broken into line boxes where the Unicode line breaking algorithm allows (the bullet after next), whitespace collapsing per
white-space(normal,nowrap,pre,pre-wrap,pre-line,break-spaces), andtext-align—justifyshares the room a line did not use out among the spaces between its words, andstartandendbecome sides once the block'sdirectionsays which end its content runs from (or, underdir="auto"andunicode-bidi: plaintext, once the text's own first strongly directional character does), leaving the last line of the block, and any line ended by a forced break, wherevertext-align-lastputs it (by default the start edge, andjustify-allis how a block asks for its last line to be stretched with the rest).text-justify: noneturns justification off. -
text-indentnarrows the first line by the amount it is given, taken off the end that line starts at: the left in a left-to-right block, the right in a right-to-left one. Underdir="auto"orunicode-bidi: plaintextit is the paragraph's own direction that says which. A box sized to its content makes room for it: the indent lengthens the first line of amax-contentwidth, and the first piece of amin-contentone. -
Where a line may end is what UAX #14 says of the paragraph's text — Unicode 16, every rule, scored on the conformance file (see Testing): after a space or a hyphen, between ideographs, after the slash of a URL, never inside a number or in front of a closing bracket — and a word is cut into the pieces between its opportunities before it is laid out. The edge of an inline box is no such place, so
un<b>believable</b>moves to the next line whole when its second half does not fit, the line backing up to where it last could have ended. A zero width space is an opportunity, a word joiner takes one away, and neither is drawn. So is a soft hyphen, and a line that ends at one ends with a hyphen —hyphenate-character, else U+2010 where the font has it and a hyphen-minus where it does not — that has to fit, or the line ends at the place before it;hyphens: nonetakes the opportunity away, andword-break: auto-phraseholds it back until a word cannot fit a whole line without it. CSS tailors the rules:word-break(break-all,keep-all,break-word,manual),line-break(strict,normal,loose,anywhere; the language-conditioned part oflooseis not written) andoverflow-wrap(normallets a word too wide for a line overflow, as browsers do;break-wordandanywhereslice it at the last glyph that fits, and onlyanywherecounts the slices toward amin-contentwidth). A preserved space at a line's end hangs past it underpre-wrap— counted for neither the fit nor the alignment, except before a forced break, where it counts when it fits — and so does an ideographic or other space separator under every value butbreak-spaces, where spaces take their room and a line may end after any of them. Thai, Lao, Khmer and Myanmar, whose words a dictionary finds, break between clusters for want of one. - Margins collapse per CSS 2.1 §8.3.1: between siblings, through a parent's open top and bottom edges, and through empty boxes; a cleared box keeps its margin inside, and an empty box holding a float ends the walk. The root's margins collapse with nothing: its top margin is room above the page's first box.
- The room a width leaves over goes to the margins (§10.3.3): both auto share it, which centres the box; one auto takes all of it, which is how a box is pushed to the far end. With neither auto the equation is over-constrained and a margin is ignored and solved for instead — the one at the end the content runs towards, so a box narrower than its containing block settles against the right edge when that containing block reads right to left, and one too wide overflows the left. It is the containing block's
directionthat decides this, not the box's own. There is no room to give away when the box is already the wider of the two, so an auto margin is zero there rather than pulling the box off the start edge. - Percentage widths resolve against the containing block; percentage heights (and
min-height,max-height) against a definite containing height — a written length, a percentage of a definite height in turn, a flex line's settled size, an absolutely positioned box's containing block, or the viewport for the root — and stayautowithout one. -
min-width,max-width,min-heightandmax-heighthold blocks, floats, flex items and replaced boxes. -
box-sizing:content-box(the initial value) andborder-box, under which a writtenwidth,height,min-/max-size or flex basis names the border box — the padding and the borders come off it, and the content floors at zero when they are wider than the size written. A form control is sized whole either way: its own border and padding live inside the size it reports. - Lists number themselves with the
list-itemcounter (css-lists-3): everydisplay: list-itembox adds one to it,<ol>and<ul>reset it, andstart,valueandtypeare read as the counter and list-style properties they stand for, so an author’s owncounter-reset: list-itemon a list changes the numbering. Markers: the glyphs (disc, circle, square), the numbering systems (decimal, decimal-leading-zero, lower and upper roman, alphabetic under both the-alphaand-latinnames, lower greek, armenian, georgian) andnone.list-style-position: outsidehangs the marker to the left of the item’s box, where a list with its padding zeroed can lose it off the edge of the page — as browsers do;insidemakes it the first thing on the item’s first line, so an item holding nothing else still shows its marker and makes a line for it.
- An inline box draws its own background and border and takes room for its margin, padding and border where it opens and where it closes. Broken over several lines it is one box sliced: the line it carries on to draws neither the edge it never reached nor the one it has not come to. Broken around a block it is two complete boxes, each with all four of its edges.
- A block-level box inside an inline box splits the inline box around it and lays out as a block child of the containing block (CSS 2.1 §9.2.1.1): a card inside a link keeps its border, padding and background.
- The line box follows CSS 2.1 §10.8: every box on the line reaches above and below the baseline it is aligned to — the strut of the block's font, text by its ascent and descent within its line height, a picture by its height, an inline-block by its own ascent and descent — and the line runs from the uppermost top to the lowermost bottom. Down a vertical page a picture has no edge to stand on, so it is set astride the line by its middle instead, and a line holding one picture measures the picture and nothing more.
-
vertical-alignon inline boxes and atomic boxes alike:baseline,sub,super,text-top,text-bottom,middle,top,bottom, a length, or a percentage of the box's own line height. A run inside nested spans takes the alignment of the nearest raised or lowered inline box around it;topandbottomboxes sit at the line's edges and grow the line when taller. -
line-height: normalis what the face asks for (its ascent, descent and line gap); a written line height places the baseline by the half-leading model.
display: inline-block, inline-flex and inline-grid (and inline-table until tables land) lay out as one atomic box on the line: shrink-to-fit wide like a float, the margin box what the line holds, sitting on the baseline of its last line box — its bottom margin edge when it has none, and the higher of the two when it clips its overflow, as CSS 2.2 has it. An absolutely positioned inline-block becomes a block with an inline static position.
-
float: leftandfloat: right, placed at their point in flow — mid-line too — no higher than an earlier float, below what their ownclearnames, then down until they fit beside the floats at that height. -
clearon blocks and on<br>(the CSS property or theclearattribute). - Auto widths shrink to fit from the min-content and max-content widths of the float's contents.
-
Bidirectional text — the items of a line carry the embedding level the Unicode bidirectional algorithm gave them (see Testing for how that is scored), a word whose characters do not all share a level is cut where it changes — and a cut is no place to end a line — and rule L2 settles the order the entries are drawn in. A run that reads right to left has its characters reversed and its brackets mirrored (L4), so a parenthesized aside still reads as one. An inline box that opens no level of its own is invisible to the algorithm and belongs to the run beside it; one that does spells the controls its
unicode-bidinames (isolate-overridean isolate with an override inside,plaintextan isolate that reads its first strong character), and an override on a block holds all of its inline content to the block's direction. The formatting characters written in the text itself — the embeddings, overrides and isolates with their pops, and the marks — are read the same way and drawn as nothing, and a preserved tab reaches the algorithm as the segment separator it is. An inline box's margin, border and padding stand at the ends of its content in visual order, the side it opens on being the one its own direction starts from (CSS 2.1 §8.6). HTML's user-agent rules make an element with adirattribute an isolate,<bdo>an isolate-override, and<bdi>an isolate that takes the direction of its first strong character unless it states one. - A box can ask for those sizes by name:
width,min-width,max-widthandflex-basisreadmin-content(the widest piece that cannot break),max-content(everything on one line) andfit-content(the narrowest held up to the room, never past the widest — the same formula shrink-to-fit uses). A block sized that way no longer fills its container, so auto margins centre it, and two offsets no longer stretch an absolutely positioned one. - Boxes with their own formatting context (
overflowother thanvisible,display: flow-root, flex containers, inline-blocks, floats themselves) contain their floats and sit beside outside floats — narrowed, or below them when a written width does not fit. - Line boxes and formatting-context boxes keep clear of floats along their whole height (CSS 2.1 §9.5), not only at their top: a float starting lower narrows the line, or moves it below the next float when its content no longer fits.
display: flex and inline-flex: the items (element children in order, the container's own text as anonymous items — an absolutely positioned child between two runs of text parts them into two, in a grid as here — order sorted), each item's flex base size and automatic minimum, lines (flex-wrap, both directions), flexible lengths (grow into free space or shrink out of overflow, weighted, never below the minimum, with items frozen at their minimum and the rest re-shared), cross sizes, align-items/align-self (stretch, flex-start, flex-end, center; baseline as flex-start for now), align-content across wrapped lines, justify-content (all six values, the reverse directions swapping the ends), row-gap/column-gap/gap, flex-direction in all four directions, and the flex and flex-flow shorthands. A single-line container gives its line the container's cross size; a wrapping container whose items fit one line keeps that line its items' size.
Either axis can run backwards. A row's main axis is the inline axis, so direction: rtl reverses it and the first item is the rightmost — and row-reverse on top of that cancels it. A column's main axis runs down whichever way the text reads, and it is that column's cross axis that is the inline one, so a right-to-left column lays its items out from the right edge. flex-wrap: wrap-reverse swaps the cross ends on top of either, which is what turns align-items: flex-start into the far side and packs the lines against the other edge.
An auto margin on an item takes the room its line has left over — shared equally among however many say auto, before justify-content is consulted at all, which is what puts one item at the far end of a bar and leaves the rest where they are. Across the line an auto margin takes the leftover the same way and the item's own alignment is not consulted. flex-basis: content sizes from the content rather than falling back to the width property. Baseline alignment is not written.
display: grid and inline-grid, css-grid-1 without subgrid and masonry:
-
The explicit grid —
grid-template-columnsandgrid-template-rowstrack lists: lengths and percentages,fr,auto,min-content,max-content,minmax(),fit-content(),repeat()with a count orauto-fill/auto-fit(as many repetitions as fit;auto-fitcollapses the empty ones), and line names in brackets;grid-template-areas(each area a rectangle, or the declaration is dropped) with the-start/-endline names it implies; thegrid-templateandgridshorthands,grid-auto-flowincluded. -
Placement —
grid-row,grid-column,grid-areaand their longhands: line numbers (negative from the end), the nth line with a name, a name alone (its area's edge, else the first so named),spanby count or to a named line; lines outside the explicit grid make implicit tracks, sized bygrid-auto-rows/grid-auto-columns(the pattern runs forward after the grid and backward before it). Auto-placement (css-grid-1 §8.5) in row or column flow, sparse ordense, inorder-modified document order; the container's own text becomes anonymous items, its::before/::afterboxes items placed by their own properties. -
Track sizing — the algorithm of css-grid-1 §11: base sizes and growth limits from the items' minimum, min-content and max-content contributions (single-track items first, then spanning items by span, then items crossing flexible tracks), the free space maximizing the tracks,
frshares found by the fr-size search, andautotracks stretched byjustify-content/align-content: normalorstretch. Percentage tracks and gaps resolve against the container's content box; under an indefinite height they count as auto while the content height is found, then resolve against it and the rows are sized again. -
Alignment —
justify-contentandalign-contentdistribute the free space between the tracks (start,end,center,space-between,space-around,space-evenly,stretch);justify-items/justify-selfandalign-items/align-selfplace an item in its area (stretch,start,end,center;normalstretches a block and keeps a picture or control at its own size; the items of a row that ask forbaselineshare one, each measured at the width it is about to have and the rest pushed down to meet the furthest, with the shift counted towards the row's own size so an automatic row comes out as tall as that baseline plus the deepest descent below one;justify-self: baselinedown a column isstartfor now); auto margins take the free space first;place-items,place-selfandplace-content. A grid item's percentages resolve against its area.z-indexon a grid (or flex) item orders it among its container's positioned boxes. -
Columns that run from the right — a grid whose
directionisrtlreads its column axis from the content box's other end, so column one is the rightmost and the roomjustify-contentleaves over is at the left. It is done by mirroring the areas once they are settled, which leaves track sizing, placement and the free-space share-out exactly as they were; where an item sits inside its area is settled in physical offsets, sojustify-self: startis separately swapped to mean the area's right edge. An absolutely positioned child placed by grid lines gets the mirrored area as its containing block. -
Sizing the container — an inline-grid, a float or a flex item that holds a grid shrinks to fit: the columns sized under a min-content and a max-content constraint, gaps included.
-
Absolute positioning (css-grid-2 §9) — an absolutely positioned child of a grid container is not a grid item, but when the container is also its containing block its placement still names a grid area, and that area is the containing block: an offset counts from the area's edge, a box with auto offsets stands at the area's corner and shrinks to fit, and a box stretched between opposite offsets fills the area. A property naming a line the grid does not have counts as
auto, and anautoedge stays on the container's padding edge, so one axis can be the area's and the other the padding box's.
Baseline alignment, subgrid and grid-lanes are not written.
Layout reads a box's inline axis as its x and its block axis as its y, which is what a horizontal writing mode means. A vertical one — vertical-rl, vertical-lr, sideways-rl, sideways-lr — is laid out through styles whose physical edges have been permuted into that frame: the inline-start edge answers to left, the block-start edge to top, and a height stands where a width would. Every algorithm above then runs unchanged, and the fragments are turned back into page coordinates once the frame is finished. The properties already written in flow-relative terms — the flex and grid axes, the gutters — and the line-relative ones — float, clear, text-align, vertical-align — are in the frame as they stand and are left alone.
The lines stack from the viewport's right edge in the -rl modes, which is where a reader of one of them starts, and a page too wide for the viewport runs off to the left, as it does in a browser opened at the top of the document. The document's mode is the root's, taken from its body child where there is one.
A box whose mode is not the one around it starts an orthogonal flow. Its own margins, borders and size belong to the formatting context it sits in; only what is inside it turns. The room its lines have is its own extent along the outer block axis, and where that is not written the specification names a fallback rather than leaving it circular — the initial containing block's size in the box's inline axis, which the content then shrinks to fit within. What the box meets the outer flow with is its own block size, and an auto block size is the size of the content, never the room its container has.
Text turns with the frame: every glyph a quarter circle, clockwise in the modes that read downwards and the other way in sideways-lr. vertical-lr is the one mode whose ascenders face its block end rather than its start, so a line laid out in that frame is turned over inside its own box, which is what lands the turned ink back where the line reserved room for it.
-
position: relativeshifts a box after layout without touching the flow — a block, a float, a flex or grid item, an inline box, or an inline replaced box such as a picture, each taking its own content with it while the content after it stays where the flow put it. -
position: stickykeeps a box inside what its offsets leave of the scrollport it is in, without letting it leave the block that contains it. The scrollport is the padding box of the nearest ancestor that scrolls or clips, or the viewport where there is none; it is settled in a pass over the finished tree, so what is inside a box that moves travels with it — an inline one included, since its words now travel with its box (Painting and text). What this shows is what a reader sees on opening the page: a box whose offset holds it clear of an edge it already sits past is moved to meet that edge, as far as its containing block allows and no further, and an offset already satisfied moves nothing. It is settled once, when the page is laid out, so a box does not re-stick while a scrollport is being moved. - A
position: fixedbox is placed against the viewport, and the viewport alone clips it and says where it can be pressed — but it is painted where it stands in the tree: its box is kept under the nearest ancestor that forms a stacking context, so a dialog written inside its backdrop is drawn, and pressed, over that backdrop. Fixed boxes first met while another is being placed (a page whosebodyis fixed to hold it still under a dialog, the dialog fixed inside a fixed backdrop — the way a consent dialog is written) are placed in turn until none is left. -
position: absoluteandfixed: the box is placed in the nearest positioned ancestor's padding box (or the viewport) by its offsets — width as written, stretched betweenleftandright, else shrink-to-fit in what the offsets leave; height likewise; over-constrained boxes solve their auto margins; a box with no offsets stands at its static position, where it would have been in flow. A box that was written as inline-level begins where its line had reached, aftertext-alignhas placed that line in the room the floats leave; a block-level one spans the block's content box and takes neither the first line's indent nor a float's room.justify-selfandalign-selfplace a box given a grid area inside that area, withsafehanding the start edge back to one that does not fit. Replaced boxes keep their own size; the offsets place them without stretching. - Painting follows CSS 2.1 Appendix E: a positioned box with a
z-indexis a stacking context painted as one unit at its level; negative levels go under the context's own flow.
visibility: hidden keeps a box's room and paints nothing (a visible descendant paints). opacity below one paints the box and everything in it as one group at that alpha, and opacity: 0 hides it outright (Painting and text). overflow other than visible clips what a box contains to its padding box, positioned descendants included when the box is their containing block; auto, scroll and hidden also make the box a scroll container, and layout settles how far its content can be moved to reach what the clip hides (The shell). clip clips and nothing more.
The translations of transform (translate, translateX, translateY, translate3d, added up along a list) and of the translate property move a box after layout, percentages of its own size; any transform makes the box a stacking context. Rotations, scales and skews are passed over, leaving the box where it is.
- Pictures are sized by CSS, by their
width/heightattributes, or by themselves, the ratio kept; a picture wider than its container is as wide as it was made and overflows, as in every browser —max-widthis what holds one to its column, and a sprite sheet atwidth: 300%inside a box that clips is meant to be three boxes wide. The attributes reach layout through the cascade, as the presentational hints they are, and nowhere else:height: autoon a picture as wide as its column is the author's word and wins overheight="1080"— the way nearly every page sizes its pictures — while until the picture has arrived the two attributes give the box the shape it will have, so the page does not jump. Acanvasis as large as its attributes say.srcset,sizesand<picture>choose the source for the viewport (see Painting and text). -
aspect-ratio(auto, a ratio, or both): a replaced box with one side given takes the other from the ratio — the style's, unless it saysautoas well and the content has a ratio of its own — and an ordinary block with no height of its own takes it from its width, of the content boxes or, underborder-box, of the border boxes. That height is a definite one, so a child'sheight: 100%is a percentage of it; while the box lets its content show past it, what is inside can still make it taller (the automatic minimum). -
object-fit(fill,contain,cover,none,scale-down) andobject-positionlay an<img>'s picture into its content box: at the shape it keeps, placed by the position — a percentage lining that point of the picture up with the same point of the box — and shown through the box, the rest cut off. - An
iframe,canvas,video,embedorobjectis a box of its own too — 300 by 150 when nothing sizes it, as CSS 2.1 §10.3.2 has it — and none of them show their children, except anobjectshowing its fallback content, which is an ordinary element with its children laid out (Scripting has how an object or embed decides what it shows). Anembedthat shows nothing, or has not decided yet, has no size of its own, so only a size written for it holds, as Chrome and Firefox measure it; anobjectorembedshowing a picture is drawn as animgis, and one showing a document draws it as aniframedoes. A page laid out without scripts has made no decisions, and its objects and embeds stay the boxes described first. Aniframewears the 2px inset border HTML's rendering section gives it (frameborder="0", or aframeborderthat is no number, takes it away) and shows its document inside: laid out and painted at the size of its content box, which is that document's viewport —100vhis the frame's height — on a canvas left transparent unless the document colours it, so the frame's own background shows through. A frame's own frames are drawn the same way; Security defaults has the rules a frame is held to, and Not written yet what a frame does not do. - An inline
<svg>is a replaced box drawn from its own content (Painting and text): sized by CSS, else by itswidth/heightattributes, else by itsviewBox's ratio — with only a ratio it takes the width it has and follows the ratio down, which is what a responsive picture wants — else 300 by 150. AviewBoxwith a zero side draws nothing, as the SVG specification says. - On a line, a replaced box carries its margins, border and padding; the picture sits inside them and the bottom margin edge rests on the baseline.
Text fields, password fields, textareas, checkboxes, radio buttons, selects and buttons lay out as atomic boxes, sized by their kind (size, cols/rows, the caption, the widest option) or by CSS, with their margins around them; block-level controls stand on their own, a written percentage width taken of the containing block. A control with a line of text in it — a field, a button, a select — stands on its line by that text's baseline, as an inline-block does by its last line's, so a label and its field read along one line; a checkbox, a radio button and a text area stand by their bottom edge. The author's padding is part of the control's box and what its text stands in from: a button or a select is sized border-box as browsers' own sheets have it, a text field content-box, so a written width is the box or the box without the padding accordingly. An empty field shows its placeholder in its own color at a little over half strength; a field not being typed into shows its value from the start, one that is keeps its caret in view; and the text is cut to the width the field ends up with, a flex line's included. See The shell for how they behave.
CSS 2.1 §17, the separated border model:
-
Structure — every
displayvalue of the table model:table,inline-table, the row groups (theadfirst andtfootlast, wherever they stand), rows, cells withcolspanandrowspan(arowspanof 0 reaches the end of its group), columns and column groups withspan, captions (caption-side: toporbottom). The anonymous boxes of §17.2.1 are generated: a run of table parts outside a table gets an anonymous table around it (an inline-table inside inline content), loose content in a table or a row group gets an anonymous row, and loose content in a row an anonymous cell, blank text between parts dropped. -
Widths — the automatic layout of §17.5.2.2: each column's minimum and maximum from its cells' content and written widths, spanning cells widening the columns they span in proportion, percentage columns taking their share first, written widths holding, the other columns sharing the rest — their maximums when it fits, a point between their minimums and maximums when not. A table's width is what it is written to be (an HTML
table's border box; another element's content box), else it shrinks to fit the room;table-layout: fixedtakes the widths of the columns and of the first row's cells and shares the rest equally.border-spacingand the table's border and padding count in. -
Heights — a row is as tall as its tallest cell, the baseline-aligned ones lined up first; a spanning cell that needs more gets it from the last row it spans; a written row or cell height is a floor; a written table height stretches the rows in proportion. Cells sit in their rows by
vertical-align(top,middle— the row groups' default —bottom,baseline). -
Column order — column one of a right-to-left table is the rightmost (§17.2), and it is the table's own
directionthat says so, not the direction of whatever contains it. The widths, the spacing and the spans are settled in column order and the run is then mirrored, so a spanning cell keeps its columns together and a column's background follows it across. - Painting — the table's border and background, the column groups' and columns' backgrounds under the rows, the row groups' and rows' under the cells, and each cell's own border and background; the table's baseline is its first row's.
-
Presentational attributes — the HTML rendering section's mappings arrive as author declarations of zero specificity:
width,height,bgcolor,align,valign,nowrapon the table parts (andalign/bgcolor/textonbody,div,p, headings,hr,imgwithhspace/vspace/border,fontwithcolor/size/face), and a table'scellspacing,cellpaddingandborder, the last two reaching its cells.
The collapsing border model (border-collapse: collapse, CSS 2.1 §17.6.2) lays out with no gutters and no table padding, and the borders merge. At every segment of every grid line the table, its column groups, columns, row groups, rows and cells all offer a border, and §17.6.2.1 picks one: a hidden border takes the line away outright, none loses to everything, otherwise the widest wins and equal widths go to the box that comes first in the list cell, row, row group, column, column group, table. The winner runs down the middle of the line and the boxes on either side each keep half of it, so the shared line is drawn once at its full width; a box that spans takes the widest segment along each of its edges, and the table box keeps the outer half of its outermost lines. empty-cells: hide and visibility: collapse on rows and columns are not written.
sashfold --render page.html -o out.png --dump-layout
prints every fragment — tag, id and class, position, size, baseline, and flags for floats, positioned boxes and stacking contexts — and every text run with its position, baseline and width.
Getting started
What works today
Measured
Design decisions
Testing and tools