Menús desplegables animados con CSS3

Explicamos cómo crear un menú desplegable animado sin necesidad de JavaScript


Los menús desplegables son una interfaz de navegación muy común. Hasta ahora con CSS podíamos hacer que los vínculos de las subsecciones aparecieran sin necesidad de JavaScript, pero si queríamos hacer que el efecto de dicha aparición fuera progresivo, no nos quedaba más opción que programar una animación. Con CSS3, podemos definir el efecto en la hoja de estilo.

Hace ya casi tres años publiqué una prueba de -webkit-transition, una propiedad que permitía especificar tiempos y modos de transición entre dos valores. Los experimentos de Webkit fueron la base del modulo de transiciones de CSS nivel 3, con el que podemos crear pequeñas animaciones.

¿Animaciones? ¿Cómo?

Lo que hacen los navegadores que soportan el módulo de transición es interpolar los pasos intermedios entre el valor de una o varias propiedades de CSS aplicadas a un elemento —o los pseudoelementos :before y :after— y el valor asignado al mismo cuando se cumplen las condiciones que hayamos elegido, por ejemplo, cuando un usuario interactúa con el elemento y se cambia la propiedad en :hover.

La propiedades de transición —que, por cierto, no se heredan— son las siguientes:

Además, contamos con una propiedad abreviada, que es transition, donde podemos indicar todos estos valores separados por espacios. Por ejemplo, en esta declaración:


transition: background-color 0.5s linear 0.25s;
 

los valores corresponden a la propiedad a animar, la duración de la animación, su tipo y el retraso inicial.

Genial. Pero antes de pasar a explicar cómo podemos emplear todo esto para hacer un menú desplegable, vamos a indicar unos cuantos puntos a tener en cuenta al trabajar con animaciones.

Algunas notas

¿Dónde indicar la transición?

Primero y más importante, las propiedades de transición se deben especificar en el elemento que va a sufrirlas. Parece obvio, pero al principio puede ser un tanto contraintuitivo.

Imaginemos que tenemos un vínculo en una barra de navegación y que queremos que su color de fondo cambie de manera progresiva cuando el usuario pase el ratón sobre él. Estas reglas…


a{
  background-color: #D0D0D0;
}

a:hover{
  background-color: #FF4040;
  transition-duration: .5s;
}
 

no funcionan. Las correctas son estas:


a{
  background-color: #D0D0D0;  
  transition-duration: .5s;
}

a:hover{
  background-color: #FF4040;
}
 

¿Se pueden indicar parámetros independientes para varias propiedades?

Sí, de varias formas, dependiendo de lo que resulte más cómodo para el autor.

Primero, podemos especificar cada propiedad con los distintos valores separados por comas:


a{
  color: #FFF;
  background: #333;
  transition-property: color, background;
  transition-duration: .5s, .75s;
  transition-timing-function: ease-in-out;
  transition-delay: .25s, 0;
}
 

Los valores se asignan en el orden en que se indican. Si faltan valores —como aquí en transition-timing-function— estos se asignan por orden a las propiedades hasta agotarse y los restantes reciben los valores por defecto.

La segunda opción es especificar las dos transiciones abreviadas:


a{
  color: #FFF;
  background: #333;
  transition: color .5s ease-in-out .25s;
  transition: background .75s;
}
 

En este caso no especificamos para el fondo ni el tipo de animación ni el retardo, dado que sus valores son los aplicados por defecto.

Y, por último, podemos especificar todos los valores en una sola transición, separando cada animación por comas:


a{
  color: #FFF;
  background: #333;
  transition: color .5s ease-in-out .25s, background .75s;
}
 

¿Y el soporte?

En el momento en que escribo, ninguno de los navegadores actuales ha implementado las propiedades estándar del módulo de transiciones, pero algunos sí soportan las versiones experimentales con los prefijos propietarios:

  Prefijo Desde la versión
Firefox -moz- 4
Safari -webkit- 3.1
Safari (iOS) -webkit- 3.2
Chrome -webkit- 4.0
Opera -o- 10.5
Explorer De momento no hay soporte alguno

Nuestro ejemplo

