Suponiendo que desea rotar 90 grados, esto es posible, incluso para elementos que no son de texto, pero como muchas cosas interesantes en CSS, requiere un poco de astucia. Mi solución también invoca técnicamente un comportamiento indefinido de acuerdo con la especificación CSS 2, así que aunque he probado y confirmado que funciona en Chrome, Firefox, Safari y Edge, no puedo prometerle que no se romperá en el futuro. lanzamiento del navegador.
Respuesta corta
Dado HTML como este, donde desea rotar .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduzca dos elementos de envoltura alrededor del elemento que desea rotar:
<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>
... y luego use el siguiente CSS, para rotar en sentido antihorario (o vea el comentario transform
para una forma de cambiarlo a una rotación en sentido horario):
.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;
}
Demostración de fragmentos de pila
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>
¿Como funciona esto?
La confusión ante los encantamientos que he usado anteriormente es razonable; están sucediendo muchas cosas, y la estrategia general no es sencilla y requiere algunos conocimientos de trivia de CSS para comprender. Veámoslo paso a paso.
El núcleo del problema al que nos enfrentamos es que las transformaciones aplicadas a un elemento utilizando su transform
propiedad CSS ocurren después de que se ha realizado el diseño. En otras palabras, el uso transform
de un elemento no afecta en absoluto el tamaño o la posición de su padre ni de ningún otro elemento. No hay absolutamente ninguna manera de cambiar este hecho de cómo transform
funciona. Por lo tanto, para crear el efecto de rotar un elemento y hacer que la altura de su padre respete la rotación, debemos hacer lo siguiente:
- De alguna manera construya algún otro elemento cuya altura sea igual al ancho del
.element-to-rotate
- Escriba nuestra transformación
.element-to-rotate
para superponerla exactamente en el elemento del paso 1.
El elemento del paso 1 será .rotation-wrapper-outer
. Pero, ¿cómo podemos hacer que su altura sea igual a .element-to-rotate
la anchura de ?
El componente clave de nuestra estrategia aquí es el padding: 50% 0
encendido .rotation-wrapper-inner
. Este exploit es un detalle excéntrico de la especificación parapadding
: ese porcentaje de relleno, incluso para padding-top
y padding-bottom
, siempre se define como porcentajes del ancho del contenedor del elemento . Esto nos permite realizar el siguiente truco de dos pasos:
- Nos pusimos
display: table
en marcha .rotation-wrapper-outer
. Esto hace que tenga un ancho de encogimiento para ajustar , lo que significa que su ancho se establecerá en función del ancho intrínseco de su contenido, es decir, en función del ancho intrínseco de .element-to-rotate
. (En apoyo a los navegadores, podríamos lograr esto de forma más limpia con width: max-content
, pero a partir de diciembre de 2017, max-content
está todavía no se admite en el borde .)
- Establecemos el
height
de .rotation-wrapper-inner
en 0, luego establecemos su relleno en 50% 0
(es decir, 50% superior y 50% inferior). Esto hace que ocupe un espacio vertical igual al 100% del ancho de su padre, que, a través del truco del paso 1, es igual al ancho de .element-to-rotate
.
A continuación, todo lo que queda es realizar la rotación y el posicionamiento reales del elemento hijo. Naturalmente, transform: rotate(-90deg)
hace la rotación; usamos transform-origin: top left;
para hacer que la rotación ocurra alrededor de la esquina superior izquierda del elemento girado, lo que hace que la traslación posterior sea más fácil de razonar, ya que deja el elemento girado directamente encima de donde de otro modo se habría dibujado. Luego, podemos usar translate(-100%)
para arrastrar el elemento hacia abajo una distancia igual a su ancho de prerotación.
Eso todavía no consigue el posicionamiento correcto, porque todavía necesitamos compensar el 50% del relleno superior activado .rotation-wrapper-outer
. Lo logramos asegurándonos de que .element-to-rotate
tiene display: block
(para que los márgenes funcionen correctamente en él) y luego aplicando un -50% margin-top
; tenga en cuenta que los márgenes porcentuales también se definen en relación con el ancho del elemento principal.
¡Y eso es!
¿Por qué no cumple totalmente con las especificaciones?
Debido a la siguiente nota de la definición de porcentajes de relleno y márgenes en la especificación (negrita mía):
El porcentaje se calcula con respecto al ancho del bloque contenedor de la caja generada , incluso para 'padding-top' y 'padding-bottom' . Si el ancho del bloque contenedor depende de este elemento, entonces el diseño resultante no está definido en CSS 2.1.
Dado que todo el truco giraba en torno a hacer que el relleno del elemento de envoltura interior fuera relativo al ancho de su contenedor, que a su vez dependía del ancho de su hijo, estamos alcanzando esta condición e invocando un comportamiento indefinido. Sin embargo, actualmente funciona en los 4 navegadores principales, a diferencia de algunos ajustes de enfoque aparentemente compatibles con las especificaciones que he intentado, como cambiar .rotation-wrapper-inner
para ser hermano en .element-to-rotate
lugar de padre.