A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

Representing hierarchical tabular data in HTML - E.g. tree-tables


Conceptually, tree-tables can be thought of as a collection of records of identical shape, but where some records are subordinate to - e.g. contained within - another record. Tree-table widgets are common in LOB applications and tools like email clients. However they are difficult to build in HTML and CSS, due to the table element having no concept of nesting, and the list elements having no concept of columns.

Semantically, it must be possible for table row elements to express a recursive, nested relationship. The most obvious way to express this relationship is through literal nesting of the table row elements in markup. These nested rows would not represent a sub-table, as they would still be governed by the columns and headings of the root table, but would be semantically a child of their parent row.

Visually, tree-tables present an interesting challenge: The parent/child relationship of rows should be clear, but the columns of the table must still align across all rows. Tree-table widgets usually solve this by representing the hierarchy using indented names/ids within the first cell, where the amount of indentation represents the depth of the item within the tree.

This is difficult to reproduce with CSS alone, because hierarchical nesting is done using margins or padding the nested element itself, e.g. the <li>, rather than an item within the <li>. In the case of tree-tables, it is not desirable to shift the entire row, as this would break the alignment of its cells with the table’s columns.

Therefore, in order to allow this in pure CSS, it may require novel constructs that allow an element’s padding or margin to be calculated dependent on its context. E.g. a table cell must be able to say, “Left-pad my contents to 24px * the number of table-rows nested between me and the table root”.

Behaviourally, it is desirable to collapse and expand the hierarchy, as well as perform standard table operations such as sorting and filtering - although the implementation and results of these operations may be different than in a non-hierarchical table. This could likely be achieved through script, as is currently done for regular tables.


While you can’t do it with the current CSS Variables spec, you’ll be able to in level 2 (I had to strip some stuff out to make level 1 simpler):

tr {
  --nesting-level: calc(parent-var(--nesting-level, 0) + 1);
  padding: calc(var(--nesting-level) * 24px);

parent-var() looks for the value of the custom property on the parent, using the second argument as a default if that custom property hasn’t been set yet. This lets you accumulate a value as you nest further in the tree.


Cool. Although this would still necessitate a change to HTML to allow trs to be nestable, right?


Oh, sure, this doesn’t solve any of the other problems, just the “how do I get something that increases based on nesting depth” part.