|
| 1 | +# CSS Layout API |
| 2 | + |
| 3 | +The CSS Layout API is designed to give authors the ability to write their own layout algorithms in |
| 4 | +additon to the native ones user agents ship with today. |
| 5 | + |
| 6 | +For example the user agents currently ship with |
| 7 | + - Block Flow Layout |
| 8 | + - Flexbox Layout |
| 9 | + |
| 10 | +With the CSS Layout API, authors could write their own layouts which implement |
| 11 | + - Constraint based layouts |
| 12 | + - Masonary layouts |
| 13 | + - Line spacing + snapping |
| 14 | + |
| 15 | +### Concepts |
| 16 | + |
| 17 | +##### The `Box` |
| 18 | + |
| 19 | +A `Box` refers to a CSS box, that is a node that has some sort of style. This can refer to: |
| 20 | + |
| 21 | + - An element with an associated style, (an element that has `display: none` for these purposes does |
| 22 | + not have a style). |
| 23 | + |
| 24 | + - The `::before` and `::after` pseudo elements with an associated style, (note for layout purposes |
| 25 | + the `::first-letter`, `::first-line`, `::selection` are *not* independent boxes, they are more a |
| 26 | + special kind of selector that can override style on *part* of another box). |
| 27 | + |
| 28 | + - A `TextNode` with some style. |
| 29 | + |
| 30 | +This is effectively the DOM tree but with some extra things. One important thing to node is that a |
| 31 | +`Box` doesn't have any layout information, it is the _input_ to layout. |
| 32 | + |
| 33 | +For the layout API specifically a box is represented like: |
| 34 | + |
| 35 | +```webidl |
| 36 | +interface Box { |
| 37 | + readonly attribute StylePropertyMapReadonly styleMap; |
| 38 | + FragmentRequest doLayout(ConstraintSpace space, OpaqueBreakToken breakToken); |
| 39 | +}; |
| 40 | +``` |
| 41 | + |
| 42 | +The `styleMap` contains the required computed style for that `Box`. |
| 43 | + |
| 44 | +##### The `Fragment` |
| 45 | + |
| 46 | +A `Fragment` refers to a CSS fragment, that is it is the part of the layout result of a box. This |
| 47 | +could be for example: |
| 48 | + |
| 49 | + - A whole box which has undergone layout. E.g. the result of laying out an `<img>` tag. |
| 50 | + |
| 51 | + - A portion of a box which has undergone layout. E.g. the result of laying out the first column of |
| 52 | + a multicol layout. `<div style="columns: 3"></div>` |
| 53 | + |
| 54 | + - A portion of a `TextNode` which has undergone layout, for example the first line, or the first |
| 55 | + portion of a line with the same style. |
| 56 | + |
| 57 | + - The `::first-letter` fragment of a `TextNode`. |
| 58 | + |
| 59 | +One can think of this as the _leaf_ representation you can get out of: |
| 60 | +```js |
| 61 | +let range = document.createRange(); |
| 62 | +range.selectNode(element); |
| 63 | +console.log(range.getClientRects()); |
| 64 | +``` |
| 65 | + |
| 66 | +For the layout API specifically a fragment is represented like: |
| 67 | + |
| 68 | +```webidl |
| 69 | +interface Fragment { |
| 70 | + readonly attribute double inlineSize; |
| 71 | + readonly attribute double blockSize; |
| 72 | +
|
| 73 | + attribute double inlineStart; // inlineOffset instead? |
| 74 | + attribute double blockStart; |
| 75 | +
|
| 76 | + readonly attribute sequence<Box> unpositionedBoxes; |
| 77 | +
|
| 78 | + readonly attribute OpaqueBreakToken? breakToken; |
| 79 | +
|
| 80 | + readonly attribute BaselineOffset dominantBaseline; |
| 81 | + readonly attribute BaselineOffset? ideographicBaseline; |
| 82 | + // other baselines go here. |
| 83 | +}; |
| 84 | +``` |
| 85 | + |
| 86 | +### Performing Layout |
| 87 | + |
| 88 | +The Layout API is best described with a simple dummy example: |
| 89 | + |
| 90 | +```js |
| 91 | +registerLayout('really-basic-block', class { |
| 92 | + *layout(constraintSpace, children, styleMap, opt_breakToken) { |
| 93 | + let inlineSize = 0; |
| 94 | + let blockSize = 0; |
| 95 | + const childFragments = []; |
| 96 | + |
| 97 | + for (let child of children) { |
| 98 | + let fragment = yield child.doLayout(constraintSpace); |
| 99 | + blockSize += fragment.blockSize; |
| 100 | + inlineSize = Math.max(inlineSize, fragment.inlineSize); |
| 101 | + childFragments.push(fragment); |
| 102 | + } |
| 103 | + |
| 104 | + return { |
| 105 | + inlineSize: inlineSize, |
| 106 | + blockSize: blockSize, |
| 107 | + children: childFragments, |
| 108 | + }; |
| 109 | + } |
| 110 | +}); |
| 111 | +``` |
| 112 | + |
| 113 | +The first thing to notice about the API is that the layout method on the class returns a generator. |
| 114 | + |
| 115 | +TODO finish writing this. |
0 commit comments