FlexBox: La caja flexible con CSS 3

Este era un artículo original del 2012. Dado el momento actual y ya definitivo de Flexbox, ha llegado el momento de poner este artículo al día para que os sea útil. Flexbox ya está listo. Es el momento de usarlo.

Flexbox viene a solucionar uno de los mayores quebraderos de los diseñadores de páginas web: cómo crear páginas flexibles, fluidas y dinámicas que funcionen en la amplia variedad de dispositivos existentes utilizando CSS. Flexbox elimina de un plumazo la complejidad de crear este tipo de páginas.

Vamos a ver en este artículo que gracias a Flexbox podemos construir componentes de nuestra páginas web olvidándonos de los “float” y “position” de turno.

En qué consiste FlexBox

Flexbox lleva ya tiempo funcionando, pero su sintaxis ha cambiado varias veces. La última (en el momento de escribir este artículo), como veréis en el enlace de W3C, es del 17 de Abril del 2014.

El objetivo de FlexBox es crear un modelo de caja o contenedor optimizado para los distintos dispositivos que usan los usuarios de una web. En CSS 2.1 se establecieron cuatro formas de crear una caja: block, inline, table y position. Ahora se crea un modelo de caja nuevo, la caja flexible o FlexBox con display: flex.

Funciona ajustando los tamaños y la disposición de los elementos que se encuentran dentro de un contenedor o caja, de tal manera que se adapten siempre al espacio disponible. Permite posicionar dichos elementos internos con gran facilidad, de manera independiente al orden en el que aparezcan en el código.

Como veremos FlexBox está pensado para componentes de una web o aplicación, a diferencia de Grid Layout, que espero veamos en otro artículo del blog, que está pensado para el diseño y disposición de la web en su conjunto. No es recomendable usar FlexBox para crear la disposición de toda la página, sino sólo para sus componentes.

En su día os comentaba que Flexbox era aún experimental y que sólo funcionaba con Chrome Canary. Hoy en día no es ya así. Como podéis ver en Can I use los nuevos navegadores ya lo soportan. Necesitaréis usar los prefijos de cada navegador, es decir, -ms-, -moz-, -o-, y -webkit- en algunos navegadores, como Safari 7 o Internet Explorer 10. Mira este artículo si necesitas (lo más probable) soportar los navegadores más antiguos.

display: flex

La declaración display: flex; define un “contenedor flexible” y convierte de forma automática a sus “hijos” directos en “elementos flexibles”. Un contenedor flexible tiene un Eje principal (main axis), que es la dirección en la cual se posicionan los elementos flexibles. Y tiene un Eje transversal, perpendicular al Eje principal. Ambos ejes tienen una serie de propiedades que controlan cómo se posiciona cada elemento flexible en relación a los demás. Puedes además poner contenedores flexibles uno dentro de otro, ya que la pripiedad display no se hereda de forma automática. Vamos a ver un gráfico que nos lo muestra mejor:

Flexbox gráfico

Empezamos con un ejemplo

Vamos a centrarnos en un ejemplo sencillo, perfecto para usar FlexBox (recordad que necesitáis un navegador moderno para ver bien la demo). Os podéis bajar aquí el código del ejemplo. Como podéis ver es un catálogo de viajes donde cada viaje tiene un título, una foto, una descripción y un botón de “Más información”. Lo que queremos es que cada entrada tenga el mismo tamaño (anchura y altura), que la foto esté sobre el texto (aunque en el HTML no sea así) y que el botón de “Más información” esté siempre alineado abajo. Además, cuando cambiemos el ancho de pantalla se ajustará todo perfectamente sin necesidad de haber calculado complejos porcentajes. Con FlexBox conseguir esto es facilísimo!

Veamos en HTML que hemos usado. Nada especial:

Como veis he puesto una clase a cada viaje para numerarlos en el HTML. Su función es simplemente jugar con ellos y ver qué fácil es cambiarlos de orden con FlexBox. También podéis ver como la imagen está abajo de todo, pero la vamos a situar arriba. Al botón le he puesto button por simplificar.

Vamos a ver el CSS, pero ahora vamos a ir poco a poco:

Los contenedores flexibles: flex e inline-flex

