Menús y galerías con Flexbox

Todo lo que viene usa solo lo que ya conocemos: display: flex, flex-direction, gap, justify-content, flex y flex-wrap. No hace falta nada nuevo.

Una regla que vale para todo lo que sigue: flex-wrap: wrap se pone siempre en el contenedor que reparte en horizontal, porque el ancho es lo único que se acaba: hacia abajo siempre hay sitio, que para eso está el scroll. En un contenedor en columna, el wrap no hace nada salvo que se le dé una altura.

Menús

  1. Menú horizontal que se adapta al texto

    Es el caso normal: cada opción ocupa lo que mide su palabra. Para eso, simplemente no se pone nada en los hijos.

    <nav class="menu">
      <ul>
        <li><a href="#">Inicio</a></li>
        <li><a href="#">Work</a></li>
        <li><a href="#">About me</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </nav>

    Compáralo con lo que pasa al añadir flex: 1 a cada opción: todas pasan a medir lo mismo y «Work» queda con un hueco enorme a los lados.

    <nav class="menu">
      <ul>
        <li><a href="#">Inicio</a></li>
        <li><a href="#">Work</a></li>
        <li><a href="#">About me</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </nav>

    La idea: en un menú, dejar que cada opción mida su contenido es la opción por defecto y la correcta. Repartir en partes iguales solo tiene sentido en una barra de botones de anchura fija.

  2. Menú vertical

    El mismo HTML. Lo único que cambia es la dirección:

    <nav class="menu">
      <ul>
        <li><a href="#">Inicio</a></li>
        <li><a href="#">Work</a></li>
        <li><a href="#">About me</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
    </nav>
  3. Cabecera con logotipo a un lado y menú al otro

    Es el caso más repetido en toda la web, y se resuelve con una sola propiedad:

    <header class="cabecera">
      <span class="logo">MiEstudio</span>
      <nav>
        <ul>
          <li><a href="#">Work</a></li>
          <li><a href="#">About me</a></li>
          <li><a href="#">Contact</a></li>
        </ul>
      </nav>
    </header>

    Hay dos flexbox anidados: la cabecera reparte el logo y el menú a los extremos, y dentro el menú coloca sus opciones en fila.

  4. Grupo de iconos de redes sociales

    Una tira corta con separación fija. Lo único particular es que no crecen: cada icono mide lo suyo.

    <div class="redes">
      <a href="#" class="icono">IG</a>
      <a href="#" class="icono">FB</a>
      <a href="#" class="icono">IN</a>
      <a href="#" class="icono">YT</a>
    </div>
  5. Menú desplegable

    Está explicado aparte, en su propio apartado. Recuerda que combina display: none, :hover y position: absolute.

