<br/><img src="http://w3.org/favicon.ico" title="After"></td> <td><img src="http://w3.org/favicon.ico"><img src="http://w3.org/favicon.ico"><!--Baseline--></td> </tr></table>
For the purposes of finding a baseline, in-flow boxes with a scrolling mechanisms (see the overflow property) must be considered as if scrolled to their origin position.
The baseline of a cell may end up below its bottom border, see the example below.
The cell in this example has a baseline below its bottom border:
div { height: 0; overflow: hidden; } <table> <tr> <td> <div> Test </div> </td> </tr> </table>
The vertical-align property of each table cell determines its alignment within the row. Each cell’s content has a baseline, a top, a middle, and a bottom, as does the row itself.
In the context of table cells, values for vertical-align have the following meanings:
baseline | The baseline of the cell is aligned with the baseline of the other cells of the first row it spans (see the definition of baselines of cells and rows). |
---|---|
top | The top of the cell box is aligned with the top of the first row it spans. |
bottom | The bottom of the cell box is aligned with the bottom of the last row it spans. |
middle | The center of the cell is aligned with the center of the rows it spans. |
... | Other values do not apply to cells; the cell is aligned at the baseline instead. |
The maximum distance between the top of the cell box and the baseline over all cells that have 'vertical-align: baseline' is used to set the baseline of the row. If a row doesn’t have any cell that has 'vertical-align: baseline', the baseline of that row is the bottom content edge of the lowest cell in the row.
The baseline of a table-root is the baseline of its first row, if any. Otherwise, it is the bottom content edge of the table-root.
To avoid ambiguous situations, the alignment of cells proceeds in the following order:
Example showing how the previous algorithm creates the various alignment lines of a row.
Since during row layout the specified heights of cells in the row were ignored and cells that were spanning more than one rows have not been sized correctly, their height will need to be eventually distributed to the set of rows they spanned. This is done by running the same algorithm as the column measurement, with the span=1 value being initialized (for min-content) with the largest of the resulting height of the previous row layout, the height specified on the corresponding table-row (if any), and the largest height specified on cells that span this row only (the algorithm starts by considering cells of span 2 on top of that assignment).
Rows that see their size increase as a result of applying these steps adjust by lowering their bottom.
The cells whose position depended on the bottom of any updated row must be positioned correctly again in their respective rows.
At this point, cell boxes that are smaller than the collective height of the rows they span receive extra top and/or bottom padding such that their content does not move vertically but their top/bottom edges meet the ones of the first/last row they span.
Once the table height has been determined, a second row layout pass will be performed, if necessary, to assign the correct minimum height to table rows, by taking percentages used in rows/cells specified height into account. Other than that, all instructions for the first-pass row layout apply (see above).
Then, if the sum of the new heights of the table rows after this second pass is different from what is needed to fill the table height previously determined, the height distribution algorithm defined below is applied (either to shrink rows by sizing them intermediately between their first-pass minimum height and their second-pass one, or to increase the heights of all rows beyond their second-pass minimum height to fill the available space; in neither case, this will have an impact on the baseline of the rows).
The first step is to attribute to each row its base size and its reference size.
Its base size is the size it would have got if the table didn’t have a specified height (the one it was assigned when ROWMIN was evaluated).
Its reference size is the largest of
The second step is to compute the final height of each row based on those sizes.
If the table height is equal or smaller than sum of reference sizes, the final height assigned to each row will be the weighted mean of the base and the reference size that yields the correct total height.
Else, if the table owns any “auto-height” row (a row whose size is only determined by its content size and none of the specified heights), each non-auto-height row receives its reference height and auto-height rows receive their reference size plus some increment which is equal to the height missing to amount to the specified table height divided by the amount of such rows.
Else, all rows receive their reference size plus some increment which is equal to the height missing to amount to the specified table height divided by the amount of rows.
The cells whose position depended on the bottom of any updated row must be positioned correctly again in their respective rows.
At this point, cell boxes that are smaller than the collective height of the rows they span receive extra top and/or bottom padding such that their content does not move vertically but their top/bottom edges meet the ones of the first/last row they span.
Once table-height distribution has concluded, and the sum of row heights plus spacing/border is equal to the table height, the content of table-cells which contained descendants whose percentage heights were ignored or treated as 0px by the first-pass row layout rules (see above) must undergo a second layout pass, as defined below.
Resolve percentage heights in table-cell content: Once the final size of the table and the rows has been determined, after height distribution has concluded, the content of the table-cells must also go through a second layout pass, where, if appropriate, percentage-based heights are this time resolved against their parent cell used height.
It is appropriate to resolve percentage heights on direct children of a table-cell if the cell is considered to have its height specified explicitly or the child is absolutely positioned, see CSS 2.
For compat reasons, it is further clarified that a cell is considered to have its height specified explicitly if the computed height of the cell is a length, or if the computed height of its table-root ancestor is a length or percentage, regardless of whether that percentage does behave as auto or not.
<section style="height: var(--wrapper-height)"> <table style="height: var(--table-height)"> <tr> <td style="height: var(--table-cell-height)"> <div style="height:100%; background:yellow">A</div> </td> <td style="height: var(--other-table-cell-height)"> B<br>C </td> </tr> </table> </section>
--table-cell-height | --table-height | result |
---|---|---|
<length> | <any> | ![]() |
<any> | <length> | ![]() |
<any> | <percentage> | ![]() |
auto | auto | ![]() |
<percentage> | auto | ![]() |
Note that neither --other-table-cell-height
nor --wrapper-height
do influence the algorithm’s outcome.
A previous version of this specification incorrectly stated that --wrapper-height
was taken into account when the table had a percentage height, but compat issues appeared when an implementation landed, and the behavior was then special-cased.
We need a resolution on what visibility:collapse does. [Issue #478]
Once the width of each column and the height of each row of the table grid has been determined, the final step of algorithm is to assign to each table-internal box its final position.
The width/height/left/top calculated below define the dimensions of the CSS Layout Box, which means that they are accessible via the offset* properties defined in CSSOM-VIEW, (currently limited to css boxes for which you can obtain a corresponding HTMLElement reference).
The table-wrapper box is then sized such that it contains the margin box of all table-non-root boxes as well as the table-root border-box.
The position of any table-caption having "top" as caption-side within the table is defined as the rectangle whose:
The position of any table-cell, table-track, or table-track-group box within the table is defined as the rectangle whose:
The position of any table-caption having "bottom" as caption-side within the table is defined as the rectangle whose:
collapse
.If an absolutely positioned element’s containing block is generated by a table-wrapper box, the containing block corresponds to the area around which the table margins are applied, including the area where the table border is drawn and the margin area of any table-caption. The offset properties (top/right/bottom/left) then indicate offsets inwards from the corresponding edges of this containing block, as normal.
Absolute positioning occurs after layout of the table and its in-flow contents, and does not contribute to the sizing of any table grid tracks or affect the size/configuration of the table grid in any way.
If an absolutely positioned element’s containing block is generated by a table-internal, the containing block corresponds to the area starting at the top left corner of the the area that would be assigned to the box during layout but whose size is computed to be the one of the area that would be assigned to the box during layout if all tracks were considered visible (irrespective of visibility being set co collapse on some boxes), not including borders and paddings as appropriate.
The offset properties (top/right/bottom/left) then indicate offsets inwards from the corresponding edges of this containing block, as normal.
This only works in Firefox. It would make it easier to implement position:sticky in the future, though. [Chrome bug][Interop risk: Firefox bug][Issue #858]
The only influence of non-containing block parent of an absolutely-positioned box is to define its static position, in case both top+bottom and/or left+right end up being auto.
For table-cells, the absolutely-positioned content is positioned follows the rules for block layout as usual.
Due to table fixup, it is not possible to create an absolutely-positioned box that is the child of a table-internal box that is not a table-cell (see note about float and position for more details).
Table cells are painted in a table-root in DOM order as usual, independently of where cells end up actually being drawn.
Name: | empty-cells |
---|---|
Value: | show | hide |
Initial: | show |
Applies to: | table-cell boxes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | specified keyword |
Canonical order: | per grammar |
Animation type: | discrete |
In collapsed-borders mode, this property has no effect.
In separated-borders mode, when this property has the value hide
, no borders or backgrounds are drawn around/behind empty cells.
An empty cell is a table-cell containing neither:
Can we simplify empty-cells:hide? [Issue #605]
For example, take the following markup and css:
< table > < td >< span ></ span ></ td > < td ></ td > < td >< span ></ span ></ td > </ table >
table{ width : 500 px ; height : 300 px ; empty-cells : hide; } table{ background : black; border : 10 px solid black; } td{ background : white; } table{ border-spacing : 0 px ; } td{ padding : 0 ; }
The correct rendering of this code snippet is depicted here:
Unlike other boxes types, table and inline-table boxes do not paint their background and borders around their entire client rect. Indeed, the table captions are to be visually positioned between the table margins and its borders, which means the drawing areas of various effects applied to the table-root need to be modified.
Painting areas:
When a table is laid out in collapsed-borders mode, the rendering of its borders on and those of its table-cells is modified. The following rules describe in which way.
The rules for background and borders painting defined in § 5.3 Drawing backgrounds and borders still apply if they are not overridden.
Borders of a non-emptytable-root are not painted in collapsed-borders mode, except if the border-image property is set.
In this latter case, the border is drawn as if the table border was twice as big as its used value specify, and as if that excess was rendered inside the padding area of the table-root.
Even if they are not drawn by the table, the table borders still occupy their space in the layout. Cells will render those shared borders.
Anonymous table-cells added by the missing cells fixup step do not render any of their backgrounds.
In addition to its own background, table-cell boxes also render the backgrounds of the table-track and table-track-group boxes in which they belong. This is actually different from simply inheriting their background because the background-origin and background-size computations will actually be done on the bounds of the grouping boxes, and not on those of the cell.
For the purposes of finding the background of each table cell, the different table boxes may be thought of as being on six superimposed layers. The background set in one of the layers will only be visible if the layers above it have a transparent background.
As the figure above shows, although all rows contain the same number of cells, not every cell may have specified content. In separated-borders mode, if the value of their empty-cells property is hide
, these empty cells are not rendered at all, as if visibility: hidden
was specified on them, letting the table background show through.
In separated-borders mode, borders of table cells are rendered as usual.
Borders of a table-cell are rendered in collapsed-borders mode as if the cell border was twice as big as its used value specify, and as if that excess was rendered in the margin area of the cell, with the added constraint that for each side of the border which isn’t located at one of the table edges, the border is actually clipped to the border-box drawing area as its real used value define except if the border-image property is set.
If applying the previously-mentioned clipping behavior results in clipping a border over a non-integer amount of device pixels, browsers may decide to snap the clipping area to a device pixel instead by ceiling the x- and y-values of the clipping area. Ceiling the values ensures that in a normal writing mode, the cell which gets the contested pixels between multiple cells is actually the most top left one, which has a greater specificity than the other ones according to this spec. See § 5.1 Paint order of cells and § 3.7.1.1 Conflict Resolution Algorithm for Collapsed Borders.
Some of the values of the border-style have different meanings for tables in collapsed-borders mode than usual. Those definitions override the default behavior for border-style values.
Same as none
, but also inhibits any other border (see § 3.7.1.3 Specificity of a border style).
Same as ridge
.
Same as groove
.
When a table part has visibility: collapse set, the rendering is handled differently depending if it is on a table-cell, spanning table-cell, or a table-track/table-track-group.
This happens when you set visibility:collapse on a table-row that contains a table-cell. If you want to hide a row but continue to display its cells that span other rows, set visibility:visible on those cells to prevent them from inheriting their value.
If the table-cell is spanning more than one table-track, and at least one of those table-track is set to visibility: collapse then clip the content to the table-cell’s border-box. This means that the top left (top right in rtl) content of the cell will continue to show, regardless of which of the tracks the cell spans has been collapsed.
When fragmenting a table, user agents must attempt to preserve the table rows unfragmented if the cells spanning the row do not span any subsequent row, and their height is at least twice smaller than both the fragmentainer height and width. Other rows are said freely fragmentable.
When a table doesn’t fit entirely in a fragmentainer, at least one row did fit entirely in the fragmentainer, and the first row that does not fit in the fragmentainer is not freely fragmentable. the user agent has to insert some vertical gap between the rows located before and at the overflow point such that the two rows end up separated in sibling fragmentainers. If the fragmentation requires repeating headers and footers, and the footer is repeated, then the footer must come directly after the last row in the fragmentainer and the vertical gap must be inserted after the repeated footer.
When there is no row fitting entirely in the current fragmentainer or when the first row that does not fit in the fragmentainer is freely fragmentable, user agents must attribute all the remaining height in the fragmentainer to the cells of that row, and fit as much content as it can in each of the cells independently, then break to the next fragment and start the content of each cell where it was stopped in its previous fragment (top borders must not be repainted in continuation fragments).
When break-before or break-after is applied to a table-row-group or a table-row box, the user agent has to insert some vertical gap between the rows located before and after the breaking point such that the two rows end up separated in sibling fragmentainers as required by the property value. If the fragmentation requires repeating headers and footers, and the footer is repeated, then the footer must come directly after the last row in the fragmentainer and the vertical gap must be inserted after the repeated footer.
When rendering the document into a paged media, user agents must repeat header rows and footer rows on each page spanned by a table if the page is the table’s fragmentainer, if the header/footer has avoid break-inside applied to it, if the height required to do so is inferior to two quarters of the page height (up to one quarter for header rows, and up to one quarter for footer rows), and if that doesn’t cause a row to be displayed twice on that page.
When the header rows are being repeated, user agents must leave room and if needed render the table top border. The same applies for footer rows and the table bottom border.
User agents may decide to extend this behavior to more fragmentation contexts, for instance repeat headers/rows across columns in addition to pages. User-agents that are rendering static documents are more likely to adopt this behavior, though this is not required per spec.
Using CSS Tables does not incur any security risk to mitigate.
Using CSS Tables does not incur any privacy risk to mitigate.
This section is not normative.
The default style sheet for HTML4 illustrates how its model maps to css properties and values:
table{ display : table} thead{ display : table-header-group} tbody{ display : table-row-group} tfoot{ display : table-footer-group} tr{ display : table-row} td, th{ display : table-cell} colgroup{ display : table-column-group} col{ display : table-column} caption{ display : table-caption} table, thead, tbody, tfoot, tr, td, th, colgroup, col, caption{ box-sizing : border-box; } thead, tfoot{ break-inside : avoid} table{ box-sizing : border-box; border-spacing : 2 px ; border-collapse : separate; text-indent : initial; } thead, tbody, tfoot, table > tr{ vertical-align : middle; } tr, td, th{ vertical-align : inherit; } td, th{ padding : 1 px ; } th{ font-weight : bold; } table, td, th{ border-color : gray; } thead, tbody, tfoot, tr{ border-color : inherit; } table[ frame=box i], table[ frame=border i], table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border : 1 px solid inset; } table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]) { border-collapse : collapse; border-style : hidden; } table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]), table:is ([ rules=all i], [ rules=rows i], [ rules=cols i], [ rules=groups i], [ rules=none i]) >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border-color : black; } table[ border=$border] /* if(parseInt($border) > 0) */ { border : /*(parseInt($border) * 1px)*/ outsetrgb ( 128 , 128 , 128 ); } table[ border=$border] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) /* if(parseInt($border) > 0) */ { border : 1 px insetrgb ( 128 , 128 , 128 ); } table[ rules=all i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; } table[ rules=rows i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; border-left : none; border-right : none; } table[ rules=cols i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : 1 px solid grey; border-top : none; border-bottom : none; } table[ rules=none i] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) { border : none; } table[ rules=groups i] >:is ( thead, tbody, tfoot) { border-top-width : 1 px ; border-top-style : solid; border-bottom-width : 1 px ; border-bottom-style : solid; } table[ rules=groups i] > colgroup{ border-left-width : 1 px ; border-left-style : solid; border-right-width : 1 px ; border-right-style : solid; } table[ frame=box i], table[ frame=border i], table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-style : outset; } table[ frame=below i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-top-style : hidden; } table[ frame=above i], table[ frame=vsides i], table[ frame=lhs i], table[ frame=rhs i] { border-bottom-style : hidden; } table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=rhs i] { border-left-style : hidden; } table[ frame=hsides i], table[ frame=above i], table[ frame=below i], table[ frame=rhs i] { border-right-style : hidden; } table[ cellpadding=$x] >:is ( thead, tbody, tfoot) > tr >:is ( th, td) /* if(parseInt($x)>0) */ { padding : /*(parseInt($x) * 1px)*/ ; } table[ cellspacing=$x] /* if(parseInt($x)>0) */ { border-spacing : /*(parseInt($x) * 1px)*/ ; } table[ width=$w] /* if(parseInt($w) > 0) */ { width : /*(parseInt($w) * 1px)*/ ; } table[ width=$w] /* if($w matches /(+|-|)([0-9]+([.][0-9]+|)|([.][0-9]+))[%]/) */ { width : /*(parseInt($w) * 1px)*/ ; } table[ height=$h] /* if(parseInt($h) > 0) { height: /*(parseInt($h) * 1px)*/ ; } table[ height=$h] /* if($h matches /(+|-|)([0-9]+([.][0-9]+|)|([.][0-9]+))[%]/) */ { height : /*(parseInt($h) * 1px)*/ ; } table[ bordercolor=$color] { border-color : /*parseHTMLColor($color)*/ ; } table[ bordercolor] >:is ( tbody, thead, tfoot, tr, colgroup, col), table[ bordercolor] >:is ( tbody, thead, tfoot) > tr, table[ bordercolor] >:is ( tbody, thead, tfoot) > tr >:is ( td, th), table[ bordercolor] > tr >:is ( td, th) table[ bordercolor] > colgroup > col) { border-color : inherit; } table[ bgcolor=$color] { background-color : /*parseHTMLColor($color)*/ ; } table[ align=left i] { float : left; } table[ align=right i] { float : right; } table[ align=center i] { margin-left : auto; margin-right : auto; } caption[ align=bottom i] { caption-side : bottom; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=top i] { vertical-align : top; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=middle i] { vertical-align : middle; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=bottom i] { vertical-align : bottom; } :is ( thead, tbody, tfoot, tr, td, th)[ valign=baseline i] { vertical-align : baseline; } :is ( thead, tbody, tfoot, tr, td, th)[ align=absmiddle i] { text-align : center; } :is ( colgroup, col, thead, tbody, tfoot, tr, td, th)[ hidden] { visibility : collapse; } :is ( td, th)[ nowrap] { white-space : nowrap; } :is ( td, th)[ nowrap][ width=$w] /* if(quirksMode && parseInt($w) > 0) */ { white-space : normal; }
Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.
All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]
Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example"
, like this:
Informative notes begin with the word “Note” and are set apart from the normative text with class="note"
, like this:
Note, this is an informative note.
Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">
, like this: UAs MUST provide an accessible alternative.
Tests relating to the content of this specification may be documented in “Tests” blocks like this one. Any such block is non-normative.
Conformance to this specification is defined for three conformance classes:
A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.
A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)
An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.
So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.
To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.
Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.
To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.
Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/. Questions should be directed to the public-css-testsuite@w3.org mailing list.
Name | Value | Initial | Applies to | Inh. | %ages | Animation type | Canonical order | Computed value |
---|---|---|---|---|---|---|---|---|
border-collapse | separate | collapse | separate | table grid boxes | yes | n/a | discrete | per grammar | specified keyword |
border-spacing | <length>{1,2} | 0px 0px | table grid boxes when border-collapse is separate | yes | n/a | by computed value | per grammar | two absolute lengths |
caption-side | top | bottom | top | table-caption boxes | yes | n/a | discrete | per grammar | specified keyword |
empty-cells | show | hide | show | table-cell boxes | yes | n/a | discrete | per grammar | specified keyword |
table-layout | auto | fixed | auto | table grid boxes | no | n/a | discrete | per grammar | specified keyword |