Como veréis no usamos ni position ni float, ni ningún valor block o inline para display. Usamos display: flex.
A partir de ese momento el contenedor (#viajes) será un contenedor flexible y los elementos dentro del contenedor serán elementos flexibles que se adaptarán a los espacios disponibles que les deje el contenedor. Podríamos haber puesto también:

en cuyo caso se comportaría en relación a otros elementos de la página de manera semejante a display: inline. En caso contrario se comporta igual que display: block.

Orientación: flex-flow, flex-direction y flex-wrap

Seguimos. Vamos a añadir al contenedor una propiedad que nos va a permitir decirle al navegador cómo se van a alinear los elementos que están dentro del contenedor (#viajes).

Hemos añadido flex-flow: row wrap. De esta manera establecemos que los elementos dentro del contenedor (los .viajes-item) se van a alinear en fila (row). flex-flow lo podríamos haber dividido en dos:

Hemos visto antes que el eje principal es la dirección principal, pero hay que tener en cuenta que esta no es siempre horizontal. La dirección del contenedor se puede cambiar con la propiedad flex-direction, que especifica cómo se sitúan los elementos flexibles dentro del contenedor. Sus posibles valores son:

  • row: se alinean en filas.
  • row-reverse: en filas, pero con el orden inverso.
  • column: se alinean en columnas.
  • column-reverse: en columnas, pero con el orden inverso.

Es decir, que en móviles lo más lógico para nuestro ejemplo es tener una lista de una columna en lugar de varias columnas en horizontal. Esto se consigue en el ejemplo con flex-direction: column;.

En tamaños grandes se usaría flex-direction: row; para lograr que se distribuya horizontalmente. El valor por defecto de flex-direction es row, por lo que si no usáramos flex-direction los elementos aparecerían distribuidos horizontalmente.

Lo dicho: para probar flex-direction:column vamos a cambiar la orientación cuando el navegador tenga un ancho de 500px o menor. Así lo veremos mejor en los móviles:

Probad a reducir el ancho del navegador y veréis el cambio al llegar a los 500px de ancho.

La propiedad flex-wrap controla si el contenedor flexible (en este caso #viajes) tiene una sola línea o múltiples líneas, así como la dirección en la que se colocan las nuevas líneas en el eje transversal. Sus posibles valores son:

  • nowrap: El contenedor consta de una sola línea.
  • wrap: El contenedor tiene múltiples líneas.
  • wrap-reverse: El contenedor tiene múltiples líneas que se colocan en orden inverso.

En este caso, al ser múltiples líneas, hemos puesto wrap.

Bueno, ya hemos definido el comportamiento del contenedor (#viajes). Ahora vamos a ver las propiedades que podemos asignar a los elementos que están dentro del contenedor (los .viajes-item). Para empezar vamos a establecer que cada .viajes-item sea a su vez un contenedor flexible, donde sus componentes (el h2, el párrafo, la imagen y el botón) se van a alinear en columna:

Orden: La propiedad order

Podemos establecer el orden en el que aparecen los componentes de una caja flexible. Por defecto aparecerán tal y como aparecen en el código HTML (equivale a order: 0), pero eso se puede cambiar de forma sencilla. Volvamos al ejemplo. Como hemos puesto una clase a cada viaje, podemos jugar con ellos:

Con la propiedad order podemos poner el orden que queramos. Hemos cambiado el orden del primero y del tercero. Es muy sencillo, como podéis ver (he puesto cada viaje de un color diferente para poder ver bien cada uno). ¿Os imagináis qué habríais tenido que hacer para conseguir esto con las herramientas que tenemos ahora mismo?

A propósito, al poner cada .viaje-item de un color diferente podéis ver cómo todos tiene la misma altura independientemente de la de sus componentes.

Vamos a aprovechar el media-query que iniciamos antes para volver a poner todo en su orden original (order: 0 para los .viajes-item) para anchos inferiores a 500px:

Dentro de cada viaje queremos poner la imagen encima de todo. ¿Cómo lo conseguimos?

Como por defecto todos tienen order: 0 si ponemos a la imagen order: -1 se pondrá la primera.

Flexibilidad: La propiedad flex

Con la propiedad flex podemos establecer cómo crece o decrece un elemento flexible dentro del contenedor en relación a los demás. En el ejemplo que estamos siguiendo las tres columnas tienen el mismo ancho, por ello veréis en el código que todos tienen flex:1:

Si hubiéramos querido que .uno ocupara el doble que los otros dos habríamos hecho:

Esta propiedad se puede volver más compleja, porque puede tener tres parámetros: flex-grow, flex-shrink y flex-basis.

  • flex-grow: Especifica el factor de crecimiento, es decir, cuanto crecerá el elemento en relación a los demás cuando hay espacio disponible del contenedor a ocupar. Por defecto es ‘0’, que es el valor que dimos en el ejemplo anterior a los tres elementos.
  • flex-shrink: Determina el factor de reducción, es decir, cuanto decrecerá el elemento en relación a los demás cuando hay espacio negativo en el contenedor (el contenedor es más pequeño de los anchos combinados de los elementos que hay en su interior). Por defecto es ‘1’.
  • flex-basis: Toma el mismo valor que la propiedad ‘width’ y establece el tamaño inicial del elemento antes de distribuir el espacio libre de acuerdo con los ratios de flex-grow o flex-shrink. Cuando se omite, su valor es ‘main-size’ (anteriormente, ‘auto’).

Para comprender este lío lo mejor es poner un ejemplo. Supongamos que tenemos un contenedor al que llamaremos “A” que tiene 300px de ancho. Hacemos que este contenedor sea flexible:

Supongamos que este contenedor tiene en su interior dos elementos, B y C, que no tienen un ancho especificado. Vamos a establecer los siguientes valores para ambos de la propiedad flex: flex-grow, flex-shrink, flex-basis:

Como hemos establecido un flex-basis para cada elemento de 100px, nos quedarán aún 100px libres sin ocupar (300px del contenedor menos 100px del elemento B y menos 100px del elemento C). ¿Como se reparte ese espacio disponible entre los elementos B y C? En función del flex-grow: 3 partes para el elemento B (75px) y una parte para el elemento C (25px). Es decir, de inicio el elemento B ocupará 100px+75px = 175px de los 300px disponibles que mide el elemento A, y el elemento C ocupará 100px+25px=125px de los 300px disponibles del elemento A.

Ahora bien, supongamos que el elemento contenedor A mide 170px y no 300px. Eso quiere decir que habrá espacio negativo, porque los elementos B y C tienen un flex-basis de 100px cada uno, es decir, 200px, que es 30px mayor que los 170px del contenedor. En este caso el ratio que se usa es el flex-shrink, que recordemos que era 1 para el elemento B y 2 para el elemento C. Esos 30px se restarán del ancho de los elementos B y C en función de dicho ratio: al elemento B se le quitarán 10px y al elemento C se le quitarán 20px.

Parece un lío, pero seguro que dentro de poco nos habituaremos a hacer estos cálculos.

Alineación de los elementos flexibles

Podemos alinear los elementos flexibles en el Eje principal con justify-content y en el Eje transversal con align-items y align-self.

Si hay espacio extra dentro de un contenedor flexible la propiedad justify-content puede definir cómo se usa ese espacio que sobra. Las opciones son:

  • flex-start: se distribuyen todos pegados al inicio.
  • flex-end: se distribuyen todos pegados al final.
  • center: se distribuyen todos alineados al centro.
  • space-between: se distribuyen ocupando todo el espacio disponible, con separaciones iguales entre ellos, pero sin dejar espacio al inicio y al final.
  • space-around: se distribuyen ocupando todo el espacio disponible, con separaciones iguales entre ellos, dejando espacio al inicio y al final.

Vamos a ver un gráfico de W3C que nos lo explica mejor:

FlexBox justify-content

Podemos alinear los elementos flexibles en el eje transversal con align-items y con align-self. La primera establece el valor por defecto para todos, la segunda sirve para ser aplicada a elementos individuales sobre-escribiendo align-items para ese elemento.
Los valores posibles son:

  • auto: Sólo se puede aplicar en align-self y equivale al valor de align-items del elemento padre, o a stretch si el elemento no tiene padre.
  • flex-start
  • flex-end
  • center
  • baseline
  • stretch

Como una imagen vale más que mil palabras, aquí podéis ver cómo se distribuirían los elementos con cada una:

FlexBox align items

En el ejemplo inicial queríamos que el botón de “Más info” estuviera alineado en la parte inferior de cada caja flexible. Como todas tienen la misma altura, los tres botones estarán igualmente a la misma altura.

Eso lo conseguimos aplicando a la imagen margin-top: auto

Además, queremos que tanto la imagen como el botón estén centrados dentro de su contenedor. Para ello utilizamos align-self:

Ojo: Hay propiedades de CSS que no funcionarán en un elemento que se encuentre dentro de un contenedor flexible, como float, clear, column- o vertical-align.

Bueno, hemos visto con un sencillo ejemplo las posibilidades de FlexBox. Hay mucho más que investigar dentro de esta nueva especificación.

Diferentes escenarios dependiendo del navegador

Siguiendo lo que nos indica Sean Fioritto en su artículo Flexbox in the real world, para usar Flexbox hoy:

Si nuestros usuarios sólo utilizan los navegadores más modernos

Podemos usar FlexBox, sólo teniendo cuidado con:

  • No usar flex-wrap ya que no lo soporta Firefox hasta la versión 28.
  • Incluir el prefijo -webkit además del normal sin prefijo, para Safari 7.
  • No usarlo para la estructura global de la página, como vimos al principio del artículo.

Si necesitas Explorer 10 y versiones anteriores de Firefox, Safari y Opera

Tendrás que combinar versiones anteriores de Flexbox con el actual, lo que es un poco tedioso (lo puedes solucionar con Autoprefixer. Tampoco deberás usar auto-márgenes.

Si necesitas IE8 e IE9 (seguro que lamentablemente, si)

En ese caso, y hasta que alguien no desarrolle un polyfill en condiciones, no hay nada que hacer salvo crear un diseño aparte para estos navegadores. Quizás con Modernizr detectar si soporta o no Flexbox y crear un diseño aparte….

Artículos de interés

Solved by Flexbox
A Complete Guide to Flexbox, de CSS-Tricks.
Flexbox in the real world, un fantástico artículo de Sean Fioritto sobre cómo usar Flexbox en el mundo real, incluyendo qué hacer con los navegadores antiguos.
The ultimate Flexbox cheat sheet, también de Sean Fioritto, con ejemplos de cómo usar todas las propiedades de Flexbox.
CSS Flexible Box Layout Module | W3C: Toda la información sobre Flexbox al detalle.
Flexplorer: Una herramienta muy útil para probar Flexbox.

52 comentarios en “FlexBox: La caja flexible con CSS 3

  1. Wow.. gracias por el articulo. me sirvio mucho para entender como funciona e implementarlos en mis proximos sitios. Dios bendiga el flexbox

  2. interesante propiedad… no la conocía.

    claro que en el ejemplo de 3 bloques de contenido, aparentemente seria lo mismo que darle un ancho de 33,3% a cada uno, sin embargo no deja de ser fácil la opción de usar flexbox, especialmente porque independiente del contenido, todos los bloques ocupan el mismo espacio verticalmente.

    Sabes si utilizando este código hay un mejor rendimiento que utilizando porcentajes?, la pregunta puede parecer absurda para un ejemplo tan simple pero no lo es si basas todo tu sitio en porcentajes.

     

    buen articulo! 😀

    1. Gracias Marcelo,

      Efectivamente, además de la facilidad de uso, lo de que los componentes ocupen el mismo espacio vertical está muy bien. Su desventaja es la incompatibilidad con navegadores antiguos. En cuanto a rendimiento se ha escrito mucho porque la primera versión de flexbox era lenta, pero esta nueva versión ya no tiene esos problemas, pero no sé comparado con la manera de hacerlo tradicionalmente… no he visto ningún artículo que diga que es desfavorable desde el punto de vista de rendimiento y yo donde lo he usado no he notado que vaya mal.

    2. Marcelo, lo que tengo entendido es que con flexbox solo hay que colocar “display: flex”, y si tienes 3 cajas el ancho es de 33.33%, pero si cambias a 4 cajas por ejemplo y le sigues diciendo a tu archivo CSS que el porcentaje es del 33.33% esto se vería extraño. Entonces con flexbox o como su nombre lo dice “caja flexible” el tamaño de las cajas es automático para que el diseño sea fluido.

  3. en cuanto a compatibilidad, un 75% del mercado de navegadores lo acepta según Can I Use

    http://caniuse.com/#search=flexbox

    dejando de lado un importante 25% ocupado principalmente por IE 8 y 9 (siempre dando problemas…)

    Me interesa el tema del rendimiento mas que la facilidad de uso, eh notado que usando porcentaje se ocupan bastantes recursos, tendré que hacer pruebas con flexbox a ver que tal me va, quizás sea lo que estoy buscando.

    Gracias de nuevo por la info, otro día te comento que tal me va con las pruebas 😉

     

  4. En el ejemplo de las cajas A B C, lo que no entiendo del cálculo del flex-grow es en el siguietne texto:

     

    3 partes para el elemento B (75px) y una parte para el elemento C (25px).” ¿De dónde salen los 75px, y de donde sale los 25 px?. Gracias

     

     

    1. Hola!,

      Como hemos puesto que el elemento B tiene flex-grow 3 y el elemento C tiene flex-grow: 1, entonces el elemento B ocupará 3/4 partes del espacio sobrante disponible y el elemento D 1/4 parte. Como ese espacio son 100px, 3/4 partes son 75px y 1/4 parte es 25px.

  5. Buen artículo Juan, aunque me gustaría saber tu punto de vista, para implementarlo en nuevos proyectos, dado la incompatibilidad con el dichoso IE.

    Desgraciadamente aun tienes que dar soporte a las versiones 8 y 9.

     

    1. Hola Nacho,

      Si, después de leer tu pregunta he añadido al final del artículo un apartado especial sobre lo que me preguntas. Lamentablemente con IE8 e IE9 no hay nada que hacer (hasta que alguien cree un polyfill, que al parecer están en ello), salvo crear un diseño aparte en su hoja de estilos. Y con IE10 hay que incluir el prefijo -ms. Con IE11 no hay ya problemas.

  6. Excelente artículo! Lástima que todo lo nuevo en CSS tenga los problemas habituales de estandarización de navegadores, aunque me encanta porque siempre busca como facilitarnos la vida xD!!

    Un saludo

    Carlos

    1. Hola pepe,

      No he creado ninguna página completa con flexbox, pero siguiendo el artículo al que enlazo muestran los problemas que puede dar hacerlo, al menos hoy en día….

    2. Según lo que dice en el artículo que enlazas, creo que el principal motivo es por cómo se va dibujando la página, a medida que va asignando propiedades a los elementos. Lo bueno del “grid” es que dibuja a de arriba a abajo, y es muy cómodo para ir leyendo sin necesidad de esperar a que todo esté cargado. Sin embargo, si utilizas “flex” para maquetar toda la página, los elementos se irán colocando y afectando a los demás, creando una sensación de movimiento muy incómoda. En el vídeo del artículo se me muy bien la diferencia entre ambos sistemas.

  7. Muy bien explicado Juan, clap! Para ie8 y ie9… Lo mejor es recomendar al cliente que no de soporte, no vale la pena ni para él ni para ti 😉

    Salu2.

  8. Muy buen artículo, muchas gracias por compartirlo 🙂

    Sólo un pequeño apunte: el valor por defecto para “flex-grow” es 0, no 1.
    http://www.w3.org/TR/css3-flexbox/#flex-grow-property
    https://developer.mozilla.org/en-US/docs/Web/CSS/flex

    De tal manera que si omitimos la declaración de la propiedad “flex”, los valores que tomará por defecto serán “0 1 auto” para “flex-grow”, “flex-shrink” y “flex-basis” respectivamente.

    Por lo demás estupendo, muy claro y útil.

  9. Se me ha olvidado aclarar también que el valor por defecto para “flex-basis” es “auto”, no 0. No hace falta que valides este comentario, pero que sirva para que edites el post 🙂

        1. Ah, vale, creo que el valor por defecto es “auto”, pero ese “auto” recupera el valor de la propiedad “main-size” del elemento, que será su “width” o “height” según si el eje principal es una fila o una columna.

  10. Muy interesante.
    Eso quiere decir que mi header en donde tengo dos elementos: div (logo) y nav. Ya no tendré que usar float: left o right? sino ahora utilizaré space-between? claro, siempre y cuando quiera que estén a los extremos. Cierto?

  11. Tutorial magnífico! Sí señor. Estoy seguro que a más de uno nos va a ayudar. Llevo bastante tiempo buscando un tutorial de flexbox tan completo como este.
    Gracias por compartirlo con todos nosotros. Ya sigo tu blog para seguir leyéndome más cositas tuyas 😉

  12. El mejor post que me he encontrado para explicar esta propiedad pero aun tengo una duda y ojala puedan ayudarme. Si tengo dos columnas para hacer un blog y en la primera columna quiero tener dos columnas para tener post uno al lado del otro y que sigan su camino hacia abajo y luego en la otra columna tener el sidebar como deberia ser la forma correcta de hacer esto.

    Cual debe ser el contenedor padre que tiene la propieda display flex el contendor de todo o sólo el contenedor que va a llevar dentro los post?

    Estoy un poco perdido en este sentido.

  13. Hola, un tutorial muy completo, la verdad es que CSS está avanzando una barbaridad estos últimos años con la incorporación de nuevas propiedades, lo cual es una ayuda por la simplicidad que aporta al nuevos diseños web, sobretodo para los que seguimos picando código 🙂

    Un saludo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *