17 Tables

Contents

  1. Building visual tables
    1. The 'display' property
    2. Cell spanning properties: 'column-span', and 'row-span'
  2. Layout model of elements inside tables
    1. Margins on rows, columns and cells
    2. Interactions between rows and columns
      1. Labelled diagram:
    3. The 'border-collapse' property
    4. The border styles
  3. Computing widths and heights
    1. The 'table-layout' property
    2. The 'collapse' value for the 'visibility' property
  4. 'Vertical-align' on table cells
  5. Table elements in selectors
    1. Columns and cell selectors
    2. Row, column and cell pseudo-classes
      1. Row pseudoclasses:
      2. Column pseudoclasses:
  6. HTML 4.0 default table stylesheet
  7. Table layout
  8. Computing widths and heights
  9. Placement of the borders: 'cell-spacing'
  10. Conflict resolution for borders
  11. Properties for columns and rows
  12. Vertical alignment of cells in a row
  13. Horizontal alignment of cells in a column
  14. Table captions: the 'caption-side' property
  15. Generating speech: the 'speak-header' property
  16. Table implementation notes

Tables are used to show the relations between pieces of data, by arranging them into visual rows, columns, and cells. CSS2 can create and control the presentation of these visual elements, but is not intended to allow the arrangement of the structure itself.

Most CSS properties apply to table elements in the same manner as they apply to block-level elements. However, due to a lack of strict containership for table columns in the HTML model, some properties behave differently for tables. A few properties apply only to tables.

17.1 Building visual tables

A visual table is composed of a single element designated as the "table", which contains any number of table columns, column groups, rows and row groups. This will allow XML data, for example, to be visually presented as a table through CSS, without any other semantics needing to be attached to the XML element types in the document.

17.1.1 The 'display' property

There are ten values for the 'display' property that create tables and parts of tables:

Elements designated as 'inline-table' are considered to be replaced elements; the others are to be treated as block-level elements for the purposes of classification of interaction with other CSS properties - that is, other CSS properties that are defined as applying to block-level elements will apply to these elements. Some of the semantics of these other properties may change, as defined below.

Throughout this specification, "table elements" refers to any element designated as one of these ten types; "'table' elements" refers to only those designated as display type 'table'. Similarly, unless single-quoted, "row groups" refers to elements of type 'table-row-group', 'table-header-group' or 'table-footer-group', "row elements" refers to row group elements or elements of type 'table-row', and 'columns' refers to elements of type 'table-column' or 'table-column-group'.

Tables are row-primary; that is, a branch of a structured document is displayed as a visual table by designating one element as a 'table', with one or more of its child elements designated as 'rows', and one or more elements inside each row designated as 'cells'. 'Row-groups' are optional structures used to group rows, and potentially create specific margin, border or padding space around them. 'Columns' and 'column-groups' are optional structures which allow borders to be placed around specific rows, as well as allowing CSS selectors to select cells and their children based on the columns they appear in. The visual interactions of columns and rows are described in the table layout model description below.

"Missing" elements are inserted as anonymous elements. In other words: any table element will automatically generate a whole table tree around itself, consisting of at least a 'table'/'inline-table', a 'table-row' and a 'table-cell'.

In particular, if the parent a 'table-cell' is not a 'table-row', an anonymous 'table-row' element will be assumed as its parent, and this anonymous element will span all consecutive 'table-cell' siblings.

Similarly, if the parent of a 'table-row' is not a 'table'/'inline-table' nor a row group element, an anonymous 'table' will be assumed as its parent, spanning all consecutive siblings that also need an anonymous 'table' parent.

Analogously for a row group element.

In this XML example, an anonymous 'table' is inserted to contain the HBOX element:

<HBOX>
  <VBOX>George</VBOX>
  <VBOX>4287</VBOX>
  <VBOX>1998</VBOX>
</HBOX>

The style sheet is:

HBOX {display: table-row}
VBOX {display: table-cell}

Anonymous 'table-cell' elements are inserted as children of 'table-rows', to contain consecutive children that are not 'table-cell's.

In this example, three anonymous 'table-cell' elements are inserted to contain the text in the ROWs. Note that the text is further encapsulated in anonymous inline boxes, as explained in "Visual rendering model.":

<STACK>
  <ROW>This is the <D>top</D> row.</ROW>
  <ROW>This is the <D>middle</D> row.</ROW>
  <ROW>This is the <D>bottom</D> row.</ROW>
</STACK>

The style sheet is:

STACK {display: inline-table}
ROW {display: table-row}
D {display: inline; font-weight: bolder}

Elements with 'display' set to 'table-column' or 'table-column-group' are not rendered (exactly as if they had 'display: none'), but they are useful, because they may have attributes which induce a certain style for the columns they represent.

