Saltar al contenido
transformaciones.css · devschool

Transformaciones en CSS

Lección 34 de 50 · 10 min de lectura · Actualizado el

En esta lección
  1. La propiedad transform
  2. Mover: translate()
  3. Escalar: scale()
  4. Girar: rotate()
  5. Inclinar: skew()
  6. Las propiedades individuales: translate, rotate y scale
  7. El punto de origen: transform-origin
  8. Combinar transformaciones: el orden importa
  9. Transformaciones en 3D
  10. Efectos secundarios que conviene saber
  11. Errores frecuentes
  12. Resumen

Una transformación cambia cómo se ve un elemento en pantalla: lo mueve, lo agranda, lo gira o lo inclina, incluso en tres dimensiones. Lo especial es que lo hace sin mover a los demás: el resto de la página se comporta como si el elemento siguiera en su sitio. Por eso las transformaciones son la base de casi todos los efectos al pasar el ratón y de las animaciones fluidas. En esta lección verás las cuatro funciones básicas, las propiedades individuales modernas, el punto de origen, por qué importa el orden y cómo hacer una tarjeta que gira en 3D.

La propiedad transform

transform recibe una o varias funciones de transformación:

.insignia { transform: rotate(-8deg); }

Una idea clave antes de empezar: la transformación es solo visual. El elemento sigue ocupando su hueco original en la página. Si mueves una caja 50px a la derecha con transform, la caja de al lado no se desplaza; como mucho, se solapan.

.boton:hover {
  transform: translateY(-3px);   /* sube 3px sin empujar nada */
}

Si lo hicieras con margin-top: -3px, todo lo que va debajo subiría también y la página “temblaría” al pasar el ratón.

Mover: translate()

.a { transform: translate(20px, 10px); }   /* 20px a la derecha, 10px abajo */
.b { transform: translateX(-100%); }       /* a la izquierda, todo su ancho */
.c { transform: translateY(8px); }         /* 8px hacia abajo */

Los valores positivos van a la derecha y hacia abajo. Lo interesante son los porcentajes: se calculan sobre el tamaño del propio elemento, no del padre. translateX(-100%) mueve el elemento exactamente su propio ancho, mida lo que mida. Por eso se usa para menús laterales que se esconden fuera de la pantalla:

.menu-lateral { transform: translateX(-100%); }  /* fuera, a la izquierda */
.menu-lateral.abierto { transform: translateX(0); }

Y por eso existe este truco clásico para centrar un elemento posicionado:

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);   /* retrocede la mitad de su propio tamaño */
}

top: 50% coloca la esquina superior en el centro; el translate la devuelve la mitad de su tamaño. Hoy hay formas más sencillas de centrar, que verás en centrar elementos.

Escalar: scale()

.a { transform: scale(1.1); }        /* 10 % más grande */
.b { transform: scale(0.5); }        /* a la mitad */
.c { transform: scale(2, 1); }       /* el doble de ancho, mismo alto */
.d { transform: scaleX(-1); }        /* espejo horizontal */

1 es el tamaño normal. Con un número negativo, el elemento se refleja: scaleX(-1) da la vuelta a un icono de flecha para que apunte al otro lado.

El texto escalado se agranda como una foto, sin reorganizarse. Para cambiar el tamaño real de un texto, usa font-size; scale es para efectos breves, como resaltar una tarjeta al pasar el ratón.

Girar: rotate()

.a { transform: rotate(45deg); }     /* 45 grados en el sentido del reloj */
.b { transform: rotate(-10deg); }    /* al revés */
.c { transform: rotate(0.5turn); }   /* media vuelta = 180deg */

Un uso muy práctico: la flechita de un desplegable que gira cuando se abre.

.flecha { transition: transform 0.2s; }
details[open] .flecha { transform: rotate(180deg); }

Inclinar: skew()

skew inclina el elemento, como si empujaras de lado un rectángulo hasta convertirlo en un paralelogramo:

.a { transform: skewX(-15deg); }
.b { transform: skew(10deg, 5deg); }   /* en X y en Y */

Es la menos usada. Queda bien en rótulos y etiquetas con aire deportivo. Si inclinas un contenedor, su texto se inclina también; para enderezarlo, aplica al hijo la inclinación contraria:

