Sticky Footer: o cómo poner el pie de página en su sitio

Hombre empujando coche
Imagen de Craig Sunter.

En diseño, un sticky footer es un pie de página que se queda "pegado", independientemente de la cantidad de contenido, a la parte inferior de la ventana del navegador. Con esto evitamos que nuestro diseño se rompa al aparecer el pie de página en mitad del documento, cuando el propio documento tiene poco contenido. No debemos confundirlo con un pie de página que se queda fijo y es visible a lo largo de todo el documento, eso es arena de otro costal.

Hay varias formas de hacerlo, y cada una de ellas con sus pros y sus contras. De momento voy a pasar por alto el uso de Grid Layout, por la escasa compatibilidad que aún existe en los diferentes navegadores, aunque es probablemente una de las mejores y más limpias formas de conseguirlo. Así que no perdamos el tiempo y ¡al lío!.

Sticky footer con márgenes negativos

Esta es la forma más elemental de conseguir nuestro objetivo y quizás la más compatible, pero con una gran pega, necesitamos dar un alto concreto a nuestro pie de página, y como supongo que ya sabrás, los altos fijos son algo que se debe evitar en diseño web.

Realmente hay tres variantes usando márgenes negativos, en función de como usemos el elemento que evita el solapamiento, cuando el contenido es lo suficientemente amplio para enviar por sí sólo al pie de página a su sitio. A fondo sólo voy a explicar la primera de ellas, en las otras dos sólo será necesario ver las modificaciones necesarias para evitar el solapamiento.

Contenedor + elemento auxiliar

La forma de conseguir un sticky footer con márgenes negativos es relativamente sencilla. Hay que tener en cuenta que lo que desconocemos en todo momento es el alto de los elementos de la web y el alto del viewport (zona visible del navegador donde se muestra el documento), por tanto no podemos calcular cuanto tenemos que desplazar el pie de página para ponerlo en su sitio.

Para ello necesitamos englobar todo el contenido, excepto el pie de página, en un contenedor auxiliar al que daremos el valor min-height: 100%. Ahora tenemos el pie de página justo por debajo del viewport. Esto podemos corregirlo usando márgenes negativos (en el contenedor o en el pie de página), pero para ello necesitamos saber el valor exacto del alto del pie de página, lo que nos obliga a darle un valor concreto, que es la gran pega de este método.

Si visualizamos un documento con poco contenido y usando este método justo hasta este punto concreto, todo parece funcionar correctamente. Sin embargo, si hay contenido suficiente para empujar el pie de página hasta su lugar correcto, veremos que se produce un solapamiento. Para ello deberemos de usar un elemento auxiliar que situaremos dentro del contenedor y al final de todo el contenido, con un alto del mismo valor que el del pie de página.


<div id="contenedor">
    <header>
        contenido
    </header>
    <main>
        contenido
    </main>
    <div id="push"></div>
</div>
<footer>
    contenido
</footer>

html, body {
    height: 100%;
}

#contenedor {
    min-height: 100%;
    margin-bottom: -8em;
}

footer, #push {
    height: 8em;
}

Doble contenedor

Para este caso cambiaremos el elemento auxiliar, por otro contenedor, teniendo así una estructura de dos elementos contenedores anidados. El primer contenedor tendrá los mismo parámetros que en el caso anterior y el segundo, que es el que envuelve el contenido directamente, un padding-bottom de valor igual al alto del pie de página.


<div id="contenedor">
    <div id="contenedor-auxiliar">
        <header>
            contenido
        </header>
        <main>
            contenido
        </main>
    </div>
</div>
<footer>
    contenido
</footer>

html, body {
    height: 100%;
}

#contenedor {
    min-height: 100%;
    margin-bottom: -8em;
}

#contenedor-auxiliar {
    padding-bottom: 8em;
}

footer {
    height: 8em;
}

Contenedor + box-sizing

Este último caso es realmente una variación del segundo, ya que cambiando el modelo de cajas mediante box-sizing: border-box no necesitamos el segundo contenedor, podemos darle un padding-bottom de valor igual a el alto del pie de página directamente al contenedor principal, con lo que nos ahorramos algo de marcado en html.


<div id="contenedor">
    <header>
        contenido
    </header>
    <main>
        contenido
    </main>
</div>
<footer>
    contenido
</footer>

* {
    box-sizing: border-box;
}

html, body {
    height: 100%;
}

#contenedor {
    min-height: 100%;
    margin-bottom: -8em;
    padding-bottom: 8em;
}

