Идеальное оглавление с HTML + CSS PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Идеальное оглавление с помощью HTML + CSS

Ранее в этом году я самостоятельно опубликовал электронную книгу под названием Понимание обещаний JavaScript (бесплатно для скачивания). Несмотря на то, что у меня не было никакого намерения превращать ее в печатную книгу, достаточно людей обратились с вопросами о печатной версии, и я решил опубликовать ее самостоятельно. Я подумал, что это будет простое упражнение с использованием HTML и CSS для создать PDF-файл и отправить его на принтер. Чего я не осознавал, так это того, что у меня не было ответа на важную часть печатной книги: оглавление.

Состав оглавления

По своей сути оглавление довольно простое. Каждая строка представляет собой часть книги или веб-страницы и указывает, где можно найти этот контент. Обычно строки состоят из трех частей:

  1. Название главы или раздела
  2. Выноски (то есть те точки, тире или линии), которые визуально соединяют заголовок с номером страницы.
  3. Номер страницы

Оглавление легко создать в текстовых редакторах, таких как Microsoft Word или Google Docs, но, поскольку мой контент был в формате Markdown, а затем преобразован в HTML, это не было для меня хорошим вариантом. Я хотел что-то автоматизированное, которое работало бы с HTML для создания оглавления в формате, подходящем для печати. Я также хотел, чтобы каждая строка была ссылкой, чтобы ее можно было использовать на веб-страницах и в PDF-файлах для навигации по документу. Я также хотел использовать точечные лидеры между заголовком и номером страницы.

И вот я начал исследования.

Я наткнулся на две отличные записи в блоге о создании оглавления с помощью HTML и CSS. Первый был «Создайте оглавление из вашего HTML» Джули Блан. Джулия работала над PagedJS, полифилл для отсутствующих функций постраничного мультимедиа в веб-браузерах, который правильно форматирует документы для печати. Я начал с примера Джули, но обнаружил, что он не совсем подходит для меня. Затем я нашел Кристофа Грабо. «Отзывчивые выноски оглавления с помощью CSS» post, в котором была представлена ​​концепция использования CSS Grid (в отличие от подхода Джули, основанного на плавающих элементах), чтобы упростить выравнивание. Но опять же, его подход не совсем подходил для моих целей.

Тем не менее, прочитав эти два поста, я почувствовал, что достаточно хорошо разобрался в вопросах компоновки, чтобы приступить к самостоятельной работе. Я использовал фрагменты из обоих сообщений в блоге, а также добавил несколько новых концепций HTML и CSS в подход, чтобы получить результат, которым я доволен.

Правильный выбор разметки

Выбирая правильную разметку для оглавления, я думал в первую очередь о правильной семантике. По сути, оглавление — это заголовок (глава или подраздел), привязанный к номеру страницы, почти как пара ключ-значение. Это привело меня к двум вариантам:

  • Один из вариантов — использовать таблицу (<table>) с одним столбцом для заголовка и одним столбцом для страницы.
  • Затем есть часто неиспользуемый и забытый список определений (<dl>) элемент. Он также действует как карта ключ-значение. Итак, еще раз, связь между заголовком и номером страницы будет очевидной.

Любой из этих вариантов казался хорошим, пока я не понял, что они действительно работают только для одноуровневых оглавлений, а именно, только если я хочу иметь оглавление только с названиями глав. Однако, если бы я хотел показать подразделы в оглавлении, у меня не было хороших вариантов. Элементы таблицы не подходят для иерархических данных, и хотя списки определений технически могут быть вложенными, семантика кажется неверной. Итак, я вернулся к чертежной доске.

Я решил использовать подход Джулии и использовать список; однако я выбрал упорядоченный список (<ol>) вместо ненумерованного списка (<ul>). Я думаю, что упорядоченный список более уместен в этом случае. Оглавление представляет собой список глав и подзаголовков в том порядке, в котором они появляются в содержании. Порядок имеет значение и не должен теряться в разметке.

