60

I want to display rotated text as table headers, using the CSS transform property. The header row should adjust its height as needed, but instead the rotated text just overflows:

example wrong

demo fiddle

My question is, how to get the table header to grow as needed? Essentially it should look like this:

example right

Tim Büthe
  • 62,884
  • 17
  • 92
  • 129

12 Answers12

51

use

writing-mode: vertical-lr;

https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode

Mansour Alnasser
  • 4,446
  • 5
  • 40
  • 51
47

If you use a pseudo element and vertical-padding, you may basicly draw a square box or <td> : http://jsfiddle.net/qjzwG/319/

.verticalTableHeader {
    text-align:center;
    white-space:nowrap;
    transform-origin:50% 50%;
    transform: rotate(90deg);

}
.verticalTableHeader:before {
    content:'';
    padding-top:110%;/* takes width as reference, + 10% for faking some extra padding */
    display:inline-block;
    vertical-align:middle;
}

If you want to keep <td> ith a small width, table-layout:fixed + width might help. http://jsfiddle.net/qjzwG/320/

.verticalTableHeader {
    text-align:center;
    white-space:nowrap;
    transform: rotate(90deg);

}
.verticalTableHeader p {
    margin:0 -100% ;
    display:inline-block;
}
.verticalTableHeader p:before{
    content:'';
    width:0;
    padding-top:110%;/* takes width as reference, + 10% for faking some extra padding */
    display:inline-block;
    vertical-align:middle;
}
table {
    text-align:center;
    table-layout : fixed;
    width:150px
}

If you want table to still be able to grow from it's content but not from width of <th> , using a wrapper with a hudge negative margin opposite to dir/direction of document might do : apparently, the closest to your needs, http://jsfiddle.net/qjzwG/320/

<table border="1">
    <tr>
      <th class="verticalTableHeader"><p>First</p></th>
      <th class="verticalTableHeader"><p>Second-long-header</p></th>
      <th class="verticalTableHeader"><p>Third</p></th>
    </tr>
.verticalTableHeader {
    text-align:center;
    white-space:nowrap;
    transform: rotate(90deg);
}
.verticalTableHeader p {
    margin:0 -999px;/* virtually reduce space needed on width to very little */
    display:inline-block;
}
.verticalTableHeader p:before {
    content:'';
    width:0;
    padding-top:110%;
    /* takes width as reference, + 10% for faking some extra padding */
    display:inline-block;
    vertical-align:middle;
}
table {
    text-align:center;
}

HTML from demo and base :

<table border="1">
    <tr>
      <th class="verticalTableHeader">First</th>
      <th class="verticalTableHeader">Second</th>
      <th class="verticalTableHeader">Third</th>
    </tr>
    <tr>
      <td>foo</td>
      <td>foo</td>
      <td>foo</td>
    </tr>
    <tr>
      <td>foo</td>
      <td>foo</td>
      <td>foo</td>
    </tr>
    <tr>
      <td>foo</td>
      <td>foo</td>
      <td>foo</td>
    </tr>
</table>

For older IE , you need to use writing-mode (CSS) :http://msdn.microsoft.com/en-us/library/ie/ms531187%28v=vs.85%29.aspx

Braiam
  • 1
  • 11
  • 47
  • 78
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • Thanks for the answer. What threw me is that your first fiddle link does not reflect the code just above it. It actually encompasses the rest of the answers code too. But i got it working. – Andrew Truckle Mar 28 '20 at 08:52
  • In other words - all your fiddles are the same link. So you only have one actual fiddle there. – Andrew Truckle Mar 28 '20 at 08:53
  • This seems not to work well for tables with dynamic columns. – codegrid May 01 '20 at 10:18
  • @codegrid do you have an example/codepen ? writing-mode is nowdays well implemented and it would be the method to follow. I'd be glad to help you from an example/issue of yours An old codepen of mine :https://codepen.io/gc-nomade/pen/EKQKBe – G-Cyrillus May 01 '20 at 10:29
  • @G-Cyrillus please check: https://plnkr.co/edit/wGTZTGwpr0kjCqqY – codegrid May 02 '20 at 12:21
  • @codegrid you might need a min-width on ths https://plnkr.co/edit/PjvOQtF82cV0QWFg – G-Cyrillus May 02 '20 at 12:35
  • @G-Cyrillus Great! But there is still an issue adjusting the width of the first column (Student Name Column) – codegrid May 02 '20 at 13:23
  • @codegrid is this a fix to the issue ? : `tbody tr > * {vertical-align:top;}` or `tbody tr > :first-child {white-space:nowrap;}`. – G-Cyrillus May 02 '20 at 17:00
12

There are new (experimental) CSS3 feature which does what exactly that: writing-mode.

You have to apply it on a div inside the table cell:

.vrt-header th {
  writing-mode: vertical-lr;
  min-width: 50px; /* for firefox */
}
<table class='vrt-header'>
  <thead>
    <tr>
      <th>First</th><th>Second</th><th>Third</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>foo</td><td>foo</td><td>foo</td>
    </tr>
    <tr>
      <td>foo</td><td>foo</td><td>foo</td>
    </tr>
    <tr>
      <td>foo</td><td>foo</td><td>foo</td>
    </tr>
  </tbody>
</table>

Thanks to @gman - it works in Firefox but not in Chrome. One can wrap the content of th in div to have the vertical text in Chrome js-fiddle demo but it feels like a kludge.

dmitry_romanov
  • 5,146
  • 1
  • 33
  • 36
  • 1
    Actually even `tb-rl` is deprecated if you read the documentation that you linked yourself. You should use `vertical-rl` for it to have more chance of working even in Firefox. – thepio Dec 30 '16 at 13:21
  • 2
    I know, and `sideways-lr` IMHO gives even better result, but as long as chrome doesn't support the option itself, elaborating such details is rather pointless. Answer has snippet which works in FF up to version 45, and the doc-page shows what one should expect if it doesn't. Now we just wait if that becomes a viable option (in year to come, hopefully). – dmitry_romanov Dec 30 '16 at 19:28
  • edited using `vertical-lr`, added `min-width` for td otherwise all the cells collapse on firefox – Omu Apr 24 '17 at 12:27
  • @Omu, you used special class typed by hand in many cells and erased single css selector which does the full job. I am not sure it is a good suggestion. And OP asked to fix th cells explicitly, not for a content of a td cell. I will reread your edit tomorrow and think :) – dmitry_romanov Apr 24 '17 at 15:46
  • @dmitry_romanov changed the td to th, still works (on chrome, ff and ie) – Omu Apr 25 '17 at 13:01
  • @Omu, that is what I mean as better selector: one shouldn't hant for each element (s)he wants to style: css filter does it. I also keep the body of table for user to experiment with it in a snippet if desired. The new css changes only `th`, as was requested. Feel free to touch it to have your name on `min-width` stroke you did - I don't to know how to keep your attribution from my side... Best wishes to you :-) ! – dmitry_romanov Apr 30 '17 at 04:33
  • the snippet doesn't display any vertical text currently – gman Aug 03 '18 at 01:03
  • @gman I checked it - works on Linux desktop https://drive.google.com/file/d/1NMvz27moGkj4qpbUwnmQiS7XGwfvCrfl/view?usp=sharing – dmitry_romanov Aug 04 '18 at 05:53
  • @gman It doesn't work in Chrome despite https://caniuse.com/#feat=css-writing-mode reports it should be fine. – dmitry_romanov Aug 04 '18 at 06:12
  • @gman I updated the answer - one can wrap the content of `th` into `div` and make it vertical, but it looks ugly. Someone will fix it, I hope ))) – dmitry_romanov Aug 04 '18 at 06:21
  • @Dims did you try the link below the snippet, with the workaround for the Chrome described as a kludge? – dmitry_romanov Feb 10 '19 at 06:35
  • 1
    I'm a bit confused. According to the documentation this should only work ob block elements, but not in tables. – Grmpfhmbl Mar 18 '19 at 00:16
  • @Grmpfhmbl, it also says "... and the direction in which inline-level content flows within a block container". May be the table cell is the block-like stuff the inline elements flow within? It was very experimental feature at the time of writing... – dmitry_romanov Mar 19 '19 at 06:53
7

try this:

.vertical-header span {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  text-align: left;
  max-height: 150px;
}
<table border=1>
  <tr>
    <th class="vertical-header"><span>Firstname</span></th>
    <th class="vertical-header"><span>Lastname</span></th>
    <th class="vertical-header"><span>Age</span></th>
  </tr>
  <tr>
    <td>Jill</td>
    <td>Smith</td>
    <td>50</td>
  </tr>
  <tr>
    <td>Eve</td>
    <td>Jackson</td>
    <td>94</td>
  </tr>
</table>
5

I struggled to get my <th>'s aligned exactly how I wanted them (even if some are multiple lines).
This is what worked for me:

example image of table

html    { font-family: Helvetica; }
table   { border-collapse: collapse; }
td, th  { border: 1px solid BurlyWood; }
td      { text-align: center; }
th      { background-color: NavajoWhite; 
          color: SaddleBrown; 
          width:50px;  
          vertical-align: bottom; }
th span { writing-mode: sideways-lr; /* +90°: use 'tb-rl' */
          text-align: left;          /* +90°: use 'right' */
          padding:10px 5px 0; }
<table>
    <tr>
      <th><span>First</span></th>
      <th><span>Second</span></th>
      <th><span>Third<br>Column</span></th>
    </tr>
    <tr>
      <td>foo</td>
      <td>foo</td>
      <td>foo</td>
    </tr>
    <tr>
      <td>foo</td>
      <td>foo</td>
      <td>foo</td>
    </tr>
