Rotated elements in CSS that correctly affect their parent height

Let's say I have some columns from which I would like to rotate the values:

http://jsfiddle.net/MTyFP/1/

<div class="container"> <div class="statusColumn"><span>Normal</span></div> <div class="statusColumn"><a>Normal</a></div> <div class="statusColumn"><b>Rotated</b></div> <div class="statusColumn"><abbr>Normal</abbr></div> </div> 

Using this CSS:

 .statusColumn b { writing-mode: tb-rl; white-space: nowrap; display: inline-block; overflow: visible; transform: rotate(-90deg); transform-origin: 50% 50%; } 

It looks like this:

A series of four block-level elements. The third element's text is rotated.

Is it possible to write any CSS that makes the rotated element affect its parent height so that the text does not overlap other elements? Something like that:

The third column's text now affects its parent box's height such that the text fits within the box.

+71
css css3 css-transforms
Apr 30 '13 at 13:51
source share
8 answers

Assuming you want to rotate 90 degrees, this is even possible for non-text elements, but, like so many interesting things in CSS, it takes a little trick. My solution also technically causes undefined behavior according to the CSS 2 specification, so while I tested and confirmed that it works in Chrome, Firefox, Safari and Edge, I cannot promise that it will not break in a future browser release.

Short answer

Given such HTML where you want to rotate .element-to-rotate ...

 <div id="container"> <something class="element-to-rotate">bla bla bla</something> </div> 

... introduce two wrapper elements around the element you want to rotate:

 <div id="container"> <div class="rotation-wrapper-outer"> <div class="rotation-wrapper-inner"> <something class="element-to-rotate">bla bla bla</something> </div> </div> </div> 

... and then use the following CSS to rotate counterclockwise (or see a missed transform for a way to change it to clockwise rotation):

 .rotation-wrapper-outer { display: table; } .rotation-wrapper-inner { padding: 50% 0; height: 0; } .element-to-rotate { display: block; transform-origin: top left; /* Note: for a CLOCKWISE rotation, use the commented-out transform instead of this one. */ transform: rotate(-90deg) translate(-100%); /* transform: rotate(90deg) translate(0, -100%); */ margin-top: -50%; /* Not vital, but possibly a good idea if the element you're rotating contains text and you want a single long vertical line of text and the pre-rotation width of your element is small enough that the text wraps: */ white-space: nowrap; } 

Demo version

 p { /* Tweak the visuals of the paragraphs for easier visualiation: */ background: pink; margin: 1px 0; border: 1px solid black; } .rotation-wrapper-outer { display: table; } .rotation-wrapper-inner { padding: 50% 0; height: 0; } .element-to-rotate { display: block; transform-origin: top left; /* Note: for a CLOCKWISE rotation, use the commented-out transform instead of this one. */ transform: rotate(-90deg) translate(-100%); /* transform: rotate(90deg) translate(0, -100%); */ margin-top: -50%; /* Not vital, but possibly a good idea if the element you're rotating contains text and you want a single long vertical line of text and the pre-rotation width of your element is small enough that the text wraps: */ white-space: nowrap; } 
 <div id="container"> <p>Some text</p> <p>More text</p> <div class="rotation-wrapper-outer"> <div class="rotation-wrapper-inner"> <p class="element-to-rotate">Some rotated text</p> </div> </div> <p>Even more text</p> <img src="https://i.stack.imgur.com/ih8Fj.png"> <div class="rotation-wrapper-outer"> <div class="rotation-wrapper-inner"> <img class="element-to-rotate" src="https://i.stack.imgur.com/ih8Fj.png"> </div> </div> <img src="https://i.stack.imgur.com/ih8Fj.png"> </div> 

How it works?

Confusion in the face of the spells I used above is reasonable; there is a lot going on, and the general strategy is not simple and requires some knowledge of CSS trivia to understand. Skip it step by step.

The core of the problem we are facing is that the transformations applied to an element using its CSS transform property occur after the layout has occurred. In other words, using transform in an element does not affect the size or position of its parent or any other elements. There is absolutely no way to change this fact of how transform works. Thus, to create the effect of rotation of the element and its parent height, observe the rotation, we need to do the following:

  1. Somehow we are constructing some other element whose height is equal to the width .element-to-rotate
  2. Write our transform on .element-to-rotate to superimpose it exactly on the element from step 1.