17.1.2 Cell spanning properties: 'column-span', and 'row-span'

'row-span'

Property name:  'row-span'
Value:  <integer> | inherit
Initial:  1
Applies to:  cell elements
Inherited:  no
Percentage values:  N/A
Media groups:  visual

The number of rows spanned by a cell. A cell box occupies a rectangle of 'column-span' by 'row-span' grid cells in a table.

'column-span'

Property name:  'column-span'
Value:  <integer> | inherit
Initial:  1
Applies to:  cell, column, and column-group elements
Inherited:  no
Percentage values:  N/A
Media groups:  visual

The number of grid columns spanned by a cell, column or column group. A cell box occupies a rectangle of 'column-span' by 'row-span' grid cells in a table.

[Need description of how to determine the row/column coordinates of cells and how to count column elements; or refer to HTML 4.0?]

17.2 Layout model of elements inside tables

The layout of tables is a fundamentally different algorithm than that used to lay out regular block-level elements. Cells are laid out on a two-dimensional grid-based pattern, unlike block elements, which are laid on in a one-dimensional flow. This is further complicated by the interactions of rows and columns.

17.2.1 Margins on rows, columns and cells

The most important difference between the layout model of tables in CSS and the layout model of other elements (e.g. inline and block-level elements) is that margins collapse horizontally as well as vertically on elements inside tables (margins on the table element itself function as expected on a replaced element). This collapsing uses the same algorithm as vertical collapsing on regular block elements in CSS. This is designed to allow easy duplication of the HTML "cell spacing" effect - that is, to easily design evenly spaced table cells in a table.

[Can this work? Maybe it is better to say that table elements (other than 'table' and 'inline-table') have no margins at all, and instead use a single 'cell-spacing' property for the whole table, or a single horizontal and a single vertical spacing (see below)]

17.2.2 Interactions between rows and columns

Row and Column elements (and row-groups and column-groups) inside tables are essentially on two different layout planes. They both constrain and are constrained by the layout of the cells they contain, but rows and columns are not considered to interact with each other directly when laying out the table. This means that widths on columns may change the layout of the cell (due to word-breaking occurring with different constraints), which may affect the height of the cell (and hence the row), but the columns do not directly affect the row height (and vice versa).

Example of interaction:

a table with many borders that cross each other

HTML source:

<STYLE>
#tbl1 {
	margin: 1em;
	border: 1em solid cyan;
	padding: 1em;
}
#
</STYLE>
...
<TABLE ID="tbl1">
<COL ID="col1"><COL ID="col2">
<TR ID="row1">
    <TD ID="cell1">Cell1<BR>(Contents)</TD>
    <TD ID="cell2">Cell2<BR>(Contents)</TD>
</TR><TR ID="row2">
    <TD ID="cell3">Cell3<BR>(Contents)</TD>
    <TD ID="cell4">Cell4<BR>(Contents)</TD>
</TR></TABLE>

Labelled diagram:  

diagram
showing the positions of some of the margins, paddings, borders.

17.2.3 The 'border-collapse' property

Property name: 'border-collapse'
Value: collapse | separate
Initial: separate
Applies to: 'table' elements
Inherited: no

This property selects between two different models of border behavior. The HTML table behavior is that borders are drawn separately for each cell, and cell spacing separates the borders. Another common model in document publishing (and used in most popular word processors) is a collapsing border model. In this model, adjacent borders are collapsed into one border when drawn. If the border is collapsed, any margin or column/row padding separating the two borders is ignored.

The question of which border to draw is determined by the following algorithm. If any border side on an element is set to 'none', it is considered to have the lowest possible precedence - in other words, any other non-'none' values that may apply to that edge from other coincident border sides will apply. (This does not apply to borders with style of 'hidden', which has highest precedence - see the following section on border style values for more information.)

  1. Borders on 'table' elements take precedence over borders on any other table elements.
  2. Borders on 'row-groups' take precedence over borders on 'rows', and likewise borders on 'column-groups' take precedence over borders on 'columns'.
  3. Borders on any other type of table element take precedence over 'table-cell' elements.
  4. If the adjacent elements are of the same type, the wider of the two borders is used. "Wider" takes into account the border-style of 'none', so a "1px solid" border will take precedence over a "20px none" border.
  5. If the borders are of the same width, the border on the element occurring first is used.