Galerías

  1. Una sola tira

    Para una fila de fotos, como la portada de una web de moda. Con wrap, cuando la pantalla se estrecha las fotos van cayendo a la línea siguiente en vez de aplastarse.

    <div class="galeria">
      <img src="IMG/naturaleza-lago-bn.jpg"
           alt="Lago entre montañas en blanco y negro">
      <img src="IMG/naturaleza-arroyo.jpg"
           alt="Arroyo de montaña en un bosque de Trentino">
      <img src="IMG/naturaleza-abetos.jpg"
           alt="Arroyo entre abetos bajo un cielo nublado">
      <img src="IMG/naturaleza-valle.jpg"
           alt="Valle con niebla al amanecer">
      <img src="IMG/retrato-contraluz.jpg"
           alt="Retrato de mujer a contraluz">
      <img src="IMG/retrato-rizado.jpg"
           alt="Primer plano de mujer con pelo rizado">
      <img src="IMG/bn-hombre.jpg"
           alt="Retrato en blanco y negro de hombre pensativo">
    </div>

    Estrecha la ventana del navegador y observa cómo se reorganizan solas, sin escribir una sola medida de pantalla.

  2. Galería cuadrada

    Una sola tira no da una galería cuadrada: por mucho que envuelva, sigue siendo una fila detrás de otra. Para una galería cuadrada hay que meter tiras dentro de tiras.

    Y hay dos maneras de montarlas, según cuál sea la tira de fuera. En las demostraciones que siguen, el borde azul marca cada una de las tiras de dentro.

    Galería cuadrada con fotos desiguales: hace falta object-fit

    Una galería cuadrada necesita que todas las fotos ocupen el mismo hueco. Pero las fotos no vienen iguales: unas son verticales, otras apaisadas, otras panorámicas. Si se les fija el ancho y el alto sin más, el navegador las estira o las aplasta para que quepan, y una cara sale con la nariz ancha.

    object-fit: cover es lo que lo evita. La foto se amplía hasta llenar el hueco conservando su proporción, y lo que se sale por los bordes se recorta. Nunca se deforma: se pierde un trozo.

    img {
      width: 100%;
      height: 120px;       /* el hueco lo decido yo */
      object-fit: cover;   /* la foto lo llena sin deformarse */
    }

    Tiene un hermano, object-fit: contain, que hace lo contrario: mete la foto entera dentro del hueco sin recortar nada, y deja franjas vacías a los lados. Para un grid se usa cover; contain sirve cuando no se puede perder ni un trozo de la imagen, como en un plano o un logotipo.

    De aquí en adelante: donde las fotos salgan todas del mismo tamaño es porque llevan cover y están recortadas. Donde cada una conserve su forma es porque no lo llevan.

    a) Columnas: una tira horizontal con tiras verticales dentro

    Si todas las tiras son iguales, tenemos un grid:

    <div class="galeria">
    
      <div class="column">
        <img src="IMG/naturaleza-arroyo.jpg"
             alt="Arroyo de montaña en un bosque de Trentino">
        <img src="IMG/naturaleza-abetos.jpg"
             alt="Arroyo entre abetos bajo un cielo nublado">
        <img src="IMG/naturaleza-valle.jpg"
             alt="Valle con niebla al amanecer">
      </div>
    
      <div class="column">
        <img src="IMG/retrato-contraluz.jpg"
             alt="Retrato de mujer a contraluz">
        <img src="IMG/retrato-rizado.jpg"
             alt="Primer plano de mujer con pelo rizado">
        <img src="IMG/retrato-panuelo.jpg"
             alt="Retrato de hombre con sombrero y pañuelo rojo">
      </div>
    
      <div class="column">
        <img src="IMG/bn-hombre.jpg"
             alt="Retrato en blanco y negro de hombre pensativo">
        <img src="IMG/bn-cupula.jpg"
             alt="Cúpula del Palacio de Bellas Artes en blanco y negro">
        <img src="IMG/bn-caballo.jpg"
             alt="Caballo en blanco y negro">
      </div>
    
    </div>

    Recuerda que solo la tira horizontal lleva wrap. Las verticales crecen hacia abajo, que es lo que se quiere. Al estrechar la pantalla, las columnas enteras caen una debajo de otra.

    Si las fotos tienen distinto tamaño se consigue un efecto interesante: todas mantienen constante el ancho, que es el de su columna, y el alto se alarga o se estrecha según su relación de aspecto.

    <div class="galeria">
    
      <div class="column">
        <img src="IMG/naturaleza-arroyo.jpg"
             alt="Arroyo de montaña en un bosque de Trentino">
        <img src="IMG/naturaleza-abetos.jpg"
             alt="Arroyo entre abetos bajo un cielo nublado">
        <img src="IMG/naturaleza-valle.jpg"
             alt="Valle con niebla al amanecer">
      </div>
    
      <div class="column">
        <img src="IMG/retrato-contraluz.jpg"
             alt="Retrato de mujer a contraluz">
        <img src="IMG/retrato-rizado.jpg"
             alt="Primer plano de mujer con pelo rizado">
        <img src="IMG/retrato-panuelo.jpg"
             alt="Retrato de hombre con sombrero y pañuelo rojo">
      </div>
    
      <div class="column">
        <img src="IMG/bn-hombre.jpg"
             alt="Retrato en blanco y negro de hombre pensativo">
        <img src="IMG/bn-cupula.jpg"
             alt="Cúpula del Palacio de Bellas Artes en blanco y negro">
        <img src="IMG/bn-caballo.jpg"
             alt="Caballo en blanco y negro">
      </div>
    
    </div>

    b) Filas: una tira vertical con tiras horizontales dentro

    Aquí el wrap va en las tiras de dentro, que son las horizontales. La de fuera apila hacia abajo, que es lo que se quiere.

    Con todas las tiras iguales, el mismo grid:

    <div class="galeria">
    
      <div class="row">
        <img src="IMG/naturaleza-arroyo.jpg"
             alt="Arroyo de montaña en un bosque de Trentino">
        <img src="IMG/naturaleza-abetos.jpg"
             alt="Arroyo entre abetos bajo un cielo nublado">
        <img src="IMG/naturaleza-valle.jpg"
             alt="Valle con niebla al amanecer">
      </div>
    
      <div class="row">
        <img src="IMG/retrato-contraluz.jpg"
             alt="Retrato de mujer a contraluz">
        <img src="IMG/retrato-rizado.jpg"
             alt="Primer plano de mujer con pelo rizado">
        <img src="IMG/retrato-panuelo.jpg"
             alt="Retrato de hombre con sombrero y pañuelo rojo">
      </div>
    
      <div class="row">
        <img src="IMG/bn-hombre.jpg"
             alt="Retrato en blanco y negro de hombre pensativo">
        <img src="IMG/bn-cupula.jpg"
             alt="Cúpula del Palacio de Bellas Artes en blanco y negro">
        <img src="IMG/bn-caballo.jpg"
             alt="Caballo en blanco y negro">
      </div>
    
    </div>

    Y con fotos de distinto tamaño, el efecto es el simétrico del anterior: ahora lo que se mantiene constante es el alto, que es el de su fila, y lo que varía es el ancho.

    <div class="galeria">
    
      <div class="row">
        <img src="IMG/naturaleza-arroyo.jpg"
             alt="Arroyo de montaña en un bosque de Trentino"
             style="flex: 75">
        <img src="IMG/naturaleza-abetos.jpg"
             alt="Arroyo entre abetos bajo un cielo nublado"
             style="flex: 150">
        <img src="IMG/naturaleza-valle.jpg"
             alt="Valle con niebla al amanecer"
             style="flex: 177">
      </div>
    
      <div class="row">
        <img src="IMG/retrato-contraluz.jpg"
             alt="Retrato de mujer a contraluz"
             style="flex: 69">
        <img src="IMG/retrato-rizado.jpg"
             alt="Primer plano de mujer con pelo rizado"
             style="flex: 133">
        <img src="IMG/retrato-panuelo.jpg"
             alt="Retrato de hombre con sombrero y pañuelo rojo"
             style="flex: 77">
      </div>
    
      <div class="row">
        <img src="IMG/bn-hombre.jpg"
             alt="Retrato en blanco y negro de hombre pensativo"
             style="flex: 140">
        <img src="IMG/bn-cupula.jpg"
             alt="Cúpula del Palacio de Bellas Artes en blanco y negro"
             style="flex: 56">
        <img src="IMG/bn-caballo.jpg"
             alt="Caballo en blanco y negro"
             style="flex: 133">
      </div>
    
    </div>

    En los dos casos ninguna foto se deforma, porque cada una conserva su proporción. La diferencia está en qué lado queda recto: con columnas, los laterales; con filas, la base de cada tira.

    Y hay una asimetría entre los dos: una columna puede crecer hacia abajo cuanto haga falta, pero una fila no puede crecer hacia la derecha, porque la pantalla se acaba. Por eso, en la galería de filas, los anchos de las fotos se dan en partes con flex, y no en píxeles: así cada fila se llena entera sea cual sea la pantalla.

  3. Columnas con título

    La misma estructura de tiras anidadas sirve para agrupar las fotos por temas, poniéndole a cada columna su propio título.

    <div class="galeria">
    
      <div class="column">
        <h3>Naturaleza</h3>
        <img src="IMG/naturaleza-arroyo.jpg"
             alt="Arroyo de montaña en un bosque de Trentino">
        <img src="IMG/naturaleza-abetos.jpg"
             alt="Arroyo entre abetos bajo un cielo nublado">
        <img src="IMG/naturaleza-valle.jpg"
             alt="Valle con niebla al amanecer">
      </div>
    
      <div class="column">
        <h3>Retratos</h3>
        <img src="IMG/retrato-contraluz.jpg"
             alt="Retrato de mujer a contraluz">
        <img src="IMG/retrato-rizado.jpg"
             alt="Primer plano de mujer con pelo rizado">
      </div>
    
      <div class="column">
        <h3>Ciudad</h3>
        <img src="IMG/ciudad-amanecer.jpg"
             alt="Rascacielos de Ciudad de México al amanecer">
        <img src="IMG/ciudad-aerea.jpg"
             alt="Vista aérea de una ciudad extensa">
        <img src="https://images.pexels.com/photos/12333846/pexels-photo-12333846.jpeg?auto=compress&cs=tinysrgb&w=600"
             alt="Rascacielos iluminados de noche">
      </div>
    
    </div>

    Cada columna lleva las fotos que haga falta, sin que las demás se enteren.

  4. Hasta dónde llega Flexbox

    Con las tiras anidadas se construyen galerías perfectamente válidas. Pero hay tres cosas que no se pueden hacer:

    1. Fijar cuántas columnas hay. Caben las que quepan. No se puede decir «siempre tres».
    2. Cuadrar la última fila. Si hay siete fotos en filas de tres, las dos últimas se quedan sueltas a la izquierda.
    3. Destacar una foto para que ocupe dos columnas y dos filas. Sencillamente no se puede.

    Para eso está Grid, que veremos en la clase siguiente.

