Een perfecte inhoudsopgave met HTML + CSS PlatoBlockchain Data Intelligence. Verticaal zoeken. Ai.

Een perfecte inhoudsopgave met HTML + CSS

Eerder dit jaar heb ik in eigen beheer een e-boek gepubliceerd genaamd JavaScript-beloften begrijpen (gratis te downloaden). Ook al was het niet mijn bedoeling om er een gedrukt boek van te maken, toch namen genoeg mensen contact op met een gedrukte versie, zodat ik besloot die ook in eigen beheer uit te geven. Ik dacht dat het een gemakkelijke oefening zou zijn om HTML en CSS te gebruiken om genereer een pdf en stuur deze naar de printer. Wat ik me niet realiseerde was dat ik geen antwoord had op een belangrijk onderdeel van een gedrukt boek: de inhoudsopgave.

De samenstelling van een inhoudsopgave

In de kern is een inhoudsopgave vrij eenvoudig. Elke regel vertegenwoordigt een deel van een boek of webpagina en geeft aan waar je die inhoud kunt vinden. Meestal bevatten de regels drie delen:

  1. De titel van het hoofdstuk of de sectie
  2. Leidingen (dwz die punten, streepjes of lijnen) die de titel visueel verbinden met het paginanummer
  3. Het paginanummer

Een inhoudsopgave is gemakkelijk te genereren in tekstverwerkingsprogramma's zoals Microsoft Word of Google Docs, maar omdat mijn inhoud in Markdown stond en vervolgens werd omgezet in HTML, was dat geen goede optie voor mij. Ik wilde iets geautomatiseerd dat met HTML zou werken om de inhoudsopgave te genereren in een formaat dat geschikt was om af te drukken. Ik wilde ook dat elke regel een link was, zodat deze in webpagina's en pdf's kon worden gebruikt om door het document te navigeren. Ik wilde ook puntjes tussen de titel en het paginanummer.

En dus begon ik te onderzoeken.

Ik kwam twee uitstekende blogberichten tegen over het maken van een inhoudsopgave met HTML en CSS. De eerste was "Bouw een inhoudsopgave van uw HTML" door Julie Blanc. Julie werkte aan PaginadJS, een polyfill voor ontbrekende paginamediafuncties in webbrowsers die documenten correct opmaken om af te drukken. Ik begon met het voorbeeld van Julie, maar ontdekte dat het niet helemaal werkte voor mij. Vervolgens vond ik Christoph Grabo's "Responsieve TOC-aanlooplijnen met CSS" post, die het concept introduceerde om CSS Grid te gebruiken (in tegenstelling tot Julie's float-gebaseerde benadering) om uitlijning gemakkelijker te maken. Maar nogmaals, zijn benadering was niet helemaal geschikt voor mijn doeleinden.

Na het lezen van deze twee berichten had ik echter het gevoel dat ik de lay-outproblemen goed genoeg begreep om zelf aan de slag te gaan. Ik heb stukken uit beide blogposts gebruikt en ook enkele nieuwe HTML- en CSS-concepten aan de aanpak toegevoegd om tot een resultaat te komen waar ik blij mee ben.

De juiste opmaak kiezen

Bij het bepalen van de juiste opmaak voor een inhoudsopgave, dacht ik vooral aan de juiste semantiek. In wezen gaat een inhoudsopgave over een titel (hoofdstuk of subsectie) die is gekoppeld aan een paginanummer, bijna als een sleutel/waarde-paar. Dat bracht me op twee opties:

  • Een optie is om een ​​tabel te gebruiken (<table>) met één kolom voor de titel en één kolom voor de pagina.
  • Dan is er nog de vaak ongebruikte en vergeten definitielijst (<dl>) element. Het fungeert ook als een sleutel-waardekaart. Dus nogmaals, de relatie tussen de titel en het paginanummer zou duidelijk zijn.

Elk van deze leek goede opties totdat ik me realiseerde dat ze eigenlijk alleen werken voor inhoudsopgaven op één niveau, namelijk alleen als ik een inhoudsopgave wilde hebben met alleen hoofdstuknamen. Als ik echter subsecties in de inhoudsopgave wilde tonen, had ik geen goede opties. Tabelelementen zijn niet geweldig voor hiërarchische gegevens, en hoewel definitielijsten technisch genest kunnen worden, leek de semantiek niet correct. Dus ging ik terug naar de tekentafel.

