На початку цього року я самостійно видав електронну книгу під назвою Розуміння обіцянок JavaScript (безкоштовно для скачування). Незважаючи на те, що я не мав наміру перетворювати її на друковану книгу, достатньо людей поцікавилися друкованою версією, яку я також вирішив опублікувати самостійно. Я думав, що це буде легкою вправою з використанням HTML і CSS для створіть PDF-файл, а потім відправте його на принтер. Чого я не розумів, так це того, що не мав відповіді на важливу частину друкованої книги: на зміст.
Склад змісту
По суті, зміст є досить простим. Кожен рядок представляє частину книги або веб-сторінки та вказує, де можна знайти цей вміст. Зазвичай рядки містять три частини:
- Назва розділу або розділу
- Лідери (тобто точки, тире або лінії), які візуально пов’язують заголовок з номером сторінки
- Номер сторінки
Зміст легко створити в таких інструментах обробки текстів, як Microsoft Word або Google Docs, але оскільки мій вміст був у Markdown, а потім перетворений у HTML, це був не найкращий варіант для мене. Я хотів чогось автоматизованого, що працювало б із HTML, щоб генерувати зміст у форматі, придатному для друку. Я також хотів, щоб кожен рядок був посиланням, щоб його можна було використовувати на веб-сторінках і PDF-файлах для навігації по документу. Я також хотів розташовувати точки між заголовком і номером сторінки.
І ось я почав досліджувати.
Я натрапив на дві чудові публікації в блозі про створення змісту за допомогою HTML і CSS. Перший був «Створіть зміст із вашого HTML» від Джулі Блан. Джулі працювала далі PagedJS, полізаповнення для відсутніх сторінкових мультимедійних функцій у веб-браузерах, які правильно форматують документи для друку. Я почав з прикладу Джулі, але виявив, що це не зовсім спрацювало для мене. Далі я знайшов Крістофа Грабо «Чувні лінії лідерів TOC із CSS» пост, який представив концепцію використання CSS Grid (на відміну від підходу Джулі на основі float) для полегшення вирівнювання. Але знову ж таки, його підхід не зовсім відповідав моїм цілям.
Проте, прочитавши ці дві публікації, я відчув, що досить добре розумію проблеми з макетом, щоб почати самостійно. Я використав фрагменти з обох дописів у блозі, а також додав деякі нові концепції 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>
Застосування стилів до змісту
Як тільки я встановив розмітку, яку планував використовувати, наступним кроком було застосування деяких стилів.
Спочатку я видалив автоматично згенеровані числа. Ви можете зберегти автоматично згенеровані числа у своєму власному проекті, якщо хочете, але зазвичай книги мають ненумеровані передмови та післямови, включені в список розділів, що робить автоматично згенеровані числа неправильними.
Для моєї мети я б заповнював номери розділів вручну, а потім налаштував макет, щоб у списку верхнього рівня не було жодних відступів (таким чином, вирівнюючи його з абзацами), і кожен вбудований список має відступ на два пробіли. Я вирішив використовувати a 2ch
значення відступу, тому що я все ще не був упевнений, який шрифт я використаю. The 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>
Стилізацію заголовка та номера сторінки
Оскільки список було оформлено на мій смак, настав час перейти до оформлення окремого елемента списку. Для кожного пункту змісту заголовок і номер сторінки мають бути в одному рядку, при цьому заголовок ліворуч, а номер сторінки — праворуч.
Можливо, ви подумаєте: «Не проблема, ось для чого призначений 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
. Номер сторінки вирівнюється праворуч, як це традиційно в змісті.
Єдина інша зміна, яку я зробив на цьому етапі, — це приховати текст «Сторінка». Це корисно для програм зчитування з екрана, але непотрібно візуально, тому я використав a традиційний 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>
Маючи цю основу, я перейшов до звернення до лідерів між заголовком і сторінкою.
Створення точкових лідерів
Лідери настільки поширені в друкованих ЗМІ, що ви можете задатися питанням, чому CSS вже не підтримує це? Відповідь: це робить. Ну, свого роду.
Насправді є leader()
функція, визначена в CSS-генерований вміст для специфікації медіа-сторінок. Однак, як і більшість специфікацій медіа-сторінок, ця функція не реалізована в жодному браузері, тому виключаємо її як опцію (принаймні на той момент, коли я пишу це). Його навіть немає в списку caniuse.com, мабуть, тому, що ніхто цього не реалізував і немає планів чи сигналів, що вони це зроблять.
На щастя, і Джулі, і Крістоф вже розглянули цю проблему у своїх відповідних публікаціях. Щоб вставити виноски точок, вони обидва використовували a ::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
приховано, тому всі ці додаткові точки невидимі. У результаті виходить гарний зміст з точковими лідерами.
Однак є ще щось, що потребує розгляду.
Сара також вказала мені, що всі ці точки враховуються як текст для програми зчитування з екрана. Так що ти чуєш? «Введення дот dot dot dot…», доки не будуть оголошені всі крапки. Це жахливий досвід для користувачів програми зчитування з екрана.
Рішення – вставити додатковий елемент с 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;
}
Тепер програми зчитування з екрана ігноруватимуть точки і позбавлять користувачів від розчарування від прослуховування кількох анонсованих точок.
Завершальні штрихи
На даний момент компонент змісту виглядає досить добре, але він може використовувати деякі незначні деталі. Для початку більшість книг візуально зміщують назви розділів від назв підрозділів, тому я зробив елементи верхнього рівня жирним і ввів поля для відокремлення підрозділів від наступних розділів:
.toc-list > li > a { font-weight: bold; margin-block-start: 1em;
}
Далі я хотів очистити вирівнювання номерів сторінок. Все виглядало нормально, коли я використовував шрифт фіксованої ширини, але для шрифтів зі змінною шириною точки виноски могли утворювати зигзагоподібний візерунок, коли вони підлаштовуються під ширину номера сторінки. Наприклад, будь-який номер сторінки з 1 буде вужчим, ніж інші, що призведе до того, що точки виношування будуть не вирівняні з точками на попередніх або наступних рядках.
Щоб вирішити цю проблему, я встановив 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 було більш складним завданням, ніж я очікував, але я дуже задоволений результатом. Цей підхід не тільки є достатньо гнучким для розміщення глав і підрозділів, але він добре обробляє підрозділи, не оновлюючи CSS. Загальний підхід працює на веб-сторінках, де потрібно посилатися на різні місця розміщення вмісту, а також на PDF-файлах, де зміст містить посилання на різні сторінки. І, звичайно, він також чудово виглядає у друкованому вигляді, якщо ви коли-небудь схильні використовувати його в брошурі чи книзі.
Я хотів би подякувати Джулі Блан і Крістофу Грабо за їхні чудові публікації в блозі щодо створення змісту, оскільки обидва вони були безцінними, коли я починав роботу. Я також хотів би подякувати Сарі Соуейдан за її відгук про доступність, коли я працював над цим проектом.
Ідеальний зміст із HTML + CSS спочатку опубліковано на CSS-трюки. Ти повинен отримати інформаційний бюлетень.
- "
- 100
- a
- МЕНЮ
- абсолют
- доступність
- розмістити
- Achieve
- через
- доданий
- Додатковий
- адреса
- ВСІ
- дозволяє
- вже
- оголошений
- відповідь
- Застосовувати
- підхід
- відповідний
- навколо
- автоматичний
- Автоматизований
- основа
- оскільки
- перед тим
- почалася
- буття
- між
- Блог
- Повідомлення в блозі
- рада
- сміливий
- бонус
- книги
- Box
- будувати
- випадок
- виклик
- зміна
- Глава
- Вибирати
- клас
- Колонка
- Приходити
- загальний
- складності
- компонент
- концепція
- З'єднуватися
- розгляду
- Контейнер
- зміст
- зміст
- Core
- може
- створення
- вирішене
- деталь
- різний
- цифр
- дисплей
- документація
- малювання
- кожен
- елементи
- приступати
- вбудований
- встановлений
- все
- приклад
- відмінно
- виключення
- Здійснювати
- очікуваний
- досвід
- риси
- зворотний зв'язок
- Перший
- виправляти
- гнучкий
- потік
- після
- формат
- знайдений
- фонд
- від
- функція
- принципово
- породжувати
- отримання
- добре
- великий
- сітка
- щасливий
- висота
- корисний
- допомагає
- тут
- приховувати
- Однак
- HTTPS
- реалізовані
- важливо
- включені
- індивідуальний
- екземпляр
- Намір
- питання
- IT
- JavaScript
- тримати
- лідер
- Лідери
- Led
- Лінія
- ліній
- LINK
- список
- Перераховані
- Прослуховування
- списки
- місць
- Довго
- подивився
- шукати
- made
- зробити
- РОБОТИ
- макіяж
- вручну
- карта
- Матерія
- Питання
- засоби
- Медіа
- Microsoft
- може бути
- мінімальний
- більше
- найбільш
- рухатися
- множинний
- а саме
- Імена
- Переміщення
- потреби
- номер
- номера
- Очевидний
- зсув
- добре
- варіант
- Опції
- порядок
- Інше
- загальний
- власний
- частина
- Викрійки
- Люди
- ідеальний
- частин
- запланований
- плани
- точка
- положення
- Пости
- досить
- попередній
- Проблема
- обробка
- проект
- власність
- мета
- цілей
- RE
- читач
- читачі
- читання
- реалізувати
- зрозумів,
- відносини
- представляє
- в результаті
- то ж
- Екран
- самовизначитися
- семантика
- комплект
- установка
- простий
- Розмір
- So
- рішення
- ВИРІШИТИ
- деякі
- що в сім'ї щось
- пробіли
- специфікації
- старт
- почалася
- Як і раніше
- підтримка
- Команда
- отже
- Мислення
- три
- Зв'язаний
- час
- назва
- інструменти
- верхній рівень
- традиційний
- перетворений
- типово
- розуміння
- оновлення
- використання
- користувачі
- значення
- різний
- версія
- вид
- W3
- хотів
- Web
- Веб-браузери
- Що
- в той час як
- вітер
- в
- без
- Work
- працював
- працює
- б
- лист
- рік
- вашу