CSS Table Module Level 3

Editor’s Draft,

More details about this document
This version:
https://www.downtownmelody.com/_x/ZHJhZnRzLmNzc3dnLm9yZw/css-tables-3/
Latest published version:
https://www.downtownmelody.com/_x/d3d3LnczLm9yZw/TR/css-tables-3/
Previous Versions:
Feedback:
CSSWG Issues Repository
Inline In Spec
Editors:
François Remy (Invited Expert)
Greg Whitworth (Microsoft)
Former Editors:
Bert Bos (W3C)
L. David Baron (Google)
Markus Mielke (Microsoft)
Saloni Mira Rai (Microsoft)
Suggest an Edit for this Spec:
GitHub Editor
Not Ready For Implementation

This spec is not yet ready for implementation. It exists in this repository to record the ideas and promote discussion.

Before attempting to implement this spec, please contact the CSSWG at [email protected].


Abstract

This CSS module defines a two-dimensional grid-based layout system, optimized for tabular data rendering. In the table layout model, each display node is assigned to an intersection between a set of consecutive rows and a set of consecutive columns, themselves generated from the table structure and sized according to their content.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-tables” in the title, like this: “[css-tables] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list [email protected].

This document is governed by the 18 August 2025 W3C Process Document.

1. Introduction

This section is not normative

Many types of information (ex: weather readings collected over the past year) are best visually represented in a two-axis grid where rows represent one item of the list (ex: a date, and the various weather properties measured during that day), and where columns represent the successive values of an item’s property (ex: the temperatures measured over the past year).

Sometimes, to make the representation easier to understand, some cells of the grid are used to represent a description or summary of their parent row/column, instead of actual data. This happens more frequently for the cells found on the first row and/or column (called headers) or the cells found on the last row and/or column (called footers).

This kind of tabular data representation is usually known as tables. Tables layout can be abused to render other grid-like representations like calendars or timelines, though authors should prefer other layout modes when the information being represented does not make sense as a data table.

The rendering of tables in HTML has been defined for a long time in the HTML specification. However, its interactions with features defined in CSS remained for a long time undefined. The goal of this specification is to define the expected behavior of user agents supporting both HTML tables and CSS.

Please be aware that some behaviors defined in this document will not be the most logical or useful way of solving the problem they aim to solve, but such behaviors are often the result of compatibility requirements and not a deliberate choice of the editors of this specification. Authors wishing to use more complex layouts are encouraged to rely on more modern CSS modules such as CSS Grid.

1.1. Value Definitions

This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3]. Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3]. Combination with other CSS modules may expand the definitions of these value types.

In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.

2. Content Model

2.1. Table Structure

The CSS table model is based on the HTML4 table model, in which the structure of a table closely parallels the visual layout of the table. In this model, a table consists of an optional caption and any number of rows of cells.

In addition, adjacent rows and columns may be grouped structurally and this grouping can be reflected in presentation (e.g., a border may be drawn around a group of rows).

The table model is said to be "row primary" since authors specify rows, not columns, explicitly in the document language. Columns are derived once all the rows have been specified: the first cell of the first row belongs to the first column and as many other columns as spanning requires (and it creates them if needed), and the following cells of that row each belong to the next available column and as many other columns as spanning requires (creating those if needed); the cells of the following rows each belong to the next available column for that row (taking rowspan into account) and as many other columns as spanning requires (creating those if needed). (see § 3.3 Dimensioning the row/column grid).

To summarize, an instance of the table model consists of:

[see-caption-below]
Two representations of the structure of a table (tree vs layout)

The CSS model does not require that the document language include elements that correspond to each of these components. For document languages (such as XML applications) that do not have pre-defined table elements, authors must map document language elements to table elements. This is done with the display property.

The following display values assign table formatting rules to an arbitrary element:

table (equivalent to HTML: <table>)
Specifies that an element defines a table that is block-level when placed in flow layout.
inline-table (equivalent to HTML: <table>)
Specifies that an element defines a table that is inline-level when placed in flow layout.
table-row (equivalent to HTML: <tr>)
Specifies that an element is a row of cells.
table-row-group (equivalent to HTML: <tbody>)
Specifies that an element groups some amount of rows.

