Easier user interface development and responsive layouts using CSS tables
CSS tables can provide easy and robust solutions to common UI layout problems. As CSS tables are merely presentational (as opposed to displaying actual tabular data in markup) they are even handier for responsive designs. CSS table properties can be added to HTML elements at one viewport size, then removed at another. Read on to lift the lid on these fantastic properties.
Let’s consider how existing and widely supported CSS properties/values can achieve layouts that ordinarily seem difficult to achieve with things other than Flexbox. This is a post about the display: table;
and display: table-cell;
properties and associated values. I’ll be the first to admit that there’s nothing new here. People have been talking about these techniques for years. They do however seem a little less known and so I hope what follows bares repeating.
Let’s talk CSS tables.
First let’s be clear. These are tables for presentation ONLY. These are CSS tables, not HTML tables and so there is no coupling of the presentation to the markup which they apply. More succinctly be confident that CSS tables are not bad.
CSS tables are something I employed as an example in my book, Responsive web design with HTML5 and CSS3. One reader didn’t seem to ‘get’ it and thought that using CSS tables was somehow a bad thing. Hopefully, this article will help clarify any confusion in peoples minds.
You’ll happily set an element to ‘display: block;’ so you can just as easily set them to ‘display: table;’ or ‘display: table-cell;’. We will employ these properties purely for layout purposes.
I don’t think there’s ever been a better time for these properties. Think of them as a half-way house until Flexbox has ubiquitous support.
Why CSS tables are great
The CSS Table model is not new, being part of the CSS 2.1 specification. It has broad and solid browser support – basically everything apart from IE7 and below.
I first came across CSS tables when I read Rachel Andrew and Kevin Yank’s book, ‘Everything you know about CSS is wrong’ way back in 2008. Sadly, it was perhaps a little before its time and people accused the techniques enclosed within of being unusable (IE8 was yet to rear its head).
Admittedly, I’ve not re-read it recently but I’m sure much of what I’ll go over here is covered there. It even has the trick for changing the order of markup using the display:table-footer-group
& display:table-header-group
properties. If you’re interested in that Dennis Gaebel has created a CodePen so you can see it in action.
Solving some common problems
Let’s push on and see what these properties can do for us.
Vertically centred content
Centrally aligning content both vertically and horizontally within a container is a cinch with display: table. Take a look at this:
Check out this Pen!
Markup wise I’ve merely nested one element (acting as a table-cell) inside another (working as a wrapping table):
.tbl {
display: table;
width: 100%;
table-layout: fixed;
background-color: hotpink;
height: 8rem;
}
.cell {
display: table-cell;
vertical-align: middle;
text-align: center;
}
Points of note:
- The display property is set to table.
- Without a width, the table will be the width of its contents.
- I’ve set the table-layout property to
fixed
. The default is usuallyauto
. More on this shortly. - The nested element is set to
table-cell
. The vertical-align property is used to vertically centre it within the wrappingtable
(“Look Ma, no padding!”) - text-align: center is used to align the text on the horizontal axis
You can obviously run with this technique and do some funky layouts easily. Here’s an example:
Fixed – fluid – fixed layout
Take a look at this (if its easier to view full screen: http://codepen.io/benfrain/full/tqhKB):
Check out this Pen!
Markup wise there is a surrounding element (the table) and three nested elements set as table-cells. The first and last nested element have been given a fixed width. As the second hasn’t got a width specified it spans the available space. Here’s the total CSS used for that layout:
.tbl {
display: table;
width: 100%;
table-layout: fixed;
}
.cell {
display: table-cell;
height: 300px;
}
.fixed1 {
background-color: aquamarine;
width: 200px;
}
.fluid {
background-color: hotpink;
}
.fixed2 {
background-color: wheat;
width: 150px;
}
Especially handy is the fact that both nested elements that are set as table-cells have equal height. Whatever height the surrounding table is set to (either explicitly with CSS or via the internal content) will be the height that the nested table-cell elements will be. Perfect for multi-column layouts that need to have equal height columns.
Working with nested elements that require overflow hidden
Let’s build upon the previous example. Suppose you have a display: table; for the outermost element (in the hierarchical sense) of your structure. Then within that you have display: table-cell; elements (perhaps for the main content and sidebars). Within one part you have a horizontal scrollable area. You might achieve this by using a div that’s too large and sticking overflow:
Check out this Pen!
If you’re using display: table; to surround the element and you don’t have table-layout: fixed;
set you may be wondering where one of your table-cells has disappeared too.
table-layout: fixed
By default most browsers use an ‘auto’ table layout mode. This causes the width of the table to be governed by its content. Not a problem usually until you introduce a larger element within (such as our horizontally scrollable area). In these instances simply ensure that table-layout: fixed;
is applied.
This makes the table set the width according to the width of the table width (or by the width of the first row of cells). Anything within, that exceeds the boundaries, automatically gets overflow applied to clip that content. It’s worth noting that table-layout: fixed is also supposedly a faster layout system so it might be worth using as a default property on the class you use to add the table display.
Further still – a simple carousel using display: table and display: table-cell
Brad Frost is a huge fan of carousels (Hi Brad ;)). Despite this, I thought I’d show how you can make a simple carousel effect using display: table;
, display: table-cell;
and a dollop of jQuery. Note: I’ve not tested this extensively (read ‘nowhere outside my own glorious dev bubble’) – it’s just an illustration.
Check out this Pen!
I won’t bore you with how that’s done, take a look at the pen if it piques your interest.
Added bonus – no rounding issues
Something I’ve grumbled about on this blog and in the recent Sass and Compass for Designers book is responsive grid problems. It’s a problem that rears its ugly head when a number of elements are floated one after the other using percentages. However, if you layout things using display: table;
and display: table-cell;
the problems disappear (see Safari – you can add up when you try).
Remember, just because you use display:table at one viewport size, doesn’t mean you need to use it at another
Media queries give us the power to define different layouts at different viewports. Use the display: table at one viewport and display: block (or whatever you like) at another. Do that stretchy screen thing here (unless you’re reading this on a phone/tablet in which case, good luck with that). Here’s the full example:
Check out this Pen!
Extra things to remember when using display: table and display: table-cell
- You cannot nest table-cells within table-cells.
- If you are planning to animate the height of elements set with
display: table;
be aware that you can’t setheight: 0; overflow:hidden;
on an element and expect it to take no vertical space (the anonymous table-cell created around the contents of the table will only ever be as small as its content). - By default nested table-cells take up all available space within the containing table. Therefore two nested table-cells will take up 50% each (all other things being equal). Three nested cells will take up 33.333% each, four 25% each etc. However, if the content within one cell exceeds that, you’ll need to apply widths to set the sizing.
- You CAN nest tables within tables and within table-cells
- Float has no effect on table-cell elements
- Margin should have no effect on elements set to table-cell
- Padding can be used to set internal spacing so
* { box-sizing: border-box}
is your friend. - You can’t set a button element to have
display: table-cell;
. Well you can but it won’t work as you expect when it comes to widths. The browser style sheets will overwrite it. Therefore, opt for an a tag instead in these situations. - In general, form elements (option, button, input etc.) won’t behave when set as table-cell. Always wrap these elements in another element and set the wrapper as
display: table-cell;
instead. - You can easily add space between table-cells with the
border-spacing
property. It can take two values, the first is the horizontal spacing and the second is the vertical. So for example:border-spacing: 4px 0;
sets 4px spacing left and right of each table-cell and zero top and bottom.
Conclusion
There’s lots of fun you can have using the display: table and associated properties. As ever, I’m just scratching the surface. However, hopefully, if you aren’t using them already you’ll find some utility using them in future.
Thanks for this introduction to using table\table-cell in CSS. Might solve a problem I have with laying out teaser boxes that need to have equal height (but hold different amounts of text).
Chris
PS Very much enjoying your book on SASS and COMPASS.