Grow your CSS skills. Land your dream job.

Rotated Table Column Headers

Published by Chris Coyier

Say you have a table header (i.e. <th>) of "Number of Howler Monkey Species by Country" and the data in the corresponding <td> is like "3". That's an awkward mismatch of width.


Perhaps not a huge problem for two columns, but if you had 20 that would be very hard to navigate and a poor use of space. A better use of space is to rotate the headers so that the column width can be much narrower.

Rotating 90-degrees is too far though. It makes it hard to read.

Interestingly, we get just about the same amount of space saving if we rotate the headers 45 degrees instead, and they are much easier to read.

The Trick

There are a couple of tricks here.

We're going to need to use transform: rotate() to angle the headers. Chrome/Safari lets you do that right on the <th>, but I had trouble with the text disappearing in Firefox that way, so let's do that within a nested <div>. That

we'll force to be the width we want the column to be (it also didn't work to force the cell narrow directly). We're going to need another nested element as well, so...

<th class="rotate"><div><span>Column header 1</span></div></th>
th.rotate {
  /* Something you can count on */
  height: 140px;
  white-space: nowrap;

th.rotate > div {
    /* Magic Numbers */
    translate(25px, 51px)
    /* 45 is really 360 - 45 */
  width: 30px;
th.rotate > div > span {
  border-bottom: 1px solid #ccc;
  padding: 5px 10px;

Note the magic numbers there. I bet some of you are smart enough to figure out the mathematical relationship to all the other numbers going on in there. In fact, my example started out life as a fork of Jimmy Bonney's Column Header Rotation article. I was able to do it without the skew() stuff he was doing which I think makes it a bit simpler, but he also had figured out some math stuff using tan() and cos() which might be a good starting point if you start digging in yourself.


If you go down this road, you might wanna make sure you aren't applying rules that screw up the table if the transforms don't work. Modernizr can test for that and apply support/non-support classes to the <html> element, so you can write stuff like:

/* These aren't needed and will be weird if transforms don't work */
.csstransforms & th.rotate {
  height: 140px;
  white-space: nowrap;

How you want to do the fallback is up to you, but it could be worse than just having the table be super wide:

My final demo has this in place.


See the Pen Rotated Table Headers by Chris Coyier (@chriscoyier) on CodePen.

It's in Sass in case you want to fork it and figure out some awesome way to use variables and math and stuff to make it more adaptable.


  1. mike
    Permalink to comment#

    Can you apply that curved path for text?

    Two word lengths straight, then a slight curve begins to end in 45Âș

  2. Gumnos
    Permalink to comment#

    Any reason not to use the more readable “rotate(-45)” instead of “rotate(315)”?

  3. GeckoTang
    Permalink to comment#

    Great article:)

    However, The demo does’nt work.
    It can’t read the Modenizr script.

  4. MaxArt
    Permalink to comment#

    Remember, you can use writing-mode: tb-lr; for IE<9, eventually adding the legacy filter: flipv fliph;.
    Not gonna try filters for the 45 deg text…

  5. Permalink to comment#

    This is nice, thank you !
    But if you have a mix between rotated and non rotated header cells you have to revert
    to 90 degree rotation and vertically align the text to the bottom. It gets more complicated if you
    have multiple text lines as header cell content, or rotated header cells of different length.

    If all this can be handled entirely with CSS , fantastic !
    Let us know.

  6. T.J. Crowder
    Permalink to comment#

    Sadly, on Chrome, Firefox, and Opera the text spacing and baseline end up being uneven. IE10 looks great, but the others are raggedy.

  7. Permalink to comment#

    Ooooh. Fancy. Thanks for sharing.

  8. Looks just fine in my Firefox.

  9. What is SCSS ?

    • Roger
      Permalink to comment#

      Kind of off topic Mohit, but a legitimate question. SCSS is the evolved syntax of SASS (Syntactically Awesome Style Sheets) and stands for Sassy CSS. These are extensions of CSS adding powerful rules to the basic language (sort of like a programming language) such as the ability to add variables and functions to manipulate values. You can go here to find out more:SASS

  10. Using translate3d(25px, 51px, 0) instead of translate(25px, 51px) cleans up the jaggies a little (Windows), though the baseline is still a little iffy.

  11. That’s pretty cool.
    Thank you for the tip :)

  12. @thewendee
    Permalink to comment#

    This is fancy and yes sir,I like it! Ya know what would would make this a schoch fancier is if these attributes would flip automatically with the language direction. One of the benefits of using tables is that they have the magic flip to match the direction. But heck, we have been balancing floats and absolute positions for for directional changes so we could manage this too. I would use this technique.

  13. Inspired by this article and my needs, expressed in my critic above, I fumbled my way to a JavaScript solution (42 lines) , that fits all my needs.

    A demo is located here, the sources and a Readme are located at GitHub .

  14. Jeffrey Ridout
    Permalink to comment#

    Using transform-origin: left bottom, display: inline-block on the span and box-sizing: border-box on the div and span means the “magic” numbers can be a lot easier. You just have to nudge it to the right, the width of the column.

    transform: translate3d(40px, 1px, 0) rotate(-45deg);
    width: 30px;
    transform-origin: left bottom;
    box-sizing: border-box;
  15. hi
    if table direction => rtl , change this css

    th.rotate > div {
      transform:  translate(45px, -20px)

    thank you

  16. Bharath
    Permalink to comment#

    It really nice workout for small mobile devices. Using space efficiently.

  17. Meena vinay

    Hi I want the table header to be set as you did it in the example .But when i checked ur code it does not works for me.And also i want the slanted values in the footer position also for mapping the values. please help

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".