.etiqueta { transform: skewX(-15deg); }
.etiqueta span { display: inline-block; transform: skewX(15deg); }

Las propiedades individuales: translate, rotate y scale

Desde 2022, todos los navegadores admiten tres propiedades con el mismo nombre que las funciones:

.tarjeta {
  translate: 0 -4px;     /* x y, separados por espacio, sin comas */
  rotate: 2deg;
  scale: 1.05;
}

¿Para qué, si ya existe transform? Porque transform es una sola propiedad: si en el :hover cambias una función, tienes que repetir todas las demás o se pierden.

/* Con transform: hay que repetirlo todo */
.carta       { transform: rotate(-3deg); }
.carta:hover { transform: rotate(-3deg) scale(1.1); }  /* si olvidas el rotate, se endereza */

/* Con propiedades individuales: cada una va por su lado */
.carta       { rotate: -3deg; }
.carta:hover { scale: 1.1; }

Además, se pueden animar por separado, cada una con su duración. Las tres se aplican siempre en el mismo orden: primero translate, luego rotate, luego scale, y por último lo que haya en transform.

El punto de origen: transform-origin

Toda transformación ocurre respecto a un punto, por defecto el centro del elemento. Si giras una caja, gira sobre su centro. Con transform-origin cambias ese punto:

.puerta {
  transform-origin: left center;   /* gira como una puerta, sobre su lado izquierdo */
  transform: rotateY(-60deg);
}
.aguja {
  transform-origin: bottom center; /* la aguja de un reloj gira desde abajo */
  rotate: 90deg;
}
.subrayado {
  transform-origin: left;          /* crece de izquierda a derecha */
  transform: scaleX(0);
}
.enlace:hover .subrayado { transform: scaleX(1); }

Acepta palabras (top, right, bottom, left, center), porcentajes o medidas: transform-origin: 20% 80%.

Combinar transformaciones: el orden importa

Puedes poner varias funciones en un mismo transform, separadas por espacios:

.a { transform: translateX(100px) rotate(45deg); }
.b { transform: rotate(45deg) translateX(100px); }

Parecen iguales, pero no lo son. Cada función transforma también los ejes de las que vienen después:

  • .a: primero se mueve 100px a la derecha y después gira sobre sí misma. Acaba a la derecha, girada.
  • .b: primero gira 45 grados, y con ella giran sus ejes. Después “100px a la derecha” significa 100px en la dirección girada: acaba en diagonal, abajo a la derecha.

Una regla práctica: si quieres mover un elemento y además girarlo sobre sí mismo, pon el translate primero.

Nota: transform no funciona en elementos en línea como <span> o <a>. Si no ves ningún efecto, añade display: inline-block.

Transformaciones en 3D

Además de los ejes X (horizontal) e Y (vertical), existe el eje Z, que sale de la pantalla hacia ti.

.a { transform: rotateX(45deg); }      /* gira como una persiana */
.b { transform: rotateY(45deg); }      /* gira como una puerta */
.c { transform: rotateZ(45deg); }      /* igual que rotate() */
.d { transform: translateZ(50px); }    /* se acerca a ti */

perspective: dar profundidad

Sin perspectiva, un giro en 3D se ve plano: la caja simplemente parece más estrecha. La perspectiva es la distancia entre tus ojos y la pantalla. Cuanto menor, más exagerado el efecto:

.escena { perspective: 800px; }   /* en el PADRE: todos los hijos comparten punto de vista */
.carta  { transform: rotateY(30deg); }

También existe la función perspective() dentro de transform, que afecta solo a ese elemento: transform: perspective(800px) rotateY(30deg);. Para varios elementos en la misma escena, usa la propiedad en el padre. Valores entre 600px y 1200px dan un resultado natural.

La tarjeta que gira

Es el ejemplo 3D más habitual: una tarjeta con dos caras que se da la vuelta al pasar el ratón. Necesitas tres piezas:

<div class="escena">
  <div class="tarjeta">
    <div class="cara frente">Clase de escalada</div>
    <div class="cara dorso">Martes y jueves, 19:00</div>
  </div>
