Wextensible

Ancho máximo para una página web

Una cuestión de accesibilidad

Los problemas de justificar texto

El tema de la accesibilidad web es algo complejo, pero poco a poco intento ir adaptando algunas cosas. Lo que ahora me ocupa va sobre la presentación visual de los contenidos de texto. En la especificación Understanding WCAG 2.0: Visual Presentation nos comentan que no debemos usar texto justificado y además que el ancho de línea no debe ser superior a 80 caracteres.

El texto justificado ajusta el espaciado entre palabras con objeto de alinear el texto con el margen derecho. Expone ese documento que hay quiénes tienen problemas leyendo texto justificado. Pero además es evidente que en dispositivos muy estrechos o bloques con poco ancho pueden darse separaciones entre palabras que dificultan la lectura.

El ejemplo es extremo pues el texto se limita a un bloque muy estrecho, pero hay que pensar que en un móvil en orientación portrait no vamos a disponer de mucho espacio para presentar el texto. Por lo tanto evitar el justificado es una necesidad que también nos impone las pequeñas medidas del ancho de los dispositivos móviles.

Página sin max-width

Según comenta el documento anterior, también para algunas personas resulta díficil leer largas líneas de texto, recomendando líneas con menos de 80 caracteres. Yo diría que aunque no tengamos problemas visuales, para cualquiera resulta más cómodo leer líneas no muy largas. El monitor de 19 " de mi ordenador de sobremesa está configurado a 1280 píxeles de ancho. Ese documento anterior aparece como la captura de pantalla adjunta (ver imagen original) cuando maximizo totalmente la ventana del navegador. La primera cuestión que observo en esa página es un tamaño de letra muy pequeño 14 px, mientras que yo he optado para mi sitio por un tamaño de 16 px con una fuente también sans-serif.

Pero lo que me parece más preocupante es ver que las líneas pueden tener más de 80 caracteres, por lo que ese sitio estaría incumpliendo esa norma de accesibilidad que difunde en esa misma página. Aunque por supuesto, los navegadores disponen de mecanismos para redimensionar la ventana y por tanto el ancho de una línea de texto. Y también puede el usuario actuar sobre el tamaño de la fuente directamente o través del zoom. Pero creo que es mejor ofrecer de entrada las condiciones óptimas para la lectura, especialmente limitar el ancho de línea a un tamaño en torno a 80 caracteres según el tamaño de letra que usemos en los párrafos del contenido. Y luego podríamos añadir alguna utilidad para ampliar ese ancho de página si aún el usuario lo requiere.

Ancho de un texto con 80 caracteres

En primer lugar tengo que ver que ancho necesito para meter 80 caracteres. Dependerá de factores como la fuente usada y su tamaño. A excepción de las fuentes monoespaciadas, todas las fuentes no tienen el mismo ancho de letra. Además habría que tener en cuenta que habrán líneas que puedan contener más o menos palabras y podemos preguntarnos cuál es el término medio de palabras que hacen una línea con 80 caracteres. Esto no parece fácil de resolver a no ser que nos apoyemos en alguna utilidad, como la herramienta denominada Font Metric que he creado para este objetivo.

Gráfica anchos de línea para 80 caracteres

La gráfica anterior se obtiene con esa herramienta. En este caso ejecuté 1000 pruebas eligiendo aleatoriamente frases al azar de un texto de una página de este sitio. Las líneas se rellenan hasta ocupar no más de 80 caracteres, desechando la última palabra que no quepa en la línea. La fuente utilizada es Verdana y el tamaño es de 16 px, una fuente del tipo sans-serif que tiene un buen ancho de letra para hacerla más legible en pantalla. Después de realizar las 1000 pruebas se observa que el valor más frecuente o moda es de 700 px.

Nota Octubre 2015: Hay que aclarar que las fuentes no se muestran igual en todos los dispositivos. Las pruebas comentadas aquí fueron hechas en un sistema operativo Windows XP con Chrome 35. Una misma fuente es posible que tenga distintos anchos medios de carácter en sistemas operativos diferentes. Hice la prueba en Windows XP cuando redacté este tema, en junio 2014, resultando que para 80 caracteres necesitaba una línea de 700 píxeles, lo que supone un ancho de 8.75 píxeles por carácter. Sin embargo actualmente tengo un Windows 8.1 (Chrome 46) y 80 caracteres necesitan un ancho de línea menor, en promedio 680px, lo que supone 8.43 píxeles por carácter. Si mantengo fijo los 700px tendré ahora un valor más frecuente de 83 caracteres por línea.