Ik besloot voort te bouwen op Julie's aanpak en een lijst te gebruiken; ik heb echter gekozen voor een geordende lijst (<ol>) in plaats van een ongeordende lijst (<ul>). Ik denk dat een geordende lijst in dit geval meer op zijn plaats is. Een inhoudsopgave vertegenwoordigt een lijst van hoofdstukken en ondertitels in de volgorde waarin ze in de inhoud voorkomen. De volgorde is belangrijk en mag niet verloren gaan in de opmaak.

Helaas betekent het gebruik van een geordende lijst dat de semantische relatie tussen de titel en het paginanummer verloren gaat, dus mijn volgende stap was om die relatie binnen elk lijstitem te herstellen. De eenvoudigste manier om dit op te lossen, is door simpelweg het woord "pagina" voor het paginanummer in te voegen. Op die manier is de relatie van het getal ten opzichte van de tekst duidelijk, zelfs zonder enig ander visueel onderscheid.

Hier is een eenvoudig HTML-skelet dat de basis vormde van mijn opmaak:

<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>

Stijlen toepassen op de inhoudsopgave

Nadat ik de markup had vastgesteld die ik wilde gebruiken, was de volgende stap het toepassen van enkele stijlen.

Eerst heb ik de automatisch gegenereerde nummers verwijderd. Je kunt ervoor kiezen om de automatisch gegenereerde nummers in je eigen project te houden als je wilt, maar het is gebruikelijk dat boeken ongenummerde voorwoorden en nawoorden in de lijst met hoofdstukken hebben, waardoor de automatisch gegenereerde nummers onjuist zijn.