The element from step 1 should be .rotation-wrapper-outer . But how can we bring its height to .element-to-rotate ?

A key component of our strategy is padding: 50% 0 on .rotation-wrapper-inner . This uses the eccentric detail of the specification for padding : this percentage padding , even for padding-top and padding-bottom , is always defined as the percentage of the width of the element container. This allows us to perform the following two-stage trick:

  1. We set display: table to .rotation-wrapper-outer . This leads to the fact that it has a shrinkage width to a width , which means that its width will be set based on the inner width of its contents, that is, based on the inner width. .element-to-rotate . (With browser support, we could achieve this more cleanly with width: max-content , but as of December 2017, max-content still not supported in Edge ).
  2. We set the height .rotation-wrapper-inner to 0, and then set its padding to 50% 0 (i.e. 50% top and 50% bottom). This forces him to occupy a vertical space equal to 100% of the width of his parent element, which, by the trick in step 1, is equal to the width. .element-to-rotate .

Then it remains only to perform the actual rotation and positioning of the child. Naturally transform: rotate(-90deg) ; we use transform-origin: top left; to make the rotation take place around the upper left corner of the rotated element, which simplifies the subsequent translation as it leaves the rotating element immediately above where it would otherwise be drawn. Then we can use translate(-100%) to drag the element down a distance equal to its width before rotation.

This is still not quite correctly positioned, because we still need to compensate for 50% of the top fill on .rotation-wrapper-outer . We achieve this by ensuring that .element-to-rotate is set to display: block (so that the fields will work correctly on it), and then applying -50% margin-top - note that percentage fields are also defined relative to the width of the parent element.

And this!

Why is this not fully compliant?

Due to the following note from the definition of percentage gaskets and fields in the specification (greasy ground):

The percentage is calculated by the width of the generated block containing the block , even for padding-top and padding-bottom . If the width of this block depends on this element, then the resulting layout is not defined in CSS 2.1.

Since the whole trick revolved around the fact that the addition of the inner wrapper element was related to the width of its container, which, in turn, depended on the width of its child element, we encounter this condition and cause undefined behavior. It currently works in all 4 major browsers, although, unlike some seemingly speculative settings that suit me, such as modifying .rotation-wrapper-inner to be an .element-to-rotate element .element-to-rotate instead of parent.

+16
Dec 17 '17 at 22:21
source share

Unfortunately (?), This is how it should work, even if you rotate your element, it still has a certain width and height, which does not change after rotation. You visually change it, but there is no invisible wrapping box that changes its size when turning things.

Imagine that it rotates less than 90 Β°; (e.g. transform: rotate(45deg) ): you will need to enter an invisible box that is now ambiguous, based on the original size of the object you are rotating, and the actual rotation value.

Rotated object

Suddenly, you have not only the width , but also the height object you rotated, but you also have the width and height "invisible window" around it. Imagine asking for the outer width of this object - what would it return to? The width of the object or our new box? How will we distinguish between both?

Therefore, CSS does not exist so you can write to fix this behavior (or should I say, β€œautomate” it). Of course, you can increase the size of your parent container manually or write JavaScript for it.

(To be clear, you can try element.getBoundingClientRect to get the rectangle mentioned above).

As described in the specification :

In the HTML namespace, the transform property does not affect the flow of content surrounding the transformed element.

This means that no changes will be made to the content surrounding the object that you are transforming if you do not do it manually.

The only thing that is taken into account when transforming an object is the overflow area:

(...) the degree of the overflow area takes into account the transformed elements. This behavior is similar to what happens when elements are shifted through relative positioning .

Check out this jsfiddle to find out more.

Actually, it's nice to compare this situation with the displacement of the object using: position: relative - the surrounding content does not change, even if you move your object ().


If you want to handle this with JavaScript, check out this question.

+35
Apr 30 '13 at 14:21
source share