Ejecutamos ahora el mismo texto pero tomando como parámetro los 700 px de ancho y buscaremos cuantos caracteres podemos meter:

Gráfica caracteres en líneas de 700px

Como era de esperar el valor más frecuente es de 80 caracteres. Entonces probaré ese ancho máximo de 700 px para limitar los contenidos de texto de las páginas de este sitio.

Ancho máximo y centrado con CSS de una página web

Para fijar un ancho máximo usamos la propiedad de estilo max-width. Podemos centrar la página con css usando margin: 0 auto para que se centre horizontalmente. Este estilo se lo podemos aplicar al elemento <html> o <body>. Pero en este sitio tengo algunos problemas que impiden aplicar ese estilo a esos elementos.

Intentaré explicar esto aunque es algo complicado. Veámos como es la estructura de una página de este sitio (puedes ver más sobre esta estructura minimalista):

<html>
...
<body>
    <header><div id="cabeza-menu">
        ...
    </div></header>
    <div id="contenido">
        ...
    </div>
    <footer><div id="pie">
        ...
    </div></footer>
</body>
</html>

Y este es el CSS antes de aplicar ningún cambio:

#cabeza-menu {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    z-index: 9999999;
    min-width: 285px;
    }
#contenido {
    margin-top: 7.5em;
    min-width: 285px;
    }
#pie {
    font-family: Verdana, Tahoma, sans-serif;
    } 

Despliegue de menú en cabecera

La barra de cabecera <div id="cabeza-menu"> está posicionada de forma absoluta. Tiene left y top al punto (0,0), la esquina superior izquierda de la ventana del navegador. El posicionamiento absoluto es por la necesidad de presentar el menú desplegable en una capa superior y que no empuje hacia abajo el resto de contenidos de la página. En la imagen adjunta se observa el menú antes y después de desplegar y como lo hace encima del resto del contenido de la página.

Recordemos que los elementos se posicionan absolutamente con respecto al primer padre con posición distinta de estática. Y si no hay ninguno lo harán con respecto al bloque de contención inicial o ventana del navegador. Si no hemos modificado el estilo inicial de <html> o <body>, el bloque de contención inicial se puede asimilar a estos. Pero si damos un max-width a alguno de ellos y luego los centramos con margin: 0 auto entonces no cabe ese asimilamiento y la barra de cabecera se sigue posicionando con respecto a la ventana y no a <html> o <body>.

Además de max-width y margin: 0 auto podemos darle posicionamiento relativo a <body> para que la cabecera se posicione con respecto a ese elemento y no a la ventana del navegador. El CSS modificado sería así

body {
    max-width: 716px;
    margin: 8px auto 8px auto;
    position: relative;
}
body > header {
    height: 1px;
}

Los elementos de estructura no se modifican. Pero dado que el contenido tiene un margen superior de 7.5em para separarlo de la cabecera, con objeto de que siga funcionando necesitamos que el elemento previo en el DOM, el <header> tenga alguna altura. Si no es así, el margen superior del contenido colapsará con el del <body> en un único margen y luego también se aplicará a la cabecera. Esto del colapsamiento de márgenes es bastante complicado y se da en ciertas circunstancias con posicionamientos relativos, como en este ejemplo de Stackoverflow.com.

Sin ganas de liarme más veo que así puedo centrar la página y limitar el ancho máximo a 716 px, que una vez quitado los 16 px de márgenes de <body> me quedan los 700 px libres para el contenido. Pero no podía ser tan fácil. Tengo algunas aplicaciones como slider tipo range o el selector de áreas entre otras que utilizan event.pageX o event.pageY para mover algún elemento dentro de otro (lo hago a través de la función recogeEvento() del componente eventos). Estas son las coordenadas respecto de la página y que luego pueden servirnos para ubicar un elemento. Pero si posiciono el <body> todo eso se me viene abajo. Una solución podría ser restar de pageX el margen izquierdo de <body>, pero tendría que revisar todos los scripts JS de este sitio que usen posicionamiento y adaptarlos a este cambio del <body>.

Para no estar haciendo cambios en muchas partes de este sitio he optado por limitar el ancho y centrar los tres elementos de la estructura (cabecera, contenido y pie), sin tocar <body>. Así queda el CSS ahora:

body > header {
    position: relative;
    max-width: 718px;
    margin: 0 auto;
    height: 7em;
   }
#cabeza-menu {
    left: 0;
    min-width: 285px;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 9999999;
    }