footer {
    height: 8em;
}

Sticky footer con display:table

Un elemento cualquiera con una height: 100% tiende a ocupar el 100% del alto de su contenedor padre, independientemente de si hay o no otros elementos. Esto, que en ocasiones puede resultar útil, es lo que en los ejemplos anteriores nos obligaba a corregir la posición del pie de página con un margen negativo.

También, este comportamiento es lo que nos hace fijar tanto al elemento html como al elemento body a un height: 100%. Es lógico, puesto que necesitamos que nuestro contenedor principal ocupe el 100% del viewport, y para ello sus ancestros deben tener también esa medida. La explicación es muy sencilla, el elemento html es el elemento raíz, por tanto no tiene elemento padre, tomando como valor de referencia el wieport.

Este comportamiento afecta a todos los elementos de tipo bloque (recuerda que básicamente hay dos tipos de elementos, si consideramos únicamente el display externo, de bloque y en línea), que bien pueden ser los que definimos mediante diplay: block, display: table e incluso display: flex.

Para este caso concreto lo que nos importa es el comportamiento de las tablas, para ser exactos, de como se comportan los elementos que hay en el interior de una tabla. Un elemento definido con display: table se comporta, en lo referente a un alto del 100%, tal cual lo haría uno definido con display:block, pero no los elementos internos, que podemos definir con una serie de propiedades display para que se comporten como si de una tabla "real" se tratase.

Una tabla tiene un comportamiento específico, y si no se le asigna ningún valor al alto y al ancho, esta tiende a ocupar el mismo espacio que ocupan los elementos que contiene. No es algo que nos convenga, por tanto debemos dar a nuestro elemento contenedor un height: 100% y un widht: 100%. A nuestro contenido le asignaremos un display: table-row para que se comporte como si de una fila se tratase, y aquí es de donde proviene la magia. Si le asignamos un height: 100% tiende a ocupar el 100% del espacio disponible, no el 100% de su elemento contenedor. Esto es lo que realmente nos interesa, puesto que de esta forma no necesitamos corregir la posición del pie de página con márgenes negativos (debemos englobar tanto contenido como pie de de página dentro de un mismo elemento contenedor). Tampoco es necesario fijar una altura al pie de página, podemos dejarlo con medidas fluidas, que es como se debe hacer.

Eso sí, no todo son ventajas, hay una pequeña pega. Hay algunos elementos dentro de una tabla que no soportan determinadas propiedades, en concreto, a una fila no le podemos dar valor a padding, si necesitamos hacer esto debemos usar un contenedor auxiliar. Si estás interesado en saber como se comportan las tablas más a fondo, puedes leer el artículo Table Formatting publicado en Site Point.


<body>
    <header>
        contenido
    </header>
    <main>
        <div class="contenedor">
            contenido
        </div>
    </main>
    <footer>
        contenido
    </footer>
</body>

html, body {
  height: 100%;
  margin: 0;
}

body {
  display: table;
  width: 100%;
}

main {
  height: 100%;
  display: table-row;
}

.contenedor {
  padding: 1em;
}

Sticky footer con flexbox

La forma de hacerlo con display: flex es muy parecida ha cuando usamos display: table. Necesitamos un contenedor principal (también podemos usar el mismo body), para englobar todos los elementos de la página (cabecera, contenido, pie de página ...), dar a los elementos html y body un valor de alto del 100% (ya sabes para que) y distribuir el espacio disponible para que este sea ocupado por el contenido.

Cuando hacemos que un elemento sea flexible, todos sus hijos pasan a comportarse de una manera concreta, en función a las propiedades que hayamos definido en este. A su vez, sus hijos disponen también de unas pocas propiedades que de otra forma no estarían disponibles para su uso. De entre estas últimas propiedades hay una que nos interesa, flex-grow que nos permite distribuir el espacio libre disponibles entre los elementos contenidos dentro de un elemento flexible de la forma en que queramos.

La gran ventaja de usar flexbox frente a display:table es que nos podemos ahorrar el contenedor extra que necesitamos en caso de querer usar alguna propiedad que display:table-row no soporta, como por ejemplo padding. Por el resto es todo igual, nada de márgenes negativos y podemos tener un pie de página con medidas fluidas.


<body>
    <header>
        encabezado
    </header>
    <main>
        contenido
    </main>
    <footer>
        pie de página
    </footer>
</body>

html, body {
  height: 100%;
}
body {
  display: flex;
  flex-direction: column;
}
main {
  flex: 1;
}