[A possible other rule, that tries to make sure thinner borders never interrupt thicker ones: if any of the coincident border is 'hidden', use that; otherwise, if all of the coincident borders are 'none', use that; otherwise take the widest border among the non-'none' styles; if there is more than one, prefer 'double' over 'solid' over 'dashed' over 'dotted ' over 'inset' over 'outset' over 'ridge over 'groove'. If there is still a conflict (which must be in the color), the result is undefined.]

The chosen border is drawn using its normal width, style and color. Other coincident border edges are not drawn underneath, in the case of partially transparent border styles (e.g. 'double' borders, which have a transparent gap).

[If the margins around table elements are dropped in favor of a single 'cell-spacing' property, as suggested above, the cell spacing and 'border-collapse' can be combined as follows:

'cell-spacing'

Property name:  'cell-spacing'
Value:  none | <length> <length>? | inherit
Initial:  none
Applies to:  table
Inherited:  yes
Percentage values:  N/A
Media groups:  visual

]

Example of collapsed borders:

an
example of a table with collapsed borders

source:

TABLE {		border: 5px solid yellow; }
#col1 {		border: 1px solid black; }
TD {		border: 5px solid red; }
TD.foo {	border: 11px solid blue; }
TD.bar {	border: 7px solid green; }
}
</STYLE>
...
<TABLE>
<COL ID="col1"><COL ID="col2"><COL ID="col3">
<TR ID="row1">
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
</TR>
<TR ID="row2">
    <TD>&nbsp;</TD>
    <TD CLASS="foo">&nbsp;</TD>
    <TD CLASS="bar">&nbsp;</TD>
</TR>
<TR ID="row3">
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
</TR>
<TR ID="row4">
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
</TR>
<TR ID="row5">
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
    <TD>&nbsp;</TD>
</TR>
</TABLE>

17.2.4 The border styles

Some of the border styles are drawn differently in tables than elsewhere (cf. "Border style"):

none
No border.
hidden
Same as 'none', but if borders are drawn collapsed, also inhibits any other border (see above).
dotted
The border is a series of dots.
dashed
The border is a series of short line segments.
solid
The border is a single line segment.
double
The border is two solid lines. The sum of the two lines and the space between them equals the value of 'border-width'.
groove
The border looks as though it were carved into the canvas.
ridge
The opposite of 'grove': the border looks as though it were coming out of the canvas.
inset
If borders are drawn non-collapsed: the border makes the entire box look as though it were embedded in the canvas. If borders are collapsed: same as 'groove'.
outset
The opposite of 'inset'. If borders are drawn non-collapsed: the border makes the entire box look as though it were coming out of the canvas. If borders are collapsed: same as 'ridge'.

Example of hidden collapsed borders:

table
with three omitted internal borders

HTML source:

<TABLE STYLE="border-collapse: collapse; border: solid;">
<TR><TD STYLE="border: hidden">foo</TD>
    <TD STYLE="border: solid">bar</TD></TR>
<TR><TD STYLE="border: none">foo</TD>
    <TD STYLE="border: solid">bar</TD></TR>
</TABLE>

17.3 Computing widths and heights

17.3.1 The 'table-layout' property

Property name: 'table-layout'
Value: auto | fixed
Initial: auto
Applies to: 'table' elements
Inherited: no

This property controls the algorithm used to lay out the table (that is, to determine the widths and heights of the table and its individual cells, rows and columns). A value of 'auto' denotes the existing-practice HTML layout algorithm, which requires multiple passes through the data and determines the width of a column of cells based on the largest unbreakable element in a cell in the column. This algorithm can be inefficient - not only does it require multiple passes, it also requires the layout engine to have access to all the content in the table before determining the final layout.

The 'fixed' layout algorithm is a much faster algorithm. The horizontal layout of the table is not dependent on the content contained in the cells, it is dependent entirely on the widths set on the table and its columns and first row of cells. If the table has a 'width' of 'auto', the entire available space is used. The cell's width is determined by any width value set on the column, overridden by any value other than 'auto' set on the cell itself. All cells that have a 'width' of 'auto' in the row are collected by the layout engine, and when the end of the row is reached, all remaining available space is evenly distributed among them.

In this manner, the engine lays out the first row of the table immediately upon receiving the content. All subsequent rows have the same widths of columns of cells - nothing in subsequent rows can affect the widths of the columns. Any cell that has content that overflows its dimensions uses the 'overflow' property to determine how to handle the overflow content. Using the 'overflow' value of 'visible' will not grow the cell dimensions, but will slop the content into the next cell. The height of the row of cells may be computed (the 'auto' value), or may be set explicitly, in which case overflow is handled in the same manner as with the width dimension.

17.3.2 The 'collapse' value for the 'visibility' property