</table>

I figured I'd share, partly as reference for "future me".

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
  • I found this same solution elsewhere, and it's much better than the accepted answers. Putting your cell content inside a node also fixed issues I was seeing with background-color attributes not staying within the cell boundaries. – Paul Ostrowski May 27 '21 at 15:12
4

well... I know this is not the best solution but you can correct it with client side javascript. In jQuery it would look like this:

$(".verticalTableHeader").each(function(){$(this).height($(this).width())})

as for a pure HTML or CSS solution, I think this is a browser limitation.

Joseph Marikle
  • 76,418
  • 17
  • 112
  • 129
  • That would be a possible solution, but I can't get it to work in my fiddle. – Tim Büthe Aug 09 '11 at 14:36
  • @TimBüthe did you include jQuery? Seems to work for me: http://jsfiddle.net/tN2Zx/ – Joseph Marikle Aug 09 '11 at 14:37
  • 4
    The problem is that the original lenght of the header is used to calculate the width of the column. So a long header text makes the column wide, even when it is not needed after teh rotation. – Jirka Feb 25 '13 at 08:57
  • I agree with @Jirka This works great, but makes the column header wider than it needs to be. – Jack Fairfield Sep 20 '17 at 15:33
0

To avoid js using I can propose to use flex in first table row. A little messy with borders in headers, but it could be fixed in thin setup:

.header-row {
  display: flex;
  flex-direction: row;
}

span {
    writing-mode: vertical-lr;
    transform: rotate(180deg);
    width: 23px;
    border-left: 1px solid black;
}

table {
  border: 1px solid black;
  border-spacing: 0px;
}

td {
  border: 1px solid black;
  width: 20px;
}
<table>
  <tr>
    <td></td>
    <td colspan="5" style="padding:0;border:none">
      <div class="header-row">
        <span>Header fsdafasd</span>
        <span>Header fsda</span>
        <span>Header fsdafa fsdaf</span>
        <span>Header asdf</span>
        <span>Header fsda</span>
      </div>
    </td>
  </tr>
  <tr>
    <td>Test name fadsf</td>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>4</td>
    <td>4</td>
  </tr>
  <tr>
    <td>Test name</td>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>4</td>
    <td>4</td>
  </tr>
  <tr>
    <td>Test name</td>
    <td>1</td>
    <td>2</td>
    <td>3</td>
    <td>4</td>
    <td>4</td>
  </tr>
</table>
Andrey Kucher
  • 445
  • 4
  • 7
0

I created something similar, I needed the text to be vertically aligned but without rotation, I did it with the following CSS attributes:

writing-mode: vertical-lr;
text-orientation: upright;

See the example below:

thead {
  background-color: black;
  color: white;
}
tbody tr td:nth-child(1) {
  text-align: center;
}
.vertical {
  writing-mode: vertical-lr;
    text-orientation: upright;
  background-color: silver;
  font-weight: bold;
}
<table width="100%">
  <thead>
    <th>Week</th>
    <th colspan="2">Content</th>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td rowspan="3" class="vertical">BASICS</td>
      <td>Topic 1</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Topic 2</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Topic 3</td>
    </tr>
  </tbody>
</table>
Jonathan Arias
  • 471
  • 7
  • 18
-1
<thead><tr><th class="rotate"><div>header 1></div></th><th class="rotate"><div>header 2></div></th></tr></thead>

apply CSS to rotate class

th.rotate {
        height: 110px; /* header height */
        white-space: nowrap;
    }

th.rotate > div {
    transform:
       translate(25px, 51px)
       rotate(90deg);
       /* rotate(315deg); for diagnol */
    width: 30px;
    margin-left: -35px;
    margin-top: -30px;
}
jai3232
  • 383
  • 3
  • 6
-1

This works for me

css

.v-text {
    transform: rotate(270deg);
    display: block;
 }

html

<table>
  <th>HEADER 1</th>
  <th>
    <p class="v-text">VERTICAL</p>
  </th>
<table>
-1

enter image description here

<style type="text/css">
        

.rotate > div {
    text-align: center;
    white-space:nowrap;
    g-origin:50% 50%;
    -webkit-transform: rotate(-90deg);
    -moz-transform: rotate(-90deg;
    -ms-transform: rotate(-90deg;
    -o-transform: rotate(-90deg;
    transform: rotate(-90deg);
}
       

https://jsfiddle.net/amrangry/w4ja3qo2/1/

Amr Angry
  • 3,711
  • 1
  • 45
  • 37
-2

I had a hard time getting these tweaks to work in a consistent manner. Just in case this helps someone, my solution was to create images of vertical text.

kmxr
  • 479
  • 4
  • 4