К сожалению, использование упорядоченного списка означает потерю семантической связи между заголовком и номером страницы, поэтому моим следующим шагом было восстановление этой связи в каждом элементе списка. Самый простой способ решить эту проблему — просто вставить слово «страница» перед номером страницы. Таким образом, отношение числа к тексту становится четким даже без каких-либо других визуальных различий.

Вот простой HTML-скелет, который лег в основу моей разметки:

<ol class="toc-list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol> <!-- subsection items --> </ol> </li>
</ol>

Применение стилей к оглавлению

После того, как я установил разметку, которую планировал использовать, следующим шагом было применение некоторых стилей.

Во-первых, я удалил автоматически сгенерированные числа. Вы можете сохранить автоматически сгенерированные номера в своем собственном проекте, если хотите, но книги обычно имеют ненумерованные предисловия и послесловия, включенные в список глав, что делает автоматически сгенерированные номера неправильными.

Для моей цели я бы заполнил номера глав вручную, а затем отрегулировал макет, чтобы список верхнего уровня не имел никаких отступов (таким образом, выравнивая его с абзацами), а каждый встроенный список имел отступ в два пробела. Я решил использовать 2ch значение заполнения, потому что я все еще не был уверен, какой шрифт я буду использовать. ch Единица длины позволяет отступу относиться к ширине символа — независимо от того, какой шрифт используется, — а не к абсолютному размеру в пикселях, который может выглядеть противоречиво.

Вот CSS, который у меня получился:

.toc-list, .toc-list ol { list-style-type: none;
} .toc-list { padding: 0;
} .toc-list ol { padding-inline-start: 2ch;
}

Сара Суидан указал мне, что браузеры WebKit удаляют семантику списка, когда list-style-type is none, поэтому мне нужно было добавить role="list" в HTML, чтобы сохранить его:

<ol class="toc-list" role="list"> <li> <a href="#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>
Резервный вариант для встраивания CodePen

Оформление заголовка и номера страницы

Когда список был настроен по моему вкусу, пришло время перейти к стилю отдельного элемента списка. Для каждого элемента в оглавлении заголовок и номер страницы должны быть в одной строке, при этом заголовок должен располагаться слева, а номер страницы — справа.

Вы можете подумать: «Нет проблем, для этого и нужен flexbox!» Вы не ошиблись! Flexbox действительно может обеспечить правильное выравнивание титульного листа. Но есть некоторые сложные проблемы с выравниванием, когда добавляются выноски, поэтому вместо этого я решил использовать подход Кристофа с использованием сетки, которая в качестве бонуса также помогает с многострочными заголовками. Вот CSS для отдельного элемента:

.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;
}

Сетка состоит из двух столбцов, первый из которых auto-sized, чтобы заполнить всю ширину контейнера, за вычетом второго столбца, размер которого равен max-content. Номер страницы выровнен по правому краю, как это принято в оглавлении.

Единственное другое изменение, которое я сделал на этом этапе, — это скрыть текст «Страница». Это полезно для программ чтения с экрана, но не нужно визуально, поэтому я использовал традиционный visually-hidden класс чтобы скрыть его от просмотра:

.visually-hidden { clip: rect(0 0 0 0); clip-path: inset(100%); height: 1px; overflow: hidden; position: absolute; width: 1px; white-space: nowrap;
}

И, конечно же, HTML нужно обновить, чтобы использовать этот класс:

<ol class="toc-list" role="list"> <li> <a href="#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>

Имея эту основу, я перешел к лидерам между заголовком и страницей.

Резервный вариант для встраивания CodePen

Создание точечных выносок

Лидеры настолько распространены в печатных СМИ, что вам может быть интересно, почему CSS еще не поддерживает это? Ответ: оно делает. Ну, вроде.

На самом деле есть leader() функция, определенная в Контент, сгенерированный CSS для спецификации Paged Media. Однако, как и в большинстве спецификаций постраничного мультимедиа, эта функция не реализована ни в одном браузере, поэтому исключаю ее как опцию (по крайней мере, на момент написания этой статьи). Его даже нет в списке caniuse.com, предположительно потому, что его никто не реализовал и нет никаких планов или сигналов, что они будут.

