Contenido perfecto con HTML + CSS trucos CSS
A principios de este año, publiqué un libro electrónico llamado Comprender las promesas de JavaScript (gratis para descargar). Aunque no tenía intención de convertirlo en un libro impreso, bastantes personas pidieron una versión impresa, que decidí publicar. Decidí que sería un ejercicio fácil con HTML y CSS generar un PDF y luego enviarlo a la impresora. Lo que no me di cuenta fue que no tenía una respuesta a una parte importante del libro impreso: el contenido. .
La composición del contenido.
Básicamente, el contenido es bastante simple. Cada línea forma parte de un libro o página web e indica dónde puedes encontrar este contenido. Por lo general, las líneas contienen tres partes:
- El título del capítulo o sección.
- Líderes (es decir, puntos, guiones o líneas) que asocian visualmente el título con el número de página
- el numero de pagina
El contenido se genera fácilmente en herramientas de procesamiento de textos como Microsoft Word o Google Docs, pero dado que mi contenido estaba en Markdown y luego se transformó en HTML, esta no era una buena opción para mí. Quería algo automatizado que trabajara con HTML para generar contenido en un formato adecuado para la impresión. También quería que cada línea fuera un enlace para que pudiera usarse en páginas web y archivos PDF para navegar por el documento. También quería puntos principales entre el título y el número de página.
Así que comencé a investigar.
Encontré dos excelentes publicaciones de blog para crear una tabla de contenido con HTML y CSS. el primero fue "Cree una tabla de contenido desde su HTML" por Julie Blanc Julie está trabajando en PaginadoJSfunción multimedia del navegador de páginas faltantes que formatea correctamente los documentos imprimibles. Empecé con el ejemplo de Julie, pero descubrí que no funcionaba para mí. Luego encontré la de Christoph Grabo Pautas de CSS de TOC receptivo una publicación que introdujo el concepto de usar CSS Grid (a diferencia del enfoque basado en flotadores de Julie) para facilitar la alineación. Una vez más, sin embargo, su enfoque no era del todo apropiado para mis propósitos.
Sin embargo, después de leer estas dos publicaciones, sentí que tenía una comprensión lo suficientemente buena de los problemas de diseño como para hacerlo solo. Usé fragmentos de ambas publicaciones de blog y agregué algunos conceptos nuevos de HTML y CSS al enfoque para obtener un resultado con el que estoy satisfecho.
Elegir la marca correcta
Cuando decidí el marcado correcto para la tabla de contenido, pensé primero en la semántica correcta. En general, el contenido está relacionado con un título (capítulo o subsección) con un número de página, casi como un par clave-valor. Esto me llevó a dos opciones:
- Una opción es usar una tabla (
<table>
) con una columna para el título y una columna para la página. - Luego está la lista de definiciones que a menudo no se usa y se olvida (
<dl>
) elemento. También actúa como una tarjeta de clave-valor. De nuevo, la conexión entre el título y el número de página sería obvia.
Ambas parecían buenas opciones hasta que me di cuenta de que realmente solo funcionan para tablas de contenido en un nivel, es decir, solo si quiero tener una tabla de contenido con solo nombres de capítulos. Sin embargo, si quería mostrar subsecciones en la tabla de contenido, no tenía buenas opciones. Los elementos de la tabla no son buenos para los datos jerárquicos y, aunque las listas técnicas de definiciones se pueden anidar, la semántica no parecía correcta. Entonces, volví a la mesa de dibujo.
Decidí basarme en el enfoque de Julie y usar una lista; sin embargo, elegí una lista ordenada (<ol>
) en lugar de una lista desordenada<ul>
). Creo que una lista ordenada es más apropiada en este caso. El índice es una lista de capítulos y subtítulos en el orden en que aparecen en el índice. El orden importa y no debe perderse en el marcado.
Desafortunadamente, usar una lista ordenada significa perder el enlace semántico entre el título y el número de página, así que mi siguiente paso fue restaurar ese enlace a cada elemento de la lista. La forma más fácil de resolver esto es simplemente insertar la palabra "página" antes del número de página, de esta manera la relación del número con el texto es clara, incluso sin ninguna otra distinción visual.
Aquí hay un esqueleto HTML simple que forma la base de mi etiquetado:
<ol class="toc-list">
<li>
<a href="https://css-tricks.com/a-perfect-table-of-contents-with-html-css/#link_to_heading">
<span class="title">Chapter or subsection title</span>
<span class="page">Page 1</span>
</a>
<ol>
<!-- subsection items -->
</ol>
</li>
</ol>
Aplicar estilos al contenido
Una vez que identifiqué la marca que planeaba usar, el siguiente paso fue aplicar algunos estilos.
Primero eliminé los números generados automáticamente. Si lo desea, puede optar por mantener los números generados automáticamente en su propio proyecto, pero es común que los libros tengan prefacios y epílogos sin numerar incluidos en la lista de capítulos, lo que hace que los números generados automáticamente sean incorrectos.
Para mi propósito, completaría los números de los capítulos manualmente, luego ajustaría el diseño para que la lista de nivel superior no se llene (de esta manera la alineo con los párrafos) y cada lista integrada tiene sangría con dos intervalos. elegí usar 2ch
valor del relleno porque todavía no estaba muy seguro de qué fuente usar ch
La unidad de longitud permite que el relleno sea relativo al ancho del carácter, sin importar qué fuente se use, en lugar de un tamaño de píxel absoluto, que puede parecer inconsistente.
Aquí está el CSS con el que terminé:
.toc-list, .toc-list ol {
list-style-type: none;
}
.toc-list {
padding: 0;
}
.toc-list ol {
padding-inline-start: 2ch;
}
sarah suedán me dijo que los navegadores WebKit eliminan la semántica de la lista cuando list-style-type
es none
así que tuve que agregar role="list"
en HTML para guardar:
<ol class="toc-list" role="list">
<li>
<a href="https://css-tricks.com/a-perfect-table-of-contents-with-html-css/#link_to_heading">
<span class="title">Chapter or subsection title</span>
<span class="page">Page 1</span>
</a>
<ol role="list">
<!-- subsection items -->
</ol>
</li>
</ol>
Formar el título y el número de página
Con la lista estilizada, en mi opinión, era hora de pasar a estilizar un elemento separado de la lista. Para cada elemento de contenido, el título y el número de página deben estar en la misma línea, con el título a la izquierda y el número de página alineado a la derecha.
Puede que estés pensando: "¡No hay problema, para eso está flexbox!" ¡Tienes razón! Flexbox realmente puede lograr la alineación de página correcta. Pero hay algunos problemas de alineación difíciles al agregar líderes, así que opté por cambiar al enfoque de Christophe, usando una red que es una ventaja, ya que también ayuda con los títulos de varias líneas. Aquí está el CSS para un elemento individual:
.toc-list li > a {
text-decoration: none;
display: grid;
grid-template-columns: auto max-content;
align-items: end;
}
.toc-list li > a > .page {
text-align: right;
}
La cuadrícula tiene dos columnas, la primera de las cuales es auto
-tamaño para llenar todo el ancho del contenedor, menos la segunda columna, que está dimensionada max-content
El número de página está alineado a la derecha, como es tradicional en el contenido.
El único otro cambio que hice en ese momento fue ocultar el texto "Página". Esto es útil para los lectores de pantalla, pero no es visualmente necesario, así que usé uno tradicional visually-hidden
clase para ocultarlo de la vista:
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(100%);
height: 1px;
overflow: hidden;
position: absolute;
width: 1px;
white-space: nowrap;
}
Y, por supuesto, HTML debe actualizarse para usar esta clase:
<ol class="toc-list" role="list">
<li>
<a href="https://css-tricks.com/a-perfect-table-of-contents-with-html-css/#link_to_heading">
<span class="title">Chapter or subsection title</span>
<span class="page"><span class="visually-hidden">Page</span> 1</span>
</a>
<ol role="list">
<!-- subsection items -->
</ol>
</li>
</ol>
Sobre esta base, recurrí a los líderes entre el título y la página.
Crear líderes de puntos
Los líderes son tan comunes en los medios impresos que quizás se pregunte por qué CSS ya no admite esto. La respuesta es: lo hace. Bueno, de alguna manera.
De hecho, hay un leader()
función definida en Contenido generado por CSS para la especificación de la página de medios. Sin embargo, como ocurre con la mayoría de las especificaciones para páginas multimedia, esta función no está implementada en ningún navegador, por lo que está desactivada como opción (al menos en el momento de escribir este artículo). Ni siquiera en la lista de caniuse.comprobablemente porque nadie se ha dado cuenta y no hay planes ni señales de que lo hagan.
Afortunadamente, tanto Julie como Christoph ya han abordado este tema en sus respectivas publicaciones. Para insertar líderes de puntos, ambos usaron ::after
pseudo-elemento con su propio content
la propiedad establecida en una cadena muy larga de puntos, como:
.toc-list li > a > .title {
position: relative;
overflow: hidden;
}
.toc-list li > a .title::after {
position: absolute;
padding-left: .25ch;
content: " . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . ";
text-align: right;
}
EN ::after
el pseudoelemento se establece en la posición absoluta para sacarlo del flujo de la página y evitar envolverlo en otras líneas. El texto está alineado a la derecha porque queremos que los últimos puntos de cada línea estén alineados con el número al final de la línea (Más sobre la complejidad de esto más adelante). .title
el elemento está configurado para tener una posición relativa de modo que ::after
el pseudoelemento no sale de su caja overflow
está oculto para que todos estos puntos extra sean invisibles. El resultado es un contenido hermoso con líderes puntuales.
Sin embargo, hay algo más que debe tenerse en cuenta.
Sarah también me señaló que todos estos puntos cuentan como texto para los lectores de pantalla. Entonces, ¿qué escuchas? "Introducción punto a punto..." hasta que se hayan declarado todos los puntos. Esta es una experiencia terrible para los usuarios de lectores de pantalla.
La solución es insertar un elemento adicional con aria-hidden
ajustado a true
y luego use este elemento para insertar los puntos. Así es como HTML se convierte en:
<ol class="toc-list" role="list">
<li>
<a href="https://css-tricks.com/a-perfect-table-of-contents-with-html-css/#link_to_heading">
<span class="title">Chapter or subsection title<span class="leaders" area-hidden="true"></span></span>
<span class="page"><span class="visually-hidden">Page</span> 1</span>
</a>
<ol role="list">
<!-- subsection items -->
</ol>
</li>
</ol>
Y CSS se convierte en:
.toc-list li > a > .title {
position: relative;
overflow: hidden;
}
.toc-list li > a .leaders::after {
position: absolute;
padding-left: .25ch;
content: " . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . "
". . . . . . . . . . . . . . . . . . . . . . . ";
text-align: right;
}
Los lectores de pantalla ahora ignorarán los puntos y ahorrarán a los usuarios la frustración de escuchar el anuncio de múltiples puntos.
Últimos retoques
En este punto, el componente de contenido se ve bastante bien, pero puede usar algunos pequeños detalles. Para empezar, la mayoría de los libros cambian visualmente los títulos de los capítulos de los títulos de las subsecciones, así que puse en negrita los elementos de nivel superior e introduje un cuadro de subsección de los siguientes capítulos:
.toc-list > li > a {
font-weight: bold;
margin-block-start: 1em;
}
Luego quise borrar la alineación de los números de página. Todo se veía bien cuando usaba una fuente de ancho fijo, pero para las fuentes de ancho variable, los puntos iniciales podrían formar un patrón en zigzag a medida que se ajustaban al ancho. Por ejemplo, cada número de página con 1 sería más angosto que los demás, a los puntos principales que no están alineados con los puntos de las líneas anteriores o siguientes.
Para solucionar este problema, pregunté font-variant-numeric
para tabular-nums
por lo que todos los números se tratan con el mismo ancho. Al establecer también el ancho mínimo en 2ch
Garanticé que todos los números con uno o dos dígitos están perfectamente alineados. (Es posible que desee establecer esto en 3ch
si su proyecto tiene más de 100 páginas). Aquí está el CSS final para el número de página:
.toc-list li > a > .page {
min-width: 2ch;
font-variant-numeric: tabular-nums;
text-align: right;
}
¡Y con eso el contenido está completo!
Conclusión
Crear una tabla de contenido con nada más que HTML y CSS fue más desafiante de lo que esperaba, pero estoy muy feliz con el resultado. Este enfoque no solo es lo suficientemente flexible para acomodar capítulos y subsecciones, sino que también maneja bien las subsecciones sin actualizar CSS. El enfoque holístico funciona en páginas web en las que desea vincular a diferentes ubicaciones de contenido, así como en archivos PDF en los que desea que el contenido se vincule a diferentes páginas. Y, por supuesto, también se ve muy bien impreso si alguna vez lo ha usado en un folleto o libro.
Me gustaría agradecer a Julie Blanc y Christoph Grabo por sus excelentes publicaciones de blog sobre creación de contenido, ya que ambos fueron invaluables cuando comencé. También me gustaría agradecer a Sarah Suedan por sus comentarios sobre accesibilidad mientras trabajaba en este proyecto.
Deja una respuesta