CSS3: Consultas de medios (Media Queries)
Introducción a las consultas de medios de CSS3 (Media query)
Una consulta de medios o media query en inglés es una expresión para verificar algunas características de los medios de salida. Un medio viene a ser el dispositivo en el que se presenta la información, como dispositivos visuales (screen) o salida impresa (print). El módulo estándar de CSS3 en fase REC W3C Media Queries nos explica todo lo relacionado a esta materia.
Desde HTML 4.01 y CSS 2.1 podíamos describir el tipo de medio al que se destinaba una página web, como recordamos en estos ejemplos:
- Con
<link rel="stylesheet" type="text/css" media="screen" href="archivo.css">
vinculábamos el estilo de un archivo externo para la salida por medio visual (pantalla): - Con
@media screen { ...reglas... }
podíamos declarar expresamente el tipo de medio visual en unas reglas de estilo directamente dentro del CSS, tanto en un archivo externo como en el elemento<style>
. - Con
@import url(archivo.css) screen;
podíamos en CSS importar el estilo de un archivo externo aplicándose al medio visual.
En este sitio y aplicado a XHTML-1.0 más CSS2.1 puede consultar el atributo HTML media o las reglas @media y @import. Esto sigue siendo igual en HTML5, incorporándose ahora la posibilidad de realizar al mismo tiempo consultas sobre características de ese medio. Por ejemplo preguntar qué ancho tiene la pantalla o cuál es la orientación del dispositivo móvil (vertical u horizontal) entre otras. Estas consultas nos permitirán configurar el estilo apropiado para cada dispositivo. Y esto es una de las bases para el diseño adaptativo (responsive design).
La regla @media antes tenía la sintaxis @media LISTA_DE_MEDIOS {...}
donde la lista de medios era una lista de tipos como screen, projection
por ejemplo. Al final tendríamos algo como:
@media screen, projection { div.titulos { font-family: Arial; } }
Este ejemplo aplicaría un estilo de fuente al elemento de clase titulos
en los monitores y proyectores. En cualquier otro medio no se aplicaría este estilo. Ahora esa lista de medios (media list) se podría convertir en una lista de consultas de medios (media query list). Un posible ejemplo podría ser:
@media screen and (min-width: 800px), projection { div.titulos { font-family: Arial; } }
El estilo sería aplicado a los monitores con ancho igual o superior a 800 píxeles y a todos los proyectores. Al resto de dispositivos y a los monitores con ancho inferior no se les aplicaría ese estilo.
Sintaxis de las consultas de medios
Una consulta de medios, que siempre devolverá al navegador un valor verdadero o falso, tiene la siguiente sintaxis:
- Al inicio de la consulta y optativamente podemos incluir alguna de las partículas siguientes:
only
puede ser usada para ocultar estilo a navegadores que no soporten las consultas de medios. Así cuando estos navegadores encuentren al inicio esta palabra clave que no reconocen, no seguirán leyendo el resto y el estilo no se aplicará. Para los navegadores que lo soporten, leerán el resto como si ignoraran esta palabra.not
niega el resto de la consulta.
- El tipo de medio es alguno como
screen, print, projection
o bien la palabraall
para referirse a todos. - Lo que viene a continuación es una o más expresiones. Veáse que si no ponemos la primera parte optativa (only o not) y esta última que también es optativa, una consulta de medios será sólo el tipo de medio, que viene a ser exactamente como ya era con las especificaciones CSS y HTML anteriores. Una expresión contiene:
- Siempre empieza con
and
y a continuación entre paréntesis hay una característica del medio y, opcionalmente, un valor para la misma. Las características del medio pueden ser comowidth, height, device-width, device-heigth, orientation, color
entre otras. - Algunas características no necesitan valor, como
and (color)
que devolverá un valor verdadero si es un medio en color o falso en otro caso. Para otras si hay que especificar el valor como conand (width: 800px)
. - Hay características que aceptan los prefijos
min
ymax
. Por ejemplo:and (width: 800px)
significa que la característicawidth
ha de ser igual a 800 píxeles.and (min-width: 800px)
significa que la característicawidth
ha de ser mayor o igual a 800 píxeles.and (max-width: 800px)
significa que la característicawidth
ha de ser menor o igual a 800 píxeles.
"<"
y">"
dentro del CSS para que no colisione con los caracteres reservados de HTML.
- Siempre empieza con
El tipo de medio all
aplica a todos los medios. En ese caso podemos omitir las partículas all
y and
. Por ejemplo, es equivalente:
@media all and (max-width: 800px) { div.miclase { color: red; } }
a esto otro:
@media (max-width: 800px) { div.miclase { color: red; } }
Características de medios (Media features)
Un resumen de las características de los medios se expone a continuación. En la especificación oficial puede encontrar más detalles, pero por ahora sólo me voy a limitar a conocer lo que hay para tener una idea general.
Característica | Valor | Mín, max | Descripción |
---|---|---|---|
width | Una longitud | ✔ | Ancho del viewport |
height | Una longitud | ✔ | Alto del viewport |
device-width | Una longitud | ✔ | Ancho del monitor |
device-height | Una longitud | ✔ | Alto del monitor |
orientation | portrait o landscape | La orientación es portrait cuando el valor de height es mayor que el de width. En otro caso será landscape. | |
aspect-ratio | Una relación | ✔ | Es la relación width/height |
device-aspect-ratio | Una relación | ✔ | Es la relación device-width/device-height |
color | Un entero | ✔ | El número de bits por componente de color. |
resolution | Una resolución | ✔ | La densidad de píxeles del dispositivo. |
Hay otras características que no se incluyen en la tabla anterior como color-index, monochrome, scan y grid. Los valores de longitud son los ya definidos en CSS-2.1, por ejemplo 800px
o 20em
. Los nuevos valores introducidos son la relación (ratio) que es algo como 16/9
y la resolución (resolution) que es un número entero positivo seguido de las unidades dpi
o dpcm
. Son puntos por pulgada y por centímetro respectivamente para medir la densidad de píxeles del dispositivo.
Todas estas características nos servirán para actuar sobre nuestras páginas adaptándolas a los distintos dispositivos. Pero hay muchas cosas que ver y probar. En este tema sólo miraré lo relacionado con las medidas del viewport y del dispositivo que afectan a la primeras característica width
de la tabla anterior.
Estructura de página web con bloques en línea
Las consultas de medios se pueden usar para adaptar la estructura de la página al ancho de la pantalla del dispositivo. Hay muchas formas de estructurar una página. En el tema cómo se hace la estructura de una página web puede ver por ejemplo la estructura elástica basada en el uso de la propiedad float
para flotar los contenedores laterales a izquierda y derecha, a la vez que todas las medidas se hacían relativas al tamaño de la fuente. Esta estructura es la que he venido usando en este sitio, aunque limitada sólo a la página de inicio. Para llevar a cabo esas flotaciones de los laterales, por ejemplo utilizando sólo el lateral derecho, con respecto al contenido que sería el bloque central hay que disponer el HTML así:
<div id="lateral"> ... </div> <div id="contenido"> ... </div>
Aplicaríamos un CSS como el siguiente:
div#lateral { width: 6.25em; height: 100%; padding: 0.3125em; float: right; } div#contenido { margin-right: 7.875em; padding: 0.3125em; }
El ancho del lateral queda fijado a 6.25em, unos 100px con una fuente de 16px. Flotamos a derecha y damos un margen derecho al contenedor algo superior al ancho del lateral más sus bordes de 0.3125em. Esto funcionaría correctamente en un dispositivo de pantalla pequeña, pero precisamente ese es el problema. Con un ancho de pantalla de 320px que tienen muchos móviles, quitando los 100px del lateral y los rellenos del contenido, nos quedamos con sólo 210px para este cuerpo central. Dependiendo de lo que vayamos a poner en el contenido, quizás es preferible pensar que para anchos de pantalla por debajo de 640px ese contenido ocupe todo el ancho disponible y no mostrar el lateral.
Es entonces cuando entran en juego las consultas de medios. Podemos detectar el ancho del dispositivo y actuar en consecuencia. Usualmente se reubica el lateral para que siga al contenido en flujo estático. Aparecerá entonces el contenido ocupando todo el ancho de pantalla y a continuación el lateral en la parte baja de la página. Sería tan fácil como hacer float: none
para el lateral y quitarle el margin-right
al contenido, pero el problema es que ese lateral está antes del contenido. Y no es fácil desplazarlo para que fluya estáticamente después del contenido.
Esto nos lleva a considerar una estructura de bloques en línea, haciendo uso de la propiedad y valor display: inline-block
. Los elementos de bloque causan un salto de línea siempre. Pero los inline-block permanecen en la misma línea si caben. Si no caben se saltan de línea. Entonces la estructura de nuestro contenido y lateral a la derecha quedaría así:
<div id="contenido"> ... </div> <div id="lateral"> ... </div>
Observe que ahora si tenemos el lateral después del contenido. Completamos con el siguiente CSS:
div#contenido { display: inline-block; width: auto; margin-right: 192px; vertical-align: top; } div#lateral { display: inline-block; width: 192px; margin-left: -192px; }
Ese código es parte del ejemplo expuesto más abajo, con un lateral un poco mayor, de 192px. Ambos contenedores están dispuestos como bloques en línea. El contenido tiene un ancho auto, que no es necesario especificarlo pues es su valor por defecto. Llenará todo el ancho disponible. Para albergar el lateral en la misma línea del contenido, éste ha de tener un margen derecho de 192px, igual que el ancho del lateral. Mientras que ese lateral debe tener un margen izquierdo negativo del mismo valor. Con este margen negativo forzamos el lateral para que se disponga en la misma línea que el contenido, al que damos el mismo margen pero positivo para que no se alargue más alla de donde empieza el lateral.
Un ejemplo de la estructura anterior es la siguiente captura de pantalla en un monitor de un ordenador de sobremesa:
Modificando la estructura de la página web con una consulta de medios
La captura de pantalla anterior es de un ejemplo con la ventana del monitor redimensionada a 641px. Pues ese ejemplo ya incluye una consulta de medios tal que para anchos de 640px y menores obtendremos otra estructura en la parte superior de la página:
La botonera o menú de la cabecera se oculta apareciendo un icono de tres franjas horizontales a la derecha del título de la página que nos permitirá acceder a esa botonera. Lo importante es que ahora estamos en dispositivos móviles con dimensiones reducidas y hemos de ocultar o trasladar de la parte superior de la página todos aquellos contenidos que no sean principales. El lateral se ha trasladado a la parte inferior de la página:
La estructura básica HTML de los cuerpos del contenido y lateral sería la siguiente:
<div id="contenido"> ... </div> <div id="lateral"> <div class="submenu"> ... </div> <div class="submenu"> ... </div> ... </div>
Siguiendo el criterio de móvil primero (mobile first), dispondremos el CSS necesario para atender en primer lugar los dispositivos móviles y luego aplicaremos consultas de medios para ordenadores de sobremesa:
div#contenido { display: block; vertical-align: top; text-align: left; } div#lateral { display: block; margin-bottom: 0.5em; text-align: left; } div.submenu { display: inline-block; padding: 0; vertical-align: top; } @media screen and (min-width: 641px){ div#contenido { display: inline-block; margin-right: 192px; } div#lateral { display: inline-block; width: 192px; margin-left: -192px; } div.submenu { padding-left: 10px; text-align: center; margin-bottom: 0.5em; } }
El contenido y lateral se declaran inicialmente como elementos de bloque con display: block
. Esto es en principio para cualquier tipo de dispositivo, haciendo que el lateral salte de línea y se ubique por debajo del contenido, antes del pie de la página que es de color y borde igual que la cabecera. Los cinco contenedores del lateral, de clase submenu
, se ubican como bloques en línea (display: inline-block)
. Si tienen anchos declarados, como sucede con los cuatro primeros, aparecen uno tras otro en una línea mientras quepan. El de color azul fuerte ya no cabe en esa línea y salta a la siguiente. El quinto, con fondo gris, no tiene ancho declarado que equivale a decir que tiene width: auto
, por lo que ocupará una línea completa.
Finalmente ponemos en el CSS una consulta de medios para dispositivos iguales o mayores de 641px. Como se comenta en el apartado anterior, ahora contenido y lateral pasan a inline-block
, dándole un ancho fijo al lateral de 192px. Forzamos a que estén juntos poniendo un margen derecho del contenido igual que el margen izquierdo del lateral pero negativo y con el valor del ancho del lateral.
El motivo de escribir el CSS pensando primero en móviles es que ejecutar una consulta de medios como la anterior supone sobrescritura de propiedades. Y esto siempre da lugar a un coste de tiempo añadido. Cuando un navegador móvil de ancho inferior a esos 641px llegue a la consulta simplemente ignorará sus declaraciones pues no le afectará. En cambio un ordenador de sobremesa o cualquier dispositivo con una pantalla de mayor tamaño deberá aplicar el estilo sobrescribiendo las propiedades. Pero al menos ese dispositivo tendrá más recursos para afrontar esta tarea que un móvil de reducido tamaño.
Mostrando y ocultando la botonera o menú principal
Para cualquier dispositivo la botonera o menú principal (ul#botonera-cabeza
) en la cabecera de la página no aparecerá inicialmente, pues le ponemos display: none
. En cambio presentaremos el icono o botón "≡" con tres franjas horizontales (div#boton-menu
) que nos permitirá acceder a ese menú. Cuando la pantalla sea igual o mayor que 641px sobrescribimos para que se muestre la botonera con block!important
mientras que el botón de menú se oculta con none!important
.
ul#botonera-cabeza { display: none; } div#boton-menu { display: block; cursor: pointer; float: right; } @media screen and (min-width: 641px){ ul#botonera-cabeza { display: block!important; } div#boton-menu { display: none!important; } }
El uso de !important
hará que se ejecute en cualquier caso, pues puede haber un problema al usar el siguiente JavaScript para mostrar y ocultar la botonera:
<script> window.onload = function(){ try { document.getElementById("total").addEventListener("click", abrirCerrarBotonera, false); } catch(e){} }; function abrirCerrarBotonera(event) { var este = event.target; var botonera = document.getElementById("botonera-cabeza"); var disp = document.defaultView.getComputedStyle(botonera, null).getPropertyValue("display"); if ((este.id=="boton-menu")|| ((este.id!="boton-menu")&&(disp=="block"))){ if (disp=="none"){ botonera.style.display = "block"; } else { botonera.style.display = "none"; } } } </script>
Este script agrega estilo en línea, es decir, añade propiedades en el atributo style
del elemento. El estilo en línea tiene mayor especificidad que el que aparece en una regla de un elemento <style>
o archivo CSS externo, a no ser que agreguemos !important
al valor de la propiedad. Por lo tanto si ponemos con el script display: none
en estilo en línea con un tamaño de pantalla menor, cuando redimensionemos la ventana a mayor tamaño sucederá que el estilo de la consulta con el valor display: block
no sería aplicado a menos que lo hagamos !important
. De forma similar hacemos también para el botón de menú que muestra y oculta la botonera o menú principal.
En cuanto al script, un manejador de evento es aplicado a un elemento <div id="total">
que contiene toda la página. Con esto cuando pulsemos el botón se abrirá la botonera o menú. Al pulsarlo otra vez o al pulsar en cualquier parte de la página se cerrará. Consultamos el estilo actual de la botonera con getComputedStyle()
para saber si está abierta o cerrada.