Use percentages for padding and pseudo-elements to promote content. In JSFiddle, I left the red pseudo-element to show it, and you have to compensate for the text shift, but I think this is the way to go.

 .statusColumn { position: relative; border: 1px solid #ccc; padding: 2px; margin: 2px; width: 200px; } .statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong { writing-mode: tb-rl; white-space: nowrap; display: inline-block; overflow: visible; -webkit-transform: rotate(-90deg); -moz-transform: rotate(-90deg); -ms-transform: rotate(-90deg); -o-transform: rotate(-90deg); transform: rotate(-90deg); -webkit-transform: rotate(-90deg); /* also accepts left, right, top, bottom coordinates; not required, but a good idea for styling */ -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; -ms-transform-origin: 50% 50%; -o-transform-origin: 50% 50%; transform-origin: 50% 50%; /* Should be unset in IE9+ I think. */ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); } .statusColumn b:before{ content:''; padding:50% 0; display:block; background:red; position:relative; top:20px } 
 <div class="container"> <div class="statusColumn"><span>Normal</span></div> <div class="statusColumn"><a>Normal</a></div> <div class="statusColumn"><b>Rotated</b></div> <div class="statusColumn"><abbr>Normal</abbr></div> </div> 

http://jsfiddle.net/MTyFP/7/

A record of this solution can be found here: http://kizu.ru/en/fun/rotated-text/

+25
Apr 30 '13 at 14:07
source share

See the updated version in my other answer . This answer is no longer valid and just doesn't work anymore. I will leave it here for historical reasons.




This question is pretty old, but with the current support for the .getBoundingClientRect() object and its width and height values ​​combined with the ability to use this neat method along with transform , I think my solution should also be mentioned.

See in action here . (Tested on Chrome 42, FF 33, and 37.)

getBoundingClientRect computes the actual width and height of the item's window . Simply put, we can scroll through all the elements and set its minimum height to the actual window height of our children.

 $(".statusColumn").each(function() { var $this = $(this), child = $this.children(":first"); $this.css("minHeight", function() { return child[0].getBoundingClientRect().height; }); }); 

(With some modification, you can scroll through the children and find out which one is the tallest and set the height of the parent of the tallest child. However, I decided to make the example simpler. You can also add the parent addition to the height if you want, or you can use the appropriate box-sizing value in CSS.)

Notice, however, that I added translate and transform-origin to your CSS to make positioning more flexible and accurate.

 transform: rotate(-90deg) translateX(-100%); transform-origin: top left; 
+10
May 13 '15 at 9:59
source share

As G-Cyr rightly notes, the current writing-mode support is more than worthy . Combined with simple rotation, you get the exact result you want. See the example below.

 .statusColumn { position: relative; border: 1px solid #ccc; padding: 2px; margin: 2px; width: 200px; } .statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong { writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap; display: inline-block; overflow: visible; } 
 <div class="container"> <div class="statusColumn"><span>Normal</span></div> <div class="statusColumn"><a>Normal</a></div> <div class="statusColumn"><b>Rotated</b></div> <div class="statusColumn"><abbr>Normal</abbr></div> </div> 
+2
May 18 '18 at 8:23
source share

The recording mode property will do this. The current writing-mode support is more than worthy . Combined with simple rotation, you get the exact result you want. See the example below.

 .statusColumn { position: relative; border: 1px solid #ccc; padding: 2px; margin: 2px; width: 200px; } .statusColumn i, .statusColumn b, .statusColumn em, .statusColumn strong { writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap; display: inline-block; overflow: visible; } 
 <div class="container"> <div class="statusColumn"><span>Normal</span></div> <div class="statusColumn"><a>Normal</a></div> <div class="statusColumn"><b>Rotated</b></div> <div class="statusColumn"><abbr>Normal</abbr></div> </div> 

(Split from Bram Wann's answer )

+1
Mar 27 '18 at 9:38
source share

Based on Bram Vanvis answer, here is the RotatedImage React component

https://codesandbox.io/s/42mo26oy5x

0
Nov 22 '17 at 11:55
source share

I know this old post, but I found it, struggling with the same problem. The solution that works for me is a rather crude "low tech" method, just surrounding the div, which I rotate 90 degrees with a lot of

 <br> 

Knowing the approximate width (which becomes height after rotation) of the div, I can compensate for the difference by adding br around this div, so the contents above and below are pushed accordingly.

-17
Jul 16 '14 at 20:40
source share



All Articles