A Complete Guide To The Table Element - CSS-Tricks
A Complete Guide To The Table Element - CSS-Tricks
A Complete Guide To The Table Element - CSS-Tricks
The <table> element in HTML is used for displaying tabular data. You can think of it as a
way to describe and display data that would make sense in spreadsheet software.
Essentially: columns and rows. In this article, we’re going to look at how to use them, when
to use them, and everything else you need to know.
It is data that is useful across multiple axes. Imagine running your finger across a row
(horizontal) to see a single person and relevant information about them. Or up and down a
column (vertical) to get a sense of the variety or pattern of data on that point.
https://css-tricks.com/complete-guide-table-element/ 1/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
element, which would wrap the first <tr> (it could wrap as many rows as needed that are
all header information).
When you use <thead> , there must be no <tr> that is a direct child of <table> . All
rows must be within either the <thead> , <tbody> , or <tfoot> . Notice that we also
wrapped all the rows of data in <tbody> here.
(#foot) Foot
Along with <thead> and <tbody> there is <tfoot> for wrapping table rows that
indicate the footer of the table. Like <thead> , best for semantically indicating these are
not data rows but ancillary information.
Back before HTML5, the <tfoot> element was required to be after <thead> and before
<tbody> ! You might think it would be the last thing before the end of <table> , but
wasn’t the case. Now in HTML5, it’s exactly the opposite, it must go after the <tbody>
like you’d probably expect.
It can be used, for example, to repeat the header in the case of a visually very tall/long
table where it may be easier to see the column titles at the bottom than the top. Although
you don’t necessarily need to use it that way.
https://css-tricks.com/complete-guide-table-element/ 2/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Using our existing simple demo, the top row is all headers. Not data, just titles for the data.
All the rest of the rows are data. So:
<th> elements are not necessarily limited to being within the <thead> . They simply
indicate header information. So they could be used, for instance, at the start of a row in the
<tbody> , if that was relevant. We’ll cover a case like that later.
Notice the slight extra gap between the first row and the rest. That is caused by the default
border-spacing being applied to the <thead> and <tbody> pushing them apart a bit
extra. This isn’t margins, they don’t collapse. You can control that spacing like:
https://css-tricks.com/complete-guide-table-element/ 3/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
CSS
table {
border-spacing: 0.5rem;
}
But far more common is to remove that space. That property is completely ignored and the
space collapsed if you do:
CSS
table {
border-collapse: collapse;
}
Just a little padding, borders, and making those <th> elements be left-aligned goes a long
way to make for a simple, styled table:
You’ll have to do a bit of mental math when you start working with connected cells.
Colspan is fairly easy. Any table cell is “worth” one, unless it has a colspan attribute and
https://css-tricks.com/complete-guide-table-element/ 4/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
then it’s worth that many. Add up the values for each table cell in that table row to get the
final value. Each row should have exactly that value, or else you’ll get an awkward table
layout that doesn’t make a rectangle (the longest row will stick out).
Rowspan is similar, it’s just a little harder and more of a mental leap, because columns
aren’t grouped like rows are. If a table element has a rowspan attribute, it spans across
two rows vertically. That means the row below it gets +1 to it’s table cell count, and needs
one less table cell to complete the row.
It can be awkward to work out in your head, but we’re developers here, we can do it =).
Often these attributes are used in really simple ways like connecting a few related table
headers:
(#as-wide-as-they-need-to-be-or-fill-the-container-or-
beyond) As Wide As They Need To Be… Or fill the
container… Or beyond
The table element itself is unusual in how wide it is. It behaves like a block-level element
(e.g. <div> ) in that if you put one table after another, each will break down onto its own
line. But the actual width of the table is only as wide as it needs to be.
If the amount of text in the tables widest row only happens to be 100px wide, the table will
be 100px wide. If the amount of text (if put on one line) would be wider than the container,
https://css-tricks.com/complete-guide-table-element/ 5/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
it will wrap.
However if text is told to not wrap (i.e. white-space: nowrap; ) the table is happy to
bust out of the container and go wider. Table cells will also not wrap, so if there are too
many to fit, the table will also go wider.
I might skip a <thead> in this situation even though that first row is all header. It’s just
no more important than the vertical column of headers so it feels weird to group that top
row alone. Just make on row of all <th> and then each subsequent row with the first cell
only as <th> .
https://css-tricks.com/complete-guide-table-element/ 6/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
What kinds of things are appropriate in tables? Here are some: a plan/pricing/features
comparison, bowling scores, an internal grid of employee data, financial data, a calendar,
the nutrition facts information panel, a logic puzzle solver, etc.
You might occasionally hear: tables are unsemantic. That’s not true – they semantically
indicate tabular data. Tables are the right choice when that is the case.
There are some significant problems with using tables for layout though. First, HTML tags
mean things. As we covered, table elements semantically describe tabular data. Using
them for anything else is a breach of semantic duty. You aren’t going to get a fine in the
mail, but you aren’t getting as much value from your HTML as you could.
Speaking of source order, that affects more than accessibility. Imagine a “sidebar on the
left” layout. A table would dictate that table comes first in the source order, which while
also being bad for accessibility, is likely bad for SEO as well, potentially valuing your
ancillary content above primary content.
https://css-tricks.com/complete-guide-table-element/ 7/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Could you fix the SEO issues by using semantic tags within the table tags? Possibly
somewhat, but now you’re using double the HTML. If you really need the layout abilities of
a table but want to use semantic tags, see the next section. If you are somehow absolutely
stuck using table tags for layout, use the ARIA role="presentation" on the table to
indicate it as such.
As I write this in the latter half of 2013, tables have become far less prevalent and even
appealing as a layout choice. We’re seeing a lot more use of fixed and absolute positioning
which you cannot do inside a table. We’re seeing flexbox being awesome and being right on
the edge of mainstream usability. We’re seeing grid layout starting to grow up. We’re
seeing inline-block be used powerfully. We’re seeing the fragility of floats in the olden
days fade away.
Rarely do you see modern websites touch tables for layout. The last holdout is HTML
emails. The landscape of what renders emails is super wide. It is everything we deal with
on the web, plus the world of native apps on both mobile and desktop on operating systems
new and ancient. You can do some progressive enhancement for emails, but the layout
itself is still generally regarded as being safest done in tables. That is substantiated by the
fact that the major email sending services still all offer templates as tables.
(#making-semantic-elements-behave-like-a-table)
Making Semantic Elements Behave Like a Table
CSS has properties to make any element you wish behave as if it was a table element.
You’ll need to structure them essentially as you would a table, and it will be subject to the
same source-order-dependency as a table, but you can do it. I’m not crapping on it either,
it’s genuinely useful sometimes. If that layout style solves a problem and has no negative
order implications, use it.
Don’t use inline styles, but just for understanding here’s how that would go:
HTML
https://css-tricks.com/complete-guide-table-element/ 8/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
</header>
<div style="display: table-row;">
<div style="display: table-cell;"></div>
<div style="display: table-cell;"></div>
<div style="display: table-cell;"></div>
</div>
</section>
A handy trick here is that you don’t even need the table-row element in there if you don’t
want. A bunch of display: table-cell; elements that are children of a display:
table; element will behave like they are all in one row.
You always alter the display property of the element to get the table-style behavior. Here’s
the values:
CSS
Notice there is no <th> alternative. That is for semantic value only. It otherwise behaves
just like a <td> , so, no need to replicate it in CSS.
If you want to learn a lot more about using semantic elements but also table-style layout,
check out the book Everything You Know About CSS Is Wrong!
(http://www.sitepoint.com/store/everything-you-know-about-css-is-wrong/)
https://css-tricks.com/complete-guide-table-element/ 9/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
I’ve never been a huge fan of that title as it suggests that using this table style layout is the
right way and any other layout technique you use is the wrong way. But as I’ve said, this
can be tremendously useful and I’m glad it’s in CSS. Just be acutely aware that no matter
what kind of elements you use to create a table-based layout, it still subject to the same
problems (largely source order dependency).
Element What it is
https://css-tricks.com/complete-guide-table-element/ 10/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Element What it is
Element(s)
Attribute What it does
Found On
Indicates the table should allow sorting. UPDATE: I’m told this was removed from spec because of
sortable table
lack of implementations.
headers td space-separated string corresponding to ID’s of the <th> elements relevant to the data
https://css-tricks.com/complete-guide-table-element/ 11/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Element(s)
Attribute What it does
Found On
row | col | rowgroup | colgroup (default) – essentially specifies the axis of the header. The default
scope th is that a header is heading a column, which is typical, but a row might start with a header also,
where you would scope that header to the row or rowgroup.
Deprecated
What to use instead
Attribute
The correct answer is to use text-align: "x"; where x is the character to align on, but it’s not implemented
char
anywhere yet. But this attribute isn’t supported either, so no big loss.
“consider starting the cell content by an independent abbreviated content itself or use the abbreviated content as
abbr
the cell content and use the long content as the description of the cell by putting it in the title attribute”
https://css-tricks.com/complete-guide-table-element/ 12/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Deprecated
What to use instead
Attribute
Here’s how that looks (using Firefox 3D feature in its dev tools):
https://css-tricks.com/complete-guide-table-element/ 13/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
These properties are either unique to table elements or they behave uniquely on table
elements.
CSS Possible
What it does
Property values
baseline
sub
super
text-top
text-
vertical- Aligns the content inside a cell. Works particularly well in tables, although only the top/bottom/middle
bottom
align make much sense in that context.
middle
top
bottom
%
length
normal
pre
white- nowrap
Controls how text wraps in a cell. Some data may need to be all on one line to make sense.
space pre-
wrap
pre-line
Applied to the table to determine if borders collapse into themselves (sort of like margin collapsing
border- collapse only bi-directional) or not. What if two borders that collapse into each other have conflicting styles
collapse separate (like color)? The styles applied to these types of elements will “win”, in order of “strength”: cell, row,
row group, column, column group, table.
If border-collapse is separate , you can specify how far cells should be spaced out from each
border-
length other. Modern version of cellspacing attribute. And speaking of that, padding is the modern
spacing
version of the cellpadding attribute.
Width works on table cells just about how you would think it does, except when there is some kind of
conflict. For instance if you tell the table itself to be 400px wide then the first cell of a three-cell row to
be 100px wide and leave the others alone, that first cell will be 100px wide and the other two will split
width length
up the remaining space. But if you tell all three of them to be 10000px wide, the table will still be 400px
and it will just give each of them a third of the space. That’s assuming white-space or elements like an
image don’t come into play. This is probably a whole post in itself!
https://css-tricks.com/complete-guide-table-element/ 14/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
CSS Possible
What it does
Property values
Border works on any of the table elements and just about how you would expect. The quirks come in
when you collapse the borders. In this case all table cells will have only one border width between
them, rather than the two you would expect them to have (border-right on the first cell and border-left
border length
on the next cell). In order to remove a border in a collapsed environment, both cells need to “agree” to
remove it. Like td:nth-child(2) { border-right: 0; } td:nth-child(3) { border-left:
0; } Otherwise, source order/specificity wins which border is shown on which edge.
auto is the default. The width of the table and its cells depends on the content inside. If you change
table- auto this to fixed , the table and column widths are set by the widths of table and col elements or by the
layout fixed width of the first row of cells. Cells in subsequent rows do not affect column widths, which can speed
up rendering. If content in subsequent cells can’t fit, the overflow property determines what happens.
This list isn’t exhaustive. There are other CSS quirks that are relevant to tables. For
instance, you can’t relatively position a table cell in which to either nudge it around or
absolutely position things within it. There are ways though. (https://css-
tricks.com/absolutely-position-element-within-a-table-cell/)
If you can think of more CSS weirdness with tables, share in the comments below.
CSS
table {
display: table;
border-collapse: separate;
border-spacing: 2px;
border-color: gray
}
thead {
display: table-header-group;
vertical-align: middle;
border-color: inherit
}
https://css-tricks.com/complete-guide-table-element/ 15/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
tbody {
display: table-row-group;
vertical-align: middle;
border-color: inherit
}
tfoot {
display: table-footer-group;
vertical-align: middle;
border-color: inherit
}
table > tr {
vertical-align: middle;
}
col {
display: table-column
}
colgroup {
display: table-column-group
}
tr {
display: table-row;
vertical-align: inherit;
border-color: inherit
}
td, th {
display: table-cell;
vertical-align: inherit
}
th {
font-weight: bold
}
caption {
display: table-caption;
text-align: -webkit-center
}
I inspected each element in Chrome Dev Tools too, which is now on Blink, and it’s still the
same.
It’s funny though. For sure, the text in <th> s is centered ( text-align: center; ) by
default. But that’s not in the UA stylesheet. Not a huge deal but rather mysterious and
https://css-tricks.com/complete-guide-table-element/ 16/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
The UA stylesheet for tables differs from browser to browser. For example, in Firefox
(here’s 3.6’s UA Stylesheet (https://codepen.io/chriscoyier/pen/EnxiB.css) , but this is
true in v23 too) table cells have this:
CSS
td {
display: table-cell;
vertical-align: inherit;
text-align: inherit;
padding: 1px;
}
Most notably, 1px of padding that WebKit doesn’t have. Not a huge deal in most cases,
surely, but it is different. That’s what CSS resets (and related projects) are all about:
removing the differences. So let’s check those out.
CSS
https://css-tricks.com/complete-guide-table-element/ 17/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
(http://html5doctor.com/html-5-reset-stylesheet/) .
CSS
table {
border-collapse: collapse;
border-spacing: 0;
}
I’ll have to dig into the reasoning here a little deeper because it seems unusual…
1. I’m a fan of border-collapse: collapse because spacing between cells is usually way
awkward, but, the default in every browser I know of is border-collapse: separate;
so isn’t in need of normalization.
2. If border-collapse is collapse , border-spacing doesn’t matter.
3. Table cell elements are in need of normalization (e.g. Firefox padding difference) but that
isn’t there.
CSS
table {
border-collapse: collapse;
width: 100%;
}
th, td {
padding: 0.25rem;
text-align: left;
border: 1px solid #ccc;
}
https://css-tricks.com/complete-guide-table-element/ 18/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
(#implied-elements-and-unclosed-tags) “Implied”
Elements and Unclosed Tags
Check out this rather awkward bit of HTML:
HTML
<table>
<col>
<tr>
<td>Cell
</table>
This may be weird to look at, but it’s valid. What’s going on here?
⦾ The <col> tag is just one of those no-content elements that doesn’t ever need a closing
tag. Like <br> / <br />
⦾ The <td> element doesn’t need to be closed in certain circumstances: “The end tag may
be omitted, if it is immediately followed by a <th> or <td> element or if there are no more
data in its parent element.”
⦾ The missing closing </tr> tag is the same story: “The end tag may be omitted if the <tr>
element is immediately followed by a <tr> element, or if the parent table group (<thead>,
<tbody> or <tfoot>) element doesn’t have any more content.”
If we inspect the rendered table in the browser, we can see that the tags that were missing
their closing tags are shown with closing tags. Those are automatically added for us. But
there are also some brand new elements in there:
One thing to notice is the <col> is wrapped within a <colgroup> automatically. Even if
we were to do:
HTML
<table>
<col>
https://css-tricks.com/complete-guide-table-element/ 19/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
<colgroup>
<col>
</colgroup>
<tr>
<td>Cell
<td>Cell
</table>
Then:
CSS
colgroup:first-child {
background: red;
}
You would think the second cell would be red, not the first, because the “first” colgroup
only affects the second cell. But when rendered, both of those columns get wrapped in a
colgroup, so the CSS selector will select the first one.
The <tbody> element is also implied. If you don’t use any of tbody, thead, or tfoot, the
whole guts of the table will be wrapped in tbody. If you use a thead, the whole table will be
wrapped in that until it find a tbody, then it will auto-close the thead if you don’t, and wrap
the rest in tbody (also optional to close). If if finds a tfoot, you can imagine what happens
(although remember tfoot should come before tbody).
You can actually use these elements in CSS selectors even though you didn’t put them in
your actual HTML. I probably wouldn’t advise it just because that’s weird, confusing, and
styling tag selectors usually isn’t advisable anyway.
The trick is essentially to reset the display property of the table cells:
https://css-tricks.com/complete-guide-table-element/ 20/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
CSS
th, td {
display: inline;
}
Just to be safe, I’d reset the whole she-bang. Just makes me feel better knowing parent
elements are also along for the ride and won’t get freaky.
CSS
This is primarily useful in responsive design where the traditional table layout makes
sense on large screens but needs significant shifts to make sense on smaller screens.
There is a whole section on that below.
https://css-tricks.com/complete-guide-table-element/ 21/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
CSS
tbody tr:nth-child(odd) {
background: #eee;
}
We’re using the tbody in the selector because it’s unlikely you’d want to stripe header and
footer rows. Set the even rows as well if you want to be specific about it instead of let what
is underneath show through.
If you need to support browsers that don’t understand :nth-child() (pretty damn old) you
could use jQuery to do it (https://css-tricks.com/snippets/jquery/jquery-zebra-stripe-a-
table/) .
https://css-tricks.com/complete-guide-table-element/ 22/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
Hightlighting a particluar row is fairly easy. You could add a class name to a row
specifically for that:
HTML
<tr class="highlight">
...
</tr>
Highlighting a column is a bit trickier. One possibility is to use the <col> element, which
does allow us to set styles for cells that appear in that column. It’s weird to wrap your head
around, because the cells that are affected by <col> aren’t actually descendants of it. The
browser just kinda knows what you mean.
A table with four columns in each row would have four <col> elements:
HTML
<table>
<col>
<col>
<col>
<col>
<thead>
...
</table>
CSS
col:nth-child(3) {
background: yellow;
}
https://css-tricks.com/complete-guide-table-element/ 23/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
However this is rarely useful. If you set the background of a row element or table cell
element, that will always beat a background of a column element. Regardless of
specificity.
You’re probably better off setting a class name on each individual table cell element that
happens to match that column position in the row. Like:
CSS
td:nth-child(2),
th:nth-child(2){
background: yellow;
}
(#highlighting-column-row-cell-on-hover) Highlighting
Column/Row/Cell on Hover
Cell highlighting is very easy. You can do it right in CSS:
CSS
Row highlighting is just as easy. You can set the background on table rows and it will show
as long as you don’t set a background on the table cells.
CSS
tbody tr:hover {
background: yellow;
}
https://css-tricks.com/complete-guide-table-element/ 24/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
If you do set a background on the table cells, you can always just to tr:hover td,
tr:hover th { } so still pretty easy.
Column highlighting is tricker. You can’t use col:hover because those columns aren’t
actual elements that take up pixel space on the screen that you could hover over. The only
option is JavaScript.
And here I’ve combined both row and column highlighting. I used jQuery to make it all 12
lines of code (the raw JavaScript was getting pretty exhausting).
It’s the same concept, it’s just much easier to make element collections, and find and
select by indexes in jQuery.
https://css-tricks.com/complete-guide-table-element/ 25/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
When the table is hovered, only the current row highlighted stays dark text, the others
fade back. Also note on this one: the roundered corners on the table itself are only possible
while you have border-collapse: separate;
https://css-tricks.com/complete-guide-table-element/ 26/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
jQuery
(#tables-can-be-difficult-in-fluid-responsive-designs)
Tables Can Be Difficult in Fluid/Responsive Designs
I’ve written about this in the past (https://css-tricks.com/responsive-data-tables/) , and I
think this graphic kind of sums up the experience of a data table on a small screen:
https://css-tricks.com/complete-guide-table-element/ 28/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
https://css-tricks.com/complete-guide-table-element/ 29/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
The most modern way of handling fixed headers is position: sticky; Here’s an article
on that (https://developers.google.com/web/updates/2012/08/Stick-your-landings-
position-sticky-lands-in-WebKit) . I’m honestly not quite sure the recommended way to
https://css-tricks.com/complete-guide-table-element/ 30/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
use it with tables though. It doesn’t work on <thead> under normal circumstances. That
kinda makes sense because you can’t absolutely position table innards. But it does work on
<th> . Anyway if someone wants to figure that out, that’d be a good update to this article
(or something).
Here’s a live demo of a jQuery plugin that does the trick. I’d probably go for something like
this these days until sticky shakes out more.
(#using-emmet-for-creating-table-markup) Using
Emmet for Creating Table Markup
Emmet (http://emmet.io/) is a great tool for a bunch of reasons. One of which is writing
HTML abbreviations (http://docs.emmet.io/abbreviations/) and having them expand out
into real HTML. Since tables are so repetitive and verbose, Emmet is perfect for them.
Emmet works on CodePen too =)
HTML
table>tr*4>td*4
HTML
table>tr*5>th+td*4
HTML
table>tr>th*5^tr*3>td*5
https://css-tricks.com/complete-guide-table-element/ 31/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
HTML
table>tr>th{Name}+th{ID}+th{Favorite Color}^tr*3>td{Name}+td{$$$$$}+td{Blue}
HTML
table>thead>tr>th*5^^tfoot>tr>th*5^^tbody>tr*10>th+td*4
HTML
https://css-tricks.com/complete-guide-table-element/ 32/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
This is such a common and generic need, that there is actually specification ready for it.
Just put the sortable attribute on the table and it will automatically do it as long as you
follow a couple of rules laid out in the spec. Update June 2109: Removed link, it no longer
mentions sortable. Seems like a dead spec for now.
At the time of this writing, I don’t know of any browsers supporting table sorting natively.
But there are lots of third-party options!
What’s with table sorting scripts and lowercase? Anyway, here’s a demo of tablesorter:
If those don’t do it for you, Codrops rounded up 33 different table sorting scripts
(http://tympanus.net/codrops/2009/10/03/33-javascript-solutions-for-sorting-tables/) ,
so there are plenty to choose from.
And those are all JavaScript solutions. It’s certainly possible to sort data on the back-end
and display the table already sorted in the HTML. That might be required in the case of
paginated tables where all the data isn’t available right in the DOM.
https://css-tricks.com/complete-guide-table-element/ 33/34
3/24/2021 A Complete Guide to the Table Element | CSS-Tricks
https://css-tricks.com/complete-guide-table-element/ 34/34