#contenido {
    background-color: white;
    border: gainsboro solid 1px;
    border-radius: 0.5em;
    font-family: Verdana, Geneva, "Lucida Grande", "DejaVu Sans",  sans-serif;
    margin: 0 auto;
    max-width: 700px;
    min-width: 285px;
    padding: 0 8px 8px 8px;
    }
#pie {
    font-family: Verdana, Tahoma, sans-serif;
    margin: 0 auto;
    max-width: 718px;
    } 

Le aplico al <header> un posicionamiento relativo para que la cabecera se posicione respecto a éste y no a la ventana del navegador. Le damos una altura suficiente para que el contenido se presente por debajo de la cabecera. Luego limito con max-width y centro con margin: 0 auto este <header> así como el contenido y el pie. Además le he puesto al contenido un relleno lateral de 8 px y un borde de 1 px. Así que su max-width será de 700 px como quería, aunque el <header> y el pie tendrán un ancho máximo de 718 px para absorber el relleno y borde del contenido.

Usando JavaScript para reponer el ancho del contenido

Botón para reponer ancho con JavaScript

Cuando el ancho de pantalla es mayor que el máximo que le hemos puesto al contenido (700 px), mostraremos un botón en la parte superior derecha para que el usuario pueda maximizar el ancho del contenido si lo desea. Una vez creado este botón, se muestra y oculta al cambiar el ancho de pantalla actuando con el evento resize de la ventana window. También modificamos el ancho máximo del <header> que contiene la cabecera y del <div id="pie">. El JavaScript necesario se encuentra en el módulo general.js, en la función adjudicarEventosGenerales() que se ejecuta en el window.onload de todas las páginas.

adjudicarEventosGenerales: function(omitirCssAfter) {
    window.setTimeout(function(){
        ...........
        //Agrega un botón en la cabecera para agrandar anchos
        wxG.agregarBotonAgrandarAncho();
        window.addEventListener("resize", wxG.agregarBotonAgrandarAncho, false);

    }, 1);
},

/* Agregar botón para agrandar anchos
*/
agregarBotonAgrandarAncho: function() {
    var boton = document.getElementById("boton-agrandar-ancho");
    var contenido = document.getElementById("contenido");
    if (!boton && (contenido.offsetWidth > 700)) {
        var cabeza = document.getElementById("cabeza");
        var boton = document.createElement("button");
        boton.id = "boton-agrandar-ancho";
        boton.innerHTML = "&#8633;";
        boton.type = "button";
        boton.title = "Maximizar ancho";
        var header = wxG.nodoPadre(document.getElementById("cabeza-menu"));
        var pie = document.getElementById("pie");
        boton.addEventListener("click", function(){
            if (wxG.estiloActual(contenido, "max-width") != "none"){
                contenido.style.maxWidth = "none";
                header.style.maxWidth = "none";
                pie.style.maxWidth = "none";
            } else {
                contenido.style.maxWidth = "700px";
                header.style.maxWidth = "718px";
                pie.style.maxWidth = "718px";
            }
        }, false);
        cabeza.appendChild(boton);
    }
},

Es importante ver que el botón no existe inicialmente. Si estamos en un dispositivo que genere el contenido con un ancho igual o inferior a 700px ese botón no será creado pues no será necesario. Esto supone una ventaja para no sobrecargar dispositivos móviles y, por otro lado, no hay necesidad de modificar todas las páginas agregando ese botón en el HTML. El estilo del botón lo configuramos con una consulta de medios:

/* permanece oculto en cualquier pantalla... */
#boton-agrandar-ancho {
    display: none;
}
/* ...pero se muestra para anchos mayores */
@media screen and (min-width: 800px) {
    #boton-agrandar-ancho {
        cursor: pointer;
        display: inline;
        margin: 4px 4px 0 0;
        padding: 0;
        border: 0;
        background-color: transparent;
        font-size: 2.5em;
        font-weight: bold;
        color: orange;
        background-image: none;
        float: right;
    }
}

Inicialmente el botón aparece oculto para todos los dispositivos. Si la pantalla tiene un ancho mayor de 800 px mostraremos el botón, pues ya fue creado dado que el contenido será mayor de 700 px. La diferencia entre estas dos cantidades es para albergar 2×8 px de márgenes del <body>, 2×8 px de relleno del contenido, 2×1 px de borde del contenido y unos 16 px para una posible barra de desplazamiento vertical. Todo suma 50 px, pero duplicamos este valor para que el efecto de maximizar el ancho de la página sea perceptible.