The 'visibility' property should take an additional value - 'collapse'. This value works identically to the 'hidden' value, except when it is applied to a 'table-row', 'table-row-group', 'table-column' or 'table-column-group'. When applied to an entire table row or column, the 'collapse' property will cause the entire row or column to be removed from the display, and the space normally taken up by the row or column will be available for other content - in this context only, it is similar to setting the 'display' property to 'none', except the cells that are no longer displayed will continue to affect the horizontal layout of the table columns (if a row is being collapsed) or vertical layout of table rows (if a column is being collapsed). This allows dynamic effects to remove table rows or columns without forcing a re-layout of the table in order to account for the potential change in column constraints.

17.4 'Vertical-align' on table cells

The CSS 'vertical-align' property also applies to 'table-cell' elements. Only the 'top', 'middle', 'bottom', 'baseline' and percentage values are applicable - all other values function as 'middle'. The property sets the vertical alignment of the content inside the table cell, in the same manner as the HTML VALIGN attribute on table cells (TD elements) does.

The baseline of a cell is the baseline of the first line of text in the cell. If there is no text, the baseline is the baseline of whatever object is displayed in the cell, or, if it has none, the bottom of the cell. The maximum distance between the top of the cell and the baseline over all cells that have 'vertical-align:baseline' is used to set the baseline of the row. Here is an example:

Example of vertically
aligning the cells

Cells 1 and 2 are aligned at their baselines. Cell 2 has the largest height above the baseline, so that determines the baseline of the row. Note that if there is no cell aligned at its baseline, the row will not have (not need) a baseline.

To avoid ambiguous situations, the alignment of cells proceeds in a certain order. First the cells that are aligned on their baseline are positioned. This will establish the baseline of the row. Next the cells with alignment 'top' are positioned.

The row now has a top, possibly a baseline, and a provisional height, which is the distance from the top to the lowest bottom of the cells positioned so far. (See conditions on the cell padding below.)

If any of the remaining cells, those aligned at the bottom or the middle, have a height that is larger than the current height of the row, the height of the row will be increased to the maximum of those cells, by lowering the bottom.

Finally the remaining cells are positioned.

The area between the cell content and the border is part of the cell's padding. The padding at the top and bottom of each cell after positioning must be at least as large as the 'padding' property specifies. The height of the row must be as small as possible without violating this rule.

17.5 Table elements in selectors

Due to the uniqueness of the table structure, there are several additional semantics and pseudo-classes for table cells.

[The implementation cost of this feature may be high. It requires two passes over the style sheet: first to determine the 'display', 'float' and 'position' properties using the normal element hierarchy, than again searching for selectors that match cell element inside column elements. In the second kind of rules, 'display', 'position' and 'float' may not appear.]

17.5.1 Columns and cell selectors

Due to their two-dimensional visual structure, cells have a unique feature in CSS selectors - they have a dual inheritance chain for contextual selectors, both from columns and from rows. This means a stylesheet author can easily write a CSS rule that applies to all the cells "contained" in a column, as well as all the cells contained in a row. The columns are treated as a second parent to the cell, not intermingled with the row inheritance chain.

<STYLE>
TR.foo TD { color: red }
COL.bar TD { color: blue }
COL.foo TR.bar TD { color: green }
TR.bar COL.bar TD { color: yellow }
</STYLE>
…
<TABLE>
<COLGROUP><COL CLASS=foo><COL CLASS=bar></COLGROUP>
<TR CLASS=foo><TD>one</TD><TD>two</TD></TR>
<TR CLASS=bar><TD>three</TD><TD>four</TD></TR>
</TABLE>

Cell "one" will be red, cell "two" will be blue. Since the row and column inheritance chains are not intermingled, the second two rules in the stylesheet will not apply to any of the content, and therefore cell "three" will be default color, and cell "four" will be blue.

If a cell appears in multiple columns (e.g. the cell has a column-span of more than 1), all column parents will apply in the selector.

17.5.2 Row, column and cell pseudo-classes

There are several new row and column pseudoclasses available for table formatting.

[The implementation cost of this feature is probably even higher than the one in the previous section. Not only does it require two passes, it also precludes the use of hash tables for quick access to rules.]

Row pseudoclasses:  

Column pseudoclasses:  

17.6 HTML 4.0 default table stylesheet

The following CSS2 stylesheet attempts to codify HTML 4.0 table behaviour.

<STYLE>
TABLE {
	display: table;
	border-collapse: separate;
	table-layout: auto;
	}
COLGROUP { display: table-column-group; column-span: attr(colspan); }
COL { display: table-column; column-span: attr(span); }
THEAD { display: table-header-group; row-span: attr(rows) }
TBODY { display: table-footer-group; row-span: attr(rows) }
TFOOT { display: table-row-group; row-span: attr(rows) }
TR { display: table-row }
TD { display: table-cell; column-span: attr(colspan); row-span: attr(rowspan) }
CAPTION { display: table-caption }
</STYLE>