Unless explicitly mentioned otherwise, mentions of table-row-groups in this spec also encompass the specialized table-header-groups and table-footer-groups.

table-header-group (equivalent to HTML: <thead>)
Like table-row-group but, for layout purposes, the first such row group is always displayed before all other rows and row groups.
If a table owns multiple display: table-header-group boxes, only the first is treated as a header; the others are treated as if they had display: table-row-group.
table-footer-group (equivalent to HTML: <tfoot>)
Like table-row-group but, for layout purposes, the fist such row group is always displayed after all other rows and row groups.
If a table owns multiple display: table-footer-group boxes, only the first is treated as a footer; the others are treated as if they had display: table-row-group.
table-column (equivalent to HTML: <col>)
Specifies that an element describes a column of cells.
table-column-group (equivalent to HTML: <colgroup>)
Specifies that an element groups one or more columns.
table-cell (equivalent to HTML: <td> or <th>)
Specifies that an element represents a table cell.
table-caption (equivalent to HTML: <caption>)
Specifies a caption for the table. Table captions are positioned between the table margins and its borders.

Note: Replaced elements with a display value of table-row, table-row-group , table-header-group, table-footer-group, table-column, table-column-group, table-cell, and table-caption are treated as inline-level boxes, as per CSS Display 3 § 2.4 Layout-Internal Display Types: the table-* and ruby-* keywords; replaced elements with a display value of table or inline-table behave according to their outer display type, as per CSS Display 3 § 2.1 Outer Display Roles for Flow Layout: the block, inline, and run-in keywords. This is a breaking change from CSS 2.1 but matches implementations.

2.1.1. Terminology

In addition to the table structure display types, the following wording is also being used in this spec:

table wrapper box
A block container box generated around table grid boxes to account for any space occupied by each table-caption it owns.
table grid box
A block-level box containing the table-internal boxes, excluding its captions.
table-root element
An element whose inner display type is table.
table-non-root box or element
A proper table child, or a table-cell box.
table-track box or element
A table-row, or table-column box.
table-track-group box or element
A table-row-group, or table-column-group box.
proper table child box or element
A table-track-group, table-track, or table-caption box.
proper table-row parent box or element
A table-root or a table-row-group box.
table-internal box or element
A table-cell, table-track or table-track-group box.
tabular container
A table-row or proper table-row parent box.
consecutive boxes
Two sibling boxes are consecutive if they have no intervening siblings other than, optionally, an anonymous inline containing only white spaces. A sequence of sibling boxes is consecutive if each box in the sequence is consecutive to the one before it in the sequence.
table grid
A matrix containing as many rows and columns as needed to describe the position of all the table-rows and table-cells of a table-root, as determined by the grid-dimensioning algorithm.

Each row of the grid might correspond to a table-row, and each column to a table-column.

slot of the table grid
A slot (r,c) is an available space created by the intersection of a row r and a column c in the table grid.

Each slot of the table grid is covered by at least one table-cell (some of them anonymous), and at most two. Each table-cell of a table-root covers at least one slot.

Table-cells which cover more than one slot do so densely, meaning the set of slots they cover can always be described as a set of four strictly-positive integers (rowStart, colStart, rowSpan, colSpan) such that a slot (r,c) is covered by the table-cell if and only if r lies in the interval between rowStart (included) and rowStart+rowSpan (excluded), and c lies in the interval between colStart (included) and colStart+colSpan (excluded);

Such table-cell is said to originate from row rowStart and column colStart. Also:

  • A table-cell is said to originate a table-row (resp. table-column) if it originates its corresponding row (resp. column)
  • A table-cell is said to originate a table-row-group (resp. table-column-group) if the group contains the cell’s originating row (resp. column)