El menú de navegación consta de una serie de listas anidadas con los vínculos. En el ejemplo hemos empleado muchas otras propiedades de CSS3, pero vamos a ceñirnos estrictamente a la animación del menú:


#navegacion li ul{
  height:0;
  overflow:hidden;
  -moz-transition-duration:.3s;
  -webkit-transition-duration:.3s;
  -o-transition-duration:.3s;
  transition-duration:.3s;
  position:absolute;
}
#navegacion li:hover ul{
  height:17em;
}
 

Simplemente variamos la altura, de 0 a 17em. ¿Y por qué no simplemente cambiar el valor a auto? Buena pregunta. En un principio es lo que probamos, pero parece que la animación no funciona si no se especifica un valor en alguna de las unidades de medida.

En la especificación hay una sección que indica cuando no debe iniciarse una animación, pero no parece que deba ser aplicable en este caso. Tal vez se trate de un bug —ya está reportado como tal en Bugzilla—, pero lo cierto es que ninguno de los navegadores que soportan transiciones animan el elemento con height:auto.

¿CSS o JavaScript?

Hace años ya que hay cierta controversia sobre si una animación pertenece a la capa de presentación o a la de comportamiento —este artículo de Jonathan Snook es de 2007—. Personalmente entiendo que la capa de comportamiento, léase JavaScript, tiene que ver con la funcionalidad que añade a una página —validación de formularios, manipulación del DOM, etc.—, y no tanto con la interpolación entre dos valores relativos al aspecto de la presentación de un elemento. Incluso el W3C las define así: «Transitions are a presentational effect».

Pero no sólo se trata de una cuestión más o menos académica: las transiciones de CSS son más eficientes. Al soportarlas el navegador de forma nativa las animaciones pueden beneficiarse de la aceleración por hardware del disposivo en el que el navegador esté instalado, reducen el consumo de recursos por parte del motor de JavaScript y, lo más importante, como este lenguaje es «mono-hilo» (single-threaded) el efecto no interfiere con otras funciones.

Así pues, viendo que además degrada muy elegantemente, no hay motivo para no empezar a utilizar las transiciones ya mismo.

Esta entrada se publicó el 31 de julio de 2011, se archivó en , y fue etiquetada como animaciones, css3. Autor: Saúl González Fernández. Hay 9 comentarios ›.