К счастью, и Джули, и Кристоф уже решили эту проблему в своих постах. Чтобы вставить точечные лидеры, они оба использовали ::after псевдоэлемент с его content свойство установлено в очень длинную строку точек, например:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .title::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Ассоциация ::after псевдоэлементу устанавливается абсолютная позиция, чтобы убрать его из потока страницы и избежать переноса на другие строки. Текст выравнивается по правому краю, потому что мы хотим, чтобы последние точки каждой строки совпадали с числом в конце строки. (Подробнее о сложностях этого позже.) .title элемент имеет относительное положение, поэтому ::after псевдоэлемент не вырывается из своей коробки. Между тем, overflow скрыт, поэтому все лишние точки невидимы. В результате получается красивое оглавление с точечными надписями.

Однако есть еще кое-что, что требует рассмотрения.

Сара также указала мне, что все эти точки считаются текстом для программ чтения с экрана. Итак, что вы слышите? «Введение точка точка точка точка…» до тех пор, пока не будут объявлены все точки. Это ужасный опыт для пользователей программ чтения с экрана.

Решение состоит в том, чтобы вставить дополнительный элемент с aria-hidden установлен в true а затем используйте этот элемент для вставки точек. Таким образом, HTML становится:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title<span class="leaders" aria-hidden="true"></span></span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>

И CSS становится:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .leaders::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Теперь скринридеры будут игнорировать точки и избавят пользователей от разочарования, связанного с объявлением нескольких точек.

Резервный вариант для встраивания CodePen

Последние штрихи

На данный момент компонент оглавления выглядит довольно хорошо, но над ним стоит немного поработать. Начнем с того, что в большинстве книг заголовки глав визуально смещены от заголовков подразделов, поэтому я выделил элементы верхнего уровня жирным шрифтом и ввел поля, чтобы отделить подразделы от последующих глав:

.toc-list > li > a { font-weight: bold; margin-block-start: 1em;
}

Затем я хотел почистить выравнивание номеров страниц. Все выглядело нормально, когда я использовал шрифт фиксированной ширины, но для шрифтов переменной ширины ведущие точки могли в конечном итоге сформировать зигзагообразный узор, поскольку они подстраиваются под ширину номера страницы. Например, любой номер страницы с цифрой 1 будет уже, чем другие, что приведет к смещению точек на выноске с точками на предыдущих или последующих строках.

Смещенные числа и точки в оглавлении.
Идеальное оглавление с помощью HTML + CSS

Чтобы решить эту проблему, я установил font-variant-numeric в tabular-nums поэтому все числа обрабатываются с одинаковой шириной. Также установив минимальную ширину на 2ch, я убедился, что все числа с одной или двумя цифрами идеально выровнены. (Вы можете установить это на 3ch если в вашем проекте более 100 страниц.) Вот окончательный CSS для номера страницы:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Выровненные лидерные точки в оглавлении.
Идеальное оглавление с помощью HTML + CSS

И на этом оглавление завершено!

Резервный вариант для встраивания CodePen

Заключение

Создание оглавления только с использованием HTML и CSS оказалось более сложной задачей, чем я ожидал, но я очень доволен результатом. Этот подход не только достаточно гибок, чтобы вместить главы и подразделы, но и прекрасно обрабатывает подразделы без обновления CSS. Общий подход работает на веб-страницах, где вы хотите ссылаться на различные местоположения контента, а также на PDF-файлах, где вы хотите, чтобы оглавление ссылалось на разные страницы. И, конечно же, он также отлично смотрится в печати, если вы когда-нибудь захотите использовать его в брошюре или книге.

Я хотел бы поблагодарить Джули Блан и Кристофа Грабо за их отличные посты в блоге о создании оглавления, так как оба они были неоценимы, когда я только начинал. Я также хотел бы поблагодарить Сару Суэйдан за ее отзыв о доступности, когда я работал над этим проектом.


Идеальное оглавление с помощью HTML + CSS первоначально опубликовано CSS-хитрости, Вам следует получить информационный бюллетень.

Отметка времени:

Больше от CSS хитрости