Such table-cell is said to span all rows r and columns c matching the above condition. Also:

  • A table-cell is said to span a table-row (resp. table-column) if it spans its corresponding row (resp. column)
  • A table-row (resp. table-column) corresponding to a row (resp. column) is said to span this row (resp. column)
  • A table-row (resp. table-column) is said to span all columns of the grid (resp. row)
  • A table-row-group (resp. table-column) containing a row (resp. column) is said to span the row (resp. column)
  • A table-row-group (resp. table-column) is said to span all columns of the grid (resp. row)

2.2. Fixup

Document languages other than HTML may not contain all the elements in the CSS 2.1 table model. In these cases, the "missing" elements must be assumed in order for the table model to work.

Any table-internal element will automatically generate necessary anonymous table objects around itself, if necessary. Any descendant of a table-root that is not table-internal must have a set of ancestors in the table consisting of at least three nested objects corresponding to a table/inline-table, a table-row, and a table-cell. Missing boxes cause the generation of anonymous boxes according to the following rules:

2.2.1. Fixup Algorithm

For the purposes of these rules, out-of-flow elements are represented as inline elements of zero width and height. Their containing blocks are chosen accordingly.

The following steps are performed in three stages:

  1. Remove irrelevant boxes:
    The following boxes are discarded as if they were display:none:
    1. Children of a table-column.
    2. Children of a table-column-group which are not a table-column.
    3. Anonymous inline boxes which contain only white space and are between two immediate siblings each of which is a table-non-root box.
    4. Anonymous inline boxes which meet all of the following criteria:
  2. Generate missing child wrappers:
    1. An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes. !!Testcase
    2. An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-group box which are not table-row boxes. !Testcase
    3. An anonymous table-cell box must be generated around each sequence of consecutive children of a table-row box which are not table-cell boxes. !Testcase
  3. Generate missing parents:
    1. An anonymous table-row box must be generated around each sequence of consecutive table-cell boxes whose parent is not a table-row. Testcase
    2. An anonymous table or inline-table box must be generated around each sequence of consecutive proper table child boxes which are misparented. If the box’s parent is an inline, run-in, or ruby box (or any box that would perform inlinification of its children), then an inline-table box must be generated; otherwise it must be a table box. Testcase Testcase !Testcase
    3. An anonymous table-wrapper box must be generated around each table-root. Its display type is inline-block for inline-table boxes and block for table boxes. The table wrapper box establishes a block formatting context. The table-root box (not the table-wrapper box) is used when doing baseline vertical alignment for an inline-table. The width of the table-wrapper box is the border-edge width of the table grid box inside it. Percentages which would depend on the width and height on the table-wrapper box’s size are relative to the table-wrapper box’s containing block instead, not the table-wrapper box itself.
Please note that some layout modes such as flexbox and grid override the display type of their children. These transformations happen before the table fixup.
Please note that the float and position properties sometimes affect the computed value of display. When those properties are used on what should have been table internal boxes, they switch to block instead. This transformation happen before the table fixup.
We have modified the text of this section from CSS 2.2 to make it easier to read. If you find any mistakes due to these changes please file an issue

2.2.2. Characteristics of fixup boxes

Beside their display type, the anonymous boxes created for fixup purposes do not receive any specific or default styling, except where otherwise mentioned by this specification.

This means in particular that their computed background is “transparent”, their computed padding is “0px”, their computed border-style is “none”.

It is also worth reminding that an anonymous box inherits property values through the box tree.

2.2.3. Examples

<div class="row">
  <div class="cell">George</div>
  <div class="cell">4287</div>
  <div class="cell">1998</div>
</div>

Here is the associated styles:

.row { display: table-row }
.cell { display: table-cell }

After fixup, this will produce layout boxes as though this was the initial HTML:

<table>
  <tr>
    <td>George</td>
    <td>4287</td>
    <td>1998</td>
  </tr>
</table>

In this example, three table-cell anonymous boxes are assumed to contain the text in the rows. The text inside of the divs with a display: table-row are encapsulated in anonymous inline boxes, as explained in visual formatting model:

<div class="inline-table">
  <div class="row">This is the top row.</div>
  <div class="row">This is the middle row.