Comentarios

  1.  liliana dice:

    Muy bien explicado, por fin encontré una página con los detalles que necesitaba. Gracias.

  2.  Sato dice:

    Excelente blog, muy bien explicado. Si hay alguna manera de ayudar para difundir tu blog no dudes en hacérmelo saber.

  3.  Mauricio dice:

    Bien explicado, la unica duda que tengo con respecto al menú, es cómo hacer que se superponga al texto, o sea, que el despliegue se haga por encima del contenido. Gracias.

  4.  Saúl González Fernández dice:

    @Mauricio: Prueba asignar una posicion relativa al elemento en el que incluyes la navegación y al del contenido; después al primero especifícale un z-index superior al del segundo.

  5.  el Sindrome de Willy Fog dice:

    Hola.

    Estoy intentando que cuando el menu se despliege, lo haga mas lento, es decir, que el submenu me aparezca a una velocidad más lenta, y si puede ser, cada linea a un tiempo.

    No sé muy bien dónde aplicar la transición, ya que sí que consigo aplicarla para que al pasar el raton por encima, la pestaña cambie de color más lentamente, pero no para ese efecto que deseo.

    Os copio el texto a ver si me decís dónde ponerlo.

    Gracias.

    
    #menuWrapper {
      width:100%; /* Ancho del menú */
      height:35px;
      padding-left:14px;
      background:#333333; /* Color de fondo */
      border-radius:20px; /* Bordes redondeados */
    }
    .menu {
      width: 100%;
      float: left;
      font-family:"Lucida Sans Unicode", "Trebuchet Unicode MS", "Lucida Grande",sans-serif;
      font-size:13px; /* Tamaño de la fuente */
      font-weight:bold;
    }
    .menu ul {
      float:left;
      height:0px;
      list-style:none;
      margin:0;
      padding:0;
      border-radius: 0px 0px 20px 20px; /* Bordes redondeados del submenú */
    }
    .menu li{
      float:left;
      padding:0px;
    }
    .menu li a{
      background:#333333 url(http://lh4.googleusercontent.com/-iqgCHipLGx4/T5DqLrsVALI/AAAAAAAACaI/bcIY7SV8s5I/s35/separador.gif) bottom right no-repeat; 
      color:#cccccc; /* Color de la fuente */
      display:block;
      font-weight:normal;
      line-height:35px;
      margin:0px;
      padding:0px 25px; /* Espacio entre cada pestaña */
      text-align:center;
      text-decoration:none;
    }
    .menu li a:hover, .menu ul li:hover a {
      background: #2580a2; /* Color de las pestañas al pasar el cursor */
      color:#FFFFFF; /* Color del texto al pasar el cursor */
      text-decoration:none;
    }
    .menu li ul {
      background:#333333; /* Color de fondo del submenú */
      display:none;
      height:auto;
      padding:0px;
      margin:0px;
      position:absolute;
      width:200px; /* Ancho del submenú */
      z-index:100;
      border-top:1px solid #fff; /* Borde superior del submenú */
    }
    .menu li:hover ul{
      display:block;
    }
    .menu li li {
      display:block;
      float:none;
      margin:0px;
      padding:0px;
      width:200px;
    }
    .menu li:hover li a {
      background:none;
      border-radius: 0px 0px 20px 20px; /* Borde de las subpestañas */
    }
    .menu li ul a {
      display:block;
      height:35px;
      font-size:12px;
      font-style:normal;
      margin:0px;
      padding:0px 10px 0px 15px;
      text-align:left;
    }
    .menu li ul a:hover, .menu li ul li:hover a{
      background:#2580a2; /* Color de las subpestañas al pasar el cursor */
      color:#ffffff;
      text-decoration:none;
    }
     
  6.  Saúl González Fernández dice:

    @el Síndrome de Willy Fog: Nos resultaría más fácil verlo si nos envías una dirección donde podamos echar un vistazo a la página, pero creemos que lo que necesitas es asignar estas propiedades:

    
    .menu li ul {
      /* Aquí tu código original, con las indicaciones que te damos debajo */ 
      height:0;
      overflow:hidden;
      -moz-transition-duration:.3s;
      -webkit-transition-duration:.3s;
      -o-transition-duration:.3s;
      transition-duration:.3s;
    }
     

    Sustituye la altura por 0 en lugar de auto y elimina display:none. En el código la duración que hemos puesto es .3s, sustitúyela por la que creas apropiada.

    
    .menu li:hover ul{
      height: 10em; /* Valor a estimar */
    }
     

    Para esta regla, elimina display:block, y dale la altura que necesites para que se vean todas las opciones del menú.

    Prueba y nos comentas.

  7.  Eduardo dice:

    Hola Saúl,
    No soy el tío que preguntó sobre como hacer que las listas desplegables de su menú salgan más lento pero, déjame decirte que me has salvado de muchas horas más de dolor de cabeza XD. Yo he estado intentando hacer que las listas de mi menú se desplieguen lento y no había podido desde hace más de una semana, no miento. Me dí cuenta desde hace 7 días que el efecto TRANSITION no afecta a los DISPLAY, a pesar de eso intentaba e intentaba todo terco pero no conseguía algo; no se me había ocurrido cambiar el tamaño de las listas desplegables para que funcione el efecto, como tú lo has explicado… muchas gracias por el código, me sirvió mucho…

  8.  Pitux dice:

    Muchas gracias, por fin entendí que la propiedad display no acepta transiciones.

  9.  Fabricio Trujillo dice:

    :O ¡Muchas gracias, me ha servido mucho! Mil gracias ?



Artículos relacionados

Destacar y atenuar elementos simultáneamente con CSS3

Destacar y atenuar elementos simultáneamente con CSS3

En algún blog nos hemos encontrado con un efecto my sutil, pero que nos ha parecido muy elegante: ver una lista de elementos como comentarios, tweets o entradas relacionadas, que al pasar el ratón por encima reaccionan oscureciendo el ítem que recibe el :hover, y atenuando los demás. En muchos casos se trata de un efecto