Voor mijn doel zou ik de hoofdstuknummers handmatig invullen en vervolgens de lay-out aanpassen zodat de lijst op het hoogste niveau geen opvulling heeft (waardoor deze wordt uitgelijnd met alinea's) en elke ingesloten lijst wordt ingesprongen met twee spaties. Ik koos ervoor om een ​​te gebruiken 2ch opvulwaarde omdat ik nog steeds niet helemaal zeker wist welk lettertype ik zou gebruiken. De ch Met lengte-eenheid kan de opvulling relatief zijn aan de breedte van een teken - ongeacht welk lettertype wordt gebruikt - in plaats van een absolute pixelgrootte die er inconsistent uit zou kunnen zien.

Dit is de CSS waarmee ik eindigde:

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

Sara Soueidan wees me erop dat WebKit-browsers lijstsemantiek verwijderen wanneer: list-style-type is none, dus ik moest toevoegen role="list" in de HTML om het te behouden:

<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 Embed-terugval

De titel en het paginanummer stylen

Nu de lijst naar mijn smaak was gestyled, was het tijd om verder te gaan met het stylen van een individueel lijstitem. Voor elk item in de inhoudsopgave moeten de titel en het paginanummer op dezelfde regel staan, met de titel links en het paginanummer rechts uitgelijnd.

Je denkt misschien: “Geen probleem, daar is flexbox voor!” Je bent niet verkeerd! Flexbox kan inderdaad voor de juiste uitlijning van de titelpagina zorgen. Maar er zijn enkele lastige afstemmingsproblemen wanneer de leiders worden toegevoegd, dus ik heb in plaats daarvan gekozen voor de benadering van Christoph met behulp van een raster, wat als bonus ook helpt bij titels met meerdere regels. Hier is de CSS voor een afzonderlijk item:

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

Het raster heeft twee kolommen, waarvan de eerste is auto-formaat om de volledige breedte van de container te vullen, minus de tweede kolom, die de grootte heeft van: max-content. Het paginanummer is naar rechts uitgelijnd, zoals traditioneel in een inhoudsopgave.

De enige andere wijziging die ik op dit punt heb aangebracht, was het verbergen van de "Pagina" -tekst. Dit is handig voor schermlezers, maar visueel onnodig, dus ik gebruikte a traditioneel visually-hidden klasse om het uit het zicht te verbergen:

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

En natuurlijk moet de HTML worden bijgewerkt om die klasse te gebruiken:

<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>

Met deze basis op zijn plaats, ging ik verder met het aanspreken van de leiders tussen de titel en de pagina.

CodePen Embed-terugval

Puntleiders maken

Leiders komen zo vaak voor in gedrukte media dat je je misschien afvraagt, waarom ondersteunt CSS dat niet al? Het antwoord is: het doet. Soort van.

Er is eigenlijk een leader() functie gedefinieerd in de Door CSS gegenereerde inhoud voor specificatie van gepagineerde media. Echter, zoals met veel van de specificaties van de paginamedia, is deze functie in geen enkele browser geïmplementeerd, waardoor het als een optie wordt uitgesloten (tenminste op het moment dat ik dit schrijf). Het staat niet eens op caniuse.com, vermoedelijk omdat niemand het heeft geïmplementeerd en er geen plannen of signalen zijn dat ze dat zullen doen.

Gelukkig hebben zowel Julie als Christoph dit probleem al in hun respectieve berichten behandeld. Om de puntleiders in te voegen, gebruikten ze allebei a ::after pseudo-element met zijn content eigenschap ingesteld op een zeer lange reeks punten, zoals deze:

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

De ::after pseudo-element is ingesteld op een absolute positie om het uit de stroom van de pagina te halen en terugloop naar andere regels te voorkomen. De tekst is rechts uitgelijnd omdat we de laatste puntjes van elke regel gelijk willen hebben met het nummer aan het einde van de regel. (Later meer over de complexiteit hiervan.) .title element is ingesteld om een ​​relatieve positie te hebben, zodat de ::after pseudo-element breekt niet uit zijn doos. Ondertussen is de overflow is verborgen dus al die extra puntjes zijn onzichtbaar. Het resultaat is een mooie inhoudsopgave met puntleiders.

Er is echter nog iets dat aandacht behoeft.

Sara wees me er ook op dat al die punten tellen als tekst voor schermlezers. Dus wat hoor je? "Introductie punt punt punt punt ..." totdat alle punten zijn aangekondigd. Dat is een vreselijke ervaring voor gebruikers van schermlezers.

De oplossing is om een ​​extra element in te voegen met aria-hidden ingesteld op true en gebruik dan dat element om de punten in te voegen. Dus de HTML wordt:

<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>

En de CSS wordt:

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

Nu zullen schermlezers de stippen negeren en gebruikers de frustratie besparen van het luisteren naar meerdere stippen die worden aangekondigd.

CodePen Embed-terugval

Afwerking

Op dit moment ziet de inhoudsopgave-component er redelijk goed uit, maar het kan wat klein detailwerk gebruiken. Om te beginnen verschuiven de meeste boeken de hoofdstuktitels visueel van de titels van subsecties, dus ik maakte de items op het hoogste niveau vetgedrukt en introduceerde een marge om subsecties te scheiden van de hoofdstukken die volgden:

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

Vervolgens wilde ik de uitlijning van de paginanummers opruimen. Alles zag er goed uit toen ik een lettertype met vaste breedte gebruikte, maar voor lettertypen met variabele breedte konden de aanlooppunten een zigzagpatroon vormen als ze zich aanpassen aan de breedte van een paginanummer. Elk paginanummer met een 1 zou bijvoorbeeld smaller zijn dan andere, wat resulteert in aanlooppunten die niet goed zijn uitgelijnd met de punten op vorige of volgende regels.

Verkeerd uitgelijnde cijfers en punten in een inhoudsopgave.
Een perfecte inhoudsopgave met HTML + CSS

Om dit probleem op te lossen, stel ik in font-variant-numeric naar tabular-nums dus alle nummers worden met dezelfde breedte behandeld. Door ook de minimale breedte in te stellen op 2ch, Ik heb ervoor gezorgd dat alle cijfers met één of twee cijfers perfect op één lijn liggen. (Misschien wilt u dit instellen op 3ch als uw project meer dan 100 pagina's heeft.) Hier is de uiteindelijke CSS voor het paginanummer:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Uitgelijnde aanlooppunten in een inhoudsopgave.
Een perfecte inhoudsopgave met HTML + CSS

En daarmee is de inhoudsopgave compleet!

CodePen Embed-terugval

Conclusie

Het maken van een inhoudsopgave met alleen HTML en CSS was een grotere uitdaging dan ik had verwacht, maar ik ben erg blij met het resultaat. Deze benadering is niet alleen flexibel genoeg om hoofdstukken en subsecties te accommoderen, maar het behandelt ook subsubsecties netjes zonder de CSS bij te werken. De algemene aanpak werkt op webpagina's waar u naar de verschillende locaties met inhoud wilt linken, maar ook op PDF's waar u wilt dat de inhoudsopgave naar verschillende pagina's linkt. En natuurlijk ziet het er ook geweldig uit als je het ooit in een brochure of boek wilt gebruiken.

Ik wil Julie Blanc en Christoph Grabo bedanken voor hun uitstekende blogberichten over het maken van een inhoudsopgave, want beide waren van onschatbare waarde toen ik begon. Ik wil ook Sara Soueidan bedanken voor haar feedback over toegankelijkheid terwijl ik aan dit project werkte.


Een perfecte inhoudsopgave met HTML + CSS oorspronkelijk gepubliceerd op CSS-trucs. Je zou moeten ontvang de nieuwsbrief.

Tijdstempel:

Meer van CSS-trucs