Elementos rotados en CSS que afectan correctamente la altura de sus padres

Resuelto Chris asked hace 11 años • 7 respuestas

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:

Una serie de cuatro elementos a nivel de bloque.  El texto del tercer elemento se gira.

¿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:

El texto de la tercera columna ahora afecta la altura del cuadro principal de modo que el texto cabe dentro del cuadro.

Chris avatar Apr 30 '13 20:04 Chris
Aceptado

Como señala acertadamente el comentario de G-Cyr , el soporte actual writing-modees 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>
Expandir fragmento

Bram Vanroy avatar May 18 '2018 08:05 Bram Vanroy

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 transformpara 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 transformpropiedad CSS ocurren después de que se haya realizado el diseño. En otras palabras, el uso transformde 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 transformfunciona. 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:

  1. De alguna manera construir algún otro elemento cuya altura sea igual al ancho del.element-to-rotate
  2. Escriba nuestra transformación para .element-to-rotatesuperponerla 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-rotatesu ancho ?

El componente clave de nuestra estrategia aquí es el padding: 50% 0on .rotation-wrapper-inner. Este exploit es un detalle excéntrico de la especificación parapadding : que los rellenos porcentuales, incluso para padding-topy padding-bottom, siempre se definen como porcentajes del ancho del contenedor del elemento . Esto nos permite realizar el siguiente truco de dos pasos:

  1. Nos pusimos display: tableen 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 con width: max-content, pero a partir de diciembre de 2017, todavía no max-contentes compatible con Edge ).
  2. Establecemos heightof .rotation-wrapper-inneren 0, luego configuramos su relleno en 50% 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-rotatetenga 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-innerpara ser hermano de .element-to-rotateen lugar de padre.

Mark Amery avatar Dec 17 '2017 22:12 Mark Amery