</div>
.escena {
  width: 240px;
  height: 160px;
  perspective: 800px;               /* 1. la profundidad */
}
.tarjeta {
  position: relative;
  height: 100%;
  transform-style: preserve-3d;     /* 2. los hijos viven en el mismo espacio 3D */
  transition: transform 0.6s;
}
.escena:hover .tarjeta {
  transform: rotateY(180deg);
}
.cara {
  position: absolute;
  inset: 0;                         /* las dos caras, una encima de otra */
  backface-visibility: hidden;      /* 3. no se ve la cara de atrás */
}
.dorso {
  transform: rotateY(180deg);       /* empieza ya dada la vuelta */
}
  1. perspective en la escena da profundidad.
  2. transform-style: preserve-3d hace que las caras giren con la tarjeta en 3D, en lugar de aplanarse.
  3. backface-visibility: hidden oculta cada cara cuando está de espaldas. El dorso empieza girado 180 grados, así que al principio no se ve; cuando la tarjeta gira, se intercambian.

El :hover se pone en la escena y no en la tarjeta: si la tarjeta gira bajo el ratón, a mitad de giro puede “perder” el puntero y volver atrás de golpe.

Efectos secundarios que conviene saber

Aplicar cualquier transform (aunque sea translate(0)) tiene dos consecuencias poco intuitivas:

  • Crea un contexto de apilamiento nuevo, lo que puede cambiar cómo funciona el z-index de sus hijos. Lo verás en z-index y capas.
  • Los hijos con position: fixed dejan de fijarse a la ventana y se fijan a ese elemento. Si un modal “fijo” se desplaza con la página, busca un transform en algún antepasado.

Errores frecuentes

  • Esperar que transform mueva a los vecinos. No lo hace: el hueco original se mantiene. Para cambiar el diseño de verdad, usa márgenes, Flexbox o Grid.
  • Sobrescribir un transform en el :hover. transform: scale(1.1) en el :hover borra el rotate() que tenía. Repite todas las funciones o usa las propiedades individuales.
  • Usar comas. transform: rotate(10deg), scale(2) no es válido. Las funciones se separan con espacios. Y en la propiedad translate tampoco hay comas: translate: 10px 20px.
  • Aplicarlo a un <span>. Los elementos en línea no se transforman. Usa inline-block.
  • Poner perspective en el elemento que gira. La propiedad perspective va en el padre; en el propio elemento, usa la función perspective().
  • Olvidar transform-style: preserve-3d. Sin ella, la tarjeta 3D se aplana y las dos caras se ven mal.

Resumen

Función o propiedadQué haceEjemplo
translate()Mueve (los % son del propio elemento)translate(-50%, -50%)
scale()Agranda o encogescale(1.1), scaleX(-1)
rotate()Girarotate(45deg), rotate(0.5turn)
skew()InclinaskewX(-15deg)
translate, rotate, scalePropiedades sueltas, independientesscale: 1.05;
transform-originPunto sobre el que se transformaleft center
Varias funcionesSe aplican en orden, cambiando los ejestranslateX(50px) rotate(30deg)
perspectiveProfundidad 3D (en el padre)perspective: 800px
rotateX(), rotateY(), translateZ()Transformaciones en 3DrotateY(180deg)
transform-style: preserve-3dLos hijos comparten el espacio 3DTarjeta que gira
backface-visibility: hiddenOculta la cara de atrásTarjeta que gira

Las transformaciones lucen de verdad cuando se mueven. Con transition ya puedes animarlas al pasar el ratón (lo tienes en transiciones); para movimientos que se repiten o empiezan solos, sigue con animaciones.

Pruébalo tú

Cambia el código y pulsa Ejecutar (o Ctrl + Enter).

transformaciones.html
Resultado

Pon a prueba lo que has aprendido

[CSS] Un <span> con transform: rotate(10deg) no gira. ¿Por qué?

[CSS] Tienes transform: rotate(10deg) y en :hover pones transform: scale(1.1). ¿Qué ocurre al pasar el ratón?

[CSS] ¿Dónde se pone la propiedad perspective para dar profundidad a una tarjeta 3D que gira?

[CSS] Un elemento tiene transform: translate(100px). ¿Qué pasa con sus vecinos?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.