La tarjeta de contenido

Una tarjeta es una caja en columna: imagen, título, texto y enlace. Y varias tarjetas juntas son una tira horizontal.

El detalle que resuelve Flexbox, y que a mano no sale, es este: los textos tienen distinta longitud, pero los botones deben quedar alineados.

<div class="cards">

  <article class="card">
    <img src="IMG/naturaleza-valle.jpg"
         alt="Valle con niebla al amanecer">
    <h3>Naturaleza</h3>
    <p>Un texto corto.</p>
    <a href="#" class="boton">Ver más</a>
  </article>

  <article class="card">
    <img src="IMG/retrato-rizado.jpg"
         alt="Primer plano de mujer con pelo rizado">
    <h3>Retratos</h3>
    <p>Un texto bastante más largo que el de la tarjeta de al lado, para que
         se vea que los botones siguen alineados aunque el contenido no mida
         lo mismo.</p>
    <a href="#" class="boton">Ver más</a>
  </article>

  <article class="card">
    <img src="https://images.pexels.com/photos/12333846/pexels-photo-12333846.jpeg?auto=compress&cs=tinysrgb&w=600"
         alt="Rascacielos iluminados de noche">
    <h3>Ciudad</h3>
    <p>Otro texto, de longitud media, algo más largo que el primero.</p>
    <a href="#" class="boton">Ver más</a>
  </article>

</div>

margin-top: auto significa «todo el espacio que sobre, ponlo encima de mí». El resultado es que el enlace se va al fondo de su tarjeta, y los tres quedan a la misma altura.

Reconocimientos

Las fotos son de Pexels, con licencia libre: se pueden usar y modificar sin pagar y sin citar la fuente. Citarla no es obligatorio, pero es lo correcto, y aquí sirve además de ejemplo de cómo se hace. Lo que la licencia no permite es revenderlas sin editar, redistribuirlas en otro banco de imágenes ni usar a las personas que aparecen de forma que queden en mal lugar.