Elementos rotados en CSS que afectan correctamente la altura de sus padres
Digamos que tengo un par de columnas, de las cuales me gustaría rotar los valores de algunas:
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>
Con este CSS:
.statusColumn b {
writing-mode: tb-rl;
white-space: nowrap;
display: inline-block;
overflow: visible;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
Termina pareciéndose a esto:
¿Es posible escribir algún CSS que haga que el elemento rotado afecte la altura de su padre, de modo que el texto no se superponga a los otros elementos? Algo como esto:
Como señala acertadamente el comentario de G-Cyr , el soporte actual writing-mode
es más que decente . Combinado con una simple rotación, obtienes el resultado exacto que deseas. Vea el ejemplo a continuación.
.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>
Suponiendo que desee 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 según la especificación CSS 2, por lo que, aunque probé y confirmé que funciona en Chrome, Firefox, Safari y Edge, no puedo prometerles que no se romperá en el futuro. lanzamiento del navegador.
Respuesta corta
Dado HTML como este, donde quieres rotar .element-to-rotate
...
<div id="container">
<something class="element-to-rotate">bla bla bla</something>
</div>
... introduce dos elementos envolventes alrededor del elemento que deseas 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 el sentido de las agujas del reloj (o vea el comentario transform
para ver una forma de cambiarlo a una rotación en el sentido de las agujas del reloj):
.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
Mostrar fragmento de código
¿Cómo funciona esto?
La confusión frente a 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 trivialidades de CSS para comprenderla. Repasé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 haya 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 que la altura de su padre respete la rotación, debemos hacer lo siguiente:
- De alguna manera construir algún otro elemento cuya altura sea igual al ancho del
.element-to-rotate
- Escriba nuestra transformación para
.element-to-rotate
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
su ancho ?
El componente clave de nuestra estrategia aquí es el padding: 50% 0
on .rotation-wrapper-inner
. Este exploit es un detalle excéntrico de la especificación parapadding
: que los rellenos porcentuales, incluso para padding-top
y padding-bottom
, siempre se definen 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 contracción para ajustarse , 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 cuanto a la compatibilidad con navegadores, podríamos lograr esto de manera más limpia conwidth: max-content
, pero a partir de diciembre de 2017, todavía nomax-content
es compatible con Edge ). - Establecemos
height
of.rotation-wrapper-inner
en 0, luego configuramos su relleno en50% 0
(es decir, 50% arriba y 50% abajo). Esto hace que ocupe un espacio vertical igual al 100% del ancho de su padre, que, mediante el 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 del elemento secundario. Naturalmente, transform: rotate(-90deg)
hace la rotación; Solemos transform-origin: top left;
hacer que la rotación ocurra alrededor de la esquina superior izquierda del elemento rotado, lo que hace que la traducción posterior sea más fácil de razonar, ya que deja el elemento rotado 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 previo a la rotación.
Eso todavía no logra el posicionamiento correcto, porque todavía necesitamos compensar el 50% del relleno superior en .rotation-wrapper-outer
. Lo logramos asegurándonos de que lo .element-to-rotate
tenga display: block
(para que los márgenes funcionen correctamente) 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é esto 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 del cuadro generado , 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 contenedor interno 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 probado, como cambiar .rotation-wrapper-inner
para ser hermano de .element-to-rotate
en lugar de padre.