RCSS VisualModel
The visual formatting model used in RCSS is almost identical to that of CSS, and it is recommended you read that for a detailed explanation of how libRocket builds and places element boxes. A short summary can be found below.
The differences between CSS and RCSS are:
- The dimensions of the viewport are defined as the dimensions of the context the document is being rendered in.
- The document element (the root) is always positioned (as absolute), and can be positioned within its context using top, right, bottom and left.
- Compact and run-in boxes are not yet supported.
- Generated content is not yet supported.
Contents
- 1 Introduction to the visual formatting model
- 2 Controlling box generation
- 3 Positioning schemes
- 4 Normal flow
- 5 Floats
- 6 Absolute positioning
- 7 Layered presentation
- 8 Visual Formatting Model Details
- 8.1 Definition of ‘containing block’
- 8.2 Content width: the ‘width’ property
- 8.3 Computing widths and margins
- 8.4 Minimum and maximum widths: ‘min-width’ and ‘max-width’
- 8.5 Content height: the ‘height’ property
- 8.6 Computing heights and margins
- 8.7 Minimum and maximum heights: ‘min-height’ and ‘max-height’
- 8.8 Line height calculations: the ‘line-height’ and ‘vertical-align’ properties
Introduction to the visual formatting model
During document layout, each element generates zero or more boxes. How these boxes are sized and laid out within their document is governed by:
- The box type (either block, for boxes generally laid out top-to-bottom, or inline, for boxes laid out along a line).
- The box size; this could either be derived from intrinsic dimensions for elements such as images and form controls, or specified directly through RCSS properties, or determined by the contents of the box.
- Relationships of the box to other boxes in the document tree, such as floating boxes.
Containing blocks
Every box has a ‘containing block’, a rectangular box with a fixed width and height. The dimensions of this box are used to evaluate such things as relative widths and margins. In general, a box will establish the containing block for its descendant elements, depending on the box’s type.
A box is not necessarily constrained by the dimensions of its containing block, it may overflow along the x- or y-axes.
The containing block of the root document element is set by the dimensions of the document’s context. The document element will generally establish a containing block for its descendants. However, it if does not have a fixed height, the height of the context will be used.
Controlling box generation
Block level elements and block boxes
Block-level elements are those that are formatted as blocks, flowing visually down the document from top to bottom (such as paragraphs). Elements with a display of block generate block boxes, as do any absolute or fixed positioned elements and floating elements.
Block-level elements generate exactly one block box that itself contains only block boxes. If any inline elements are inside this block box, an anonymous block box is created (one not representing any specific element) to hold and position the inline content. The anonymous block box is positioned with the original block box just like any other block box.
So, while processing the following RML fragment (assuming div and p elements are block, and img elements are inline):
<div> <div> </div> <p> <img /> </p> </div>
the first div would generate a block box. This box would establish the containing block for its descendants. The second div would generate a block box using the first’s containing block, as would the p element. The img element would be placed inside an anonymous block box, which would be positioned within the p tag and generated with its containing block.
Inline-level elements and inline boxes
Inline-level elements are those elements that do not generate blocks of content, but flow their content into lines contained by a block box. Elements with a display property of inline or inline-block generate inline boxes.
Loose text within elements also generates inline boxes.
The ‘display’ property
- display
Value: | block | inline-block | none |
Initial: | inline |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
The values have the following meanings:
- block
- This element generates a block box.
- inline
- This element generates one or more inline boxes.
- inline-block
- This element generates a block box, which its descendants are positioned within, but is positioned itself as a single inline box. This is similar to the behaviour of replaced elements (those with intrinsic widths).
- none
- This element (and all of its descendants) generate no boxes, and are not displayed. Note this will mean the element will not affect layout. The ‘visibility’ property can be used to make an element affect layout but not be rendered.
The only difference between this and the CSS display property is the reduced range of values allowed.
Positioning schemes
RCSS can position elements according to three schemes:
- Normal flow, which includes placement of block boxes, the flowing of inline boxes and relative positioning of either.
- Floats. Floating elements are placed as normal, then shifted as far to the left or right as possible within their containing block. Inline content is then flowed around floating boxes.
- Absolute positioning. Elements that are positioned with absolute or fixed are removed from the normal flow (therefore having no impact on the layout of other elements) and placed at an explicit location relative to their containing block.
Choosing a positioning scheme: ‘position’ property
- position
Value: | relative | absolute | fixed |
Initial: | static |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
The values have the following meanings:
- static
- The element generates boxes that are positioned according to normal flow.
- relative
- The element generates boxes that are positioned according to normal flow, but then offset according to the values of the ‘top’, ‘right’, ‘bottom’ and ‘left’ properties. Other elements are positioned as though the box was in its original location.
- absolute
- The element generates a block box that is removed from normal flow. The top, right, bottom and left properties will position the box relative to the edges of its containing block.
- fixed
- The element is positioned like an absolute box, but will not scroll along with other content if it is within an overflowing box.
Box offsets: ‘top’, ‘right’, ‘bottom’, ‘left’
- top, bottom
Value: | <percentage> |
Initial: | 0px |
Applies to: | positioned elements |
Inherited: | no |
Percentages: | relative to the height of the containing block |
- left, right
Value: | <percentage> |
Initial: | 0px |
Applies to: | positioned elements |
Inherited: | no |
Percentages: | relative to the width of the containing block |
The values have the following meanings:
- <length>
- The edge is offset a fixed distance from the reference edge.
- <percentage>
- The edge is offset from the reference edge a distance relative to the dimensions of the containing block.
Note that RCSS does not yet support the ‘auto’ keyword for any of these properties.
Normal flow
Block formatting
When formatting block boxes, boxes are laid out vertically one after the other. The bottom edge of a block box will touch the top edge of the block box following it. The left edge of a block box will touch the left edge of its containing block. Block elements do not themselves flow around floating elements, but their inline content will.
Inline formatting
Inline boxes are positioned horizontally on a line, one after the other, from the top of the containing block. Horizontal padding, borders and margins are respected when positioning the boxes. Inline boxes on a single line are said to be laid out in a line box.
Each line box is generally the same width as its containing block, with its left edge touching the left edge of the containing block and its right edge touching the right edge of the containing block. However, floating elements may force a line box to be shorter along one or both of its edges. The height of a line box is governed by the height of the box’s inline content. Inline boxes are positioned vertically through the line box using the vertical-align property.
If several inline boxes cannot fit horizontally onto a single line box, they are flowed into several vertically-stacked line boxes.
The text-align property governs how the excess horizontal space between the widths of the inline boxes and the width of the line box is distributed.
Inline boxes may be split across several line boxes. When this occurs, any horizontal margins, borders and padding will only be present at the beginning and end of the inline box, not on each line.
Relative positioning
Once a block or inline box has been positioned, it can be moved from its location if its position is set to relative. It is offset from its position using the top, right, bottom and left properties. Note that it affects the positioning of further elements from its original location, not its new one.
Floats
A float is a box that is shifted horizontally to the left or right edge of its containing block. They do not affect placement of further block boxes, however line boxes will be shortened to avoid running over them. In this way, inline content will be flowed around them. Block boxes can be forced vertically past floating boxes using the clear property.
See the CSS2 documentation on floating elements for a description on the full float model, and interesting uses of floats.
Positioning the float: the ‘float’ property
- float
Value: | right | none |
Initial: | none |
Applies to: | all but absolutely positioned elements |
Inherited: | no |
Percentages: | N/A |
The values of this property have the following meanings:
- left
- The element generates a block box that is floated to the left.
- right
- The element generates a block box that is floated to the right.
- none
- The element’s boxes are not floated.
Controlling flow next to floats: the ‘clear’ property
- clear
Value: | right | both | none |
Initial: | none |
Applies to: | block-level elements |
Inherited: | no |
Percentages: | N/A |
The values of this property have the following meanings:
- left
- The box’s position is pushed far enough vertically so its top edge is below the bottom edge of all previously placed left-floating elements.
- right
- The box’s position is pushed far enough vertically so its top edge is below the bottom edge of all previously placed right-floating elements.
- both
- The box’s position is pushed far enough vertically so its top edge is below the bottom edge of all previously placed floating elements.
- none
- The box’s position is placed irrespective of floating elements.
Floating boxes themselves can be cleared.
Note that RCSS ignores floating elements placed within a sibling’s containing block.
Absolute positioning
An absolutely positioned box (one with a position of absolute or fixed) is explicitly offset from the edges of its containing block. Floating boxes are ignored when laying out its inline content.
Note that in RCSS, fixed positioned boxes are not placed relative to the viewport, but identically to absolute boxes.
Layered presentation
Each box has a stacking level within a single stacking context. Boxes within a stacking context are rendered in stacking level order, from lowest to highest (so boxes with the highest stacking level will appear on top). All the boxes within a single stacking context are rendered at once; they will not intermingle rendering with the members of another stacking context.
The root element of a document establishes a stacking context for its descendants. Other elements within a document may establish local stacking contexts of their own. An element which does so has two stacking levels; one within the stacking context it is a member of (given by the z-index property) and another inside the stacking context it establishes (always ‘0’).
Specifying the stacking level: the ‘z-index’ property
- z-index
Value: | auto |
Initial: | auto |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Values have the following meanings:
- <number>
- The number is the stack level within the current stacking context. It also establishes a local stacking context in which its own stack level is ‘0’.
- auto
- The stack level of the box is the same as its parent’s. It does not establish a local stacking context.
Note that in RCSS any element can have the z-index property, not just positioned elements.
Visual Formatting Model Details
Definition of ‘containing block’
During layout, the containing block of an element is fixed when the element is encountered in the document tree. It is sized as follows:
- For the root element of the document (the body element), the containing block is the size of the document’s context.
- For other elements (other than absolutely positioned elements), the containing block is the size of the nearest block ancestor’s content area. If the block ancestor does not have a fixed height (ie, its height property is set to auto), the height of the containing block is given by the height of the nearest block ancestor’s fixed content area. This may end up as the height of the context.
- If the element is positioned fixed or absolute, its containing block is given by the padded size of the nearest block element with a position other than static, or the root if no such element exists. Like other elements, they will look further back in order to get a fixed height.
Content width: the ‘width’ property
- width
Value: | <percentage> | auto |
Initial: | auto |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the width of the containing block |
This property only applies to block boxes and inline boxes with an intrinsic width. Other inline boxes are sized by their content.
Values have the following meaning:
- <length>
- Specifies a fixed width.
- <percentage>
- Specifies a width relative to the width of the box’s containing block.
- auto
- The width depends on the values of other properties. See below.
/* Fixes the width of input elements of class 'text' to 12 times their line height. */ input.text { width: 12em; }
Computing widths and margins
If any of a box’s width, margin-left or margin-right are set to auto, then they are evaluated when the box is sized. Depending on the box type, they are set as follows:
- For an inline non-replaced box, any auto margins are set to ‘0’. width is ignored.
- For an inline replaced box, auto margins are set to ‘0’. A width of auto is set to the element’s intrinsic width.
- For a block box, the formula margin-left + border-left-width + padding-left + 'width' + padding-right + border-right-width + margin-right = containing block width must hold true. If width is auto, then any auto margins are set to ‘0’ and the box width is set to the appropriate value. Otherwise, the inequality in the equation is split evenly between the auto-margins.
This is different from the CSS equation in the following ways:
- The left and right properties cannot be set to auto.
- Floating and positioned boxes are evaluated like normal boxes.
Minimum and maximum widths: ‘min-width’ and ‘max-width’
- min-width
Value: | <percentage> |
Initial: | 0px |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the width of the containing block |
- max-width
Value: | <percentage> |
Initial: | -1 |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the width of the containing block |
Values have the following meaning:
- <length>
- Specifies a fixed minimum or maximum width.
- <percentage>
- Specifies a minimum or maximum width relative to the width of the containing block.
Note that there is no auto value for max-width; instead, if it is a negative number there is no maximum width.
When evaluating width, if the computed width is greater than max-width, then the width is computed again, this time substituting max-width for width. If the computed value is less than min-width, the the width is computed again, this time substituting min-width for width.
Content height: the ‘height’ property
- height
Value: | <percentage> | auto |
Initial: | auto |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the height of the containing block |
This property only applies to block boxes and inline boxes with an intrinsic height. Other inline boxes are sized by their content.
Values have the following meaning:
- <length>
- Specifies a fixed height.
- <percentage>
- Specifies a height relative to the height of the box’s containing block.
- auto
- The width depends on the values of other properties. See below.
/* Fixes the height of the background div to 100% of its containing block. */ div#background { height : 100%; }
Computing heights and margins
If any of a box’s height, margin-top or margin-bottom are set to auto, then they are evaluated when the box is sized. Depending on the box type, they are set as follows:
- For an inline non-replaced box, any auto margins are set to ‘0’. height is ignored.
- For an inline replaced box, auto margins are set to ‘0’. A height of auto is set to the element’s intrinsic width.
- For a block box with a fixed height, the formula margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom = containing block height must hold true. The inequality in the equation is split evenly between the auto-margins.
- For a block box with an auto height, any auto margins are set to ‘0’, and the height will stretch to fit the box’s content exactly.
This is different from the CSS equation in the following ways:
- The top and bottom properties cannot be set to auto.
- Floating and positioned boxes are evaluated like normal boxes.
- Block boxes with a fixed height will resolve auto vertical margins similarly to horizontal margins.
Minimum and maximum heights: ‘min-height’ and ‘max-height’
- min-height
Value: | <percentage> |
Initial: | 0px |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the height of the containing block |
- max-height
Value: | <percentage> |
Initial: | -1 |
Applies to: | block and replaced inline elements |
Inherited: | no |
Percentages: | relative to the height of the containing block |
Values have the following meaning:
- <length>
- Specifies a fixed minimum or maximum height.
- <percentage>
- Specifies a minimum or maximum height relative to the height of the containing block.
Note that there is no auto value for max-height; instead, if it is a negative number there is no maximum height.
When evaluating height, if the computed width is greater than max-height, then the height is computed again, this time substituting max-height for height. If the computed value is less than min-height, the the width is computed again, this time substituting min-height for height. A block box with a height of auto will never set its height below min-height or above max-height; this may result in overflow.
Line height calculations: the ‘line-height’ and ‘vertical-align’ properties
The height of a line box is determined as follows:
- The height of each inline element is calculated.
- The inline boxes are aligned vertically (by their vertical-align property)
- The line box height is given by distance between the top edge of the highest box and the bottom edge of the lowest box.
Note that vertical padding, margins and borders of inline boxes are not taken into account when determining line box height, although they are rendered.
- line-height
Value: | <length> | <percentage> |
Initial: | 1.2 |
Applies to: | all elements |
Inherited: | yes |
Percentages: | relative to the the font size of the element itself |
This property determines the minimal height of the element’s inline boxes.
Values for this property have the following meanings:
- <number>
- The line height is set to the element’s font height scaled by this number.
- <length>
- The line height is set to this fixed value.
- <percentage>
- The line height is set to the element’s font height scaled by the percentage.
/* Three ways of setting the same line-height. */ div { line-height: 1.3; line-height: 1.3em; line-height: 130%; }
- vertical-align
Value: | sub | super | text-top | text-bottom | middle | top | bottom | <percentage> | <length> |
Initial: | baseline |
Applies to: | inline-level elements |
Inherited: | no |
Percentages: | relative to the element’s line-height |
This property affects the vertical positioning of an inline box within the line box.
The values have the following meanings:
- baseline
- Align the baseline of the inline box with the baseline of the line box.
- sub
- Set the baseline of the inline box to an appropriate height for rendering subscript.
- super
- Set the baseline of the inline box to an appropriate height for rendering superscript.
- text-top
- Align the top of the inline box with the top of the parent box’s font.
- text-bottom
- Align the bottom of the inline box with the bottom of the parent box’s font. middle: Align the midpoint of the box with the bottom of the parent box’s font plus half the ex height.
- top
- Align the top of the inline box with the top of the line box.
- bottom
- Align the bottom of the inline box with the bottom of the line box.
- <percentage>
- Raise or lower the element from the baseline by the line-height scaled by this percentage.
- <length>
- Raise or lower the element from the baseline by a fixed amount.
/* Sample RCSS for defining a superscript tag. */ super { vertical-align: super; }
/* Sample RML demonstrating rendering a superscript. */ <p> Better than ever before!<super>*</super> </p>