Abschnitt 14 · Lektion 3
Listen und Tabellen
Listen
Eine Liste bringt zwei Dinge mit, die man oft nicht will, ein Aufzählungszeichen und einen Einzug. Die beiden kommen aus verschiedenen Eigenschaften. Daher die Verwirrung, wenn das Zeichen weg ist und der Einzug stehen bleibt.
<p class="etikett">nur list-style entfernt</p>
<nav class="halb">
<ul>
<li><a href="#">Start</a></li>
<li><a href="#">Projekte</a></li>
<li><a href="#">Kontakt</a></li>
</ul>
</nav>
<p class="etikett">list-style und padding entfernt</p>
<nav class="ganz">
<ul>
<li><a href="#">Start</a></li>
<li><a href="#">Projekte</a></li>
<li><a href="#">Kontakt</a></li>
</ul>
</nav> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.etikett {
margin: 0 0 8px 0;
font-size: 14px;
color: #52525b;
}
nav {
margin-bottom: 32px;
border-left: 2px solid #e4e4e7;
}
nav ul {
display: flex;
gap: 16px;
margin: 0;
}
/* Der Punkt ist weg, der Einzug nicht. Er kommt aus
dem padding des ul und hat mit dem Punkt nichts zu
tun. Die linke Linie macht ihn sichtbar. */
.halb ul {
list-style: none;
}
.ganz ul {
padding-inline-start: 0;
list-style: none;
} list-style: none entfernt das Zeichen. Der Einzug bleibt, denn er ist ein padding-inline-start am ul, das der Browser mitbringt. Eine Navigation braucht deshalb zwei Zeilen. Wenn du nur die erste schreibst, wunderst du dich über die eingerückte Kopfzeile.
Bei den beiden Listen im Beispiel steht der Unterschied im Inspector als Zahl, oben padding: 0px 0px 0px 40px, unten padding: 0px. Vierzig Pixel, die niemand hingeschrieben hat. Und sie sind auch dann noch da, wenn das Aufzählungszeichen längst weg ist.
list-style ist ein Shorthand für drei Eigenschaften. list-style-type ist das Zeichen, list-style-image ein Bild an seiner Stelle, und list-style-position entscheidet, ob es außerhalb des Textblocks steht (outside, der Standard) oder in ihm (inside). Ein none im Shorthand setzt Zeichen und Bild auf none und die Lage auf ihren Standard. Mehr braucht eine Navigation nicht. Bei mehrzeiligen Punkten sieht man den Unterschied der Lage sofort, denn mit inside rutscht die zweite Zeile unter das Zeichen.
<ul class="vorteile">
<li>Kein Konto nötig</li>
<li>Keine Werbung</li>
<li>Läuft auch offline</li>
</ul>
<p class="hinweis">
Früher musste man dafür den Punkt entfernen und mit
::before einen neuen bauen. Heute färbt ::marker das
echte Zeichen, und die Liste bleibt eine Liste.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.vorteile {
max-width: 40ch;
padding-inline-start: 24px;
}
.vorteile li {
margin-bottom: 8px;
}
.vorteile li::marker {
color: #2563eb;
font-size: 20px;
}
.hinweis {
max-width: 60ch;
margin-top: 24px;
color: #52525b;
} ::marker fasst das Zeichen selbst an, also Farbe, Schriftgröße, Schriftfamilie und mit content sogar das Zeichen. Safari kann davon bisher nur Farbe und Schriftgröße (Stand Oktober 2026). Früher musste man den Punkt entfernen und mit ::before einen neuen bauen, der dann keiner mehr war. Für Screenreader macht das einen Unterschied. Eine Liste ohne list-style wird in manchen Browsern nicht mehr als Liste angesagt.
Tabellen
Eine Tabelle ist für Daten da, nicht für Layout. Screenreader lesen Zeilen und Spalten in Beziehung zueinander vor, und ein Layout in einer Tabelle wird damit unverständlich. Für Layout gibt es Abschnitt 9 und 10.
<table>
<caption>Aufrufe im letzten Quartal</caption>
<thead>
<tr>
<th>Projekt</th>
<th>Sprache</th>
<th>Aufrufe</th>
</tr>
</thead>
<tbody>
<tr><td>Tipptrainer</td><td>TypeScript</td><td>128.401</td></tr>
<tr><td>Referenz</td><td>Astro</td><td>96.020</td></tr>
<tr><td>Newsletter</td><td>HTML</td><td>9844</td></tr>
</tbody>
</table> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
table {
width: 100%;
border-collapse: collapse;
}
caption {
caption-side: bottom;
padding-top: 8px;
text-align: left;
font-size: 14px;
color: #52525b;
}
th,
td {
padding: 8px 16px;
border-bottom: 1px solid #e4e4e7;
text-align: left;
}
th {
font-size: 14px;
text-transform: uppercase;
color: #52525b;
}
/* Zahlen nach rechts, damit die Stellen untereinander
stehen, und tabular-nums, damit jede Ziffer gleich
breit ist. */
th:last-child,
td:last-child {
text-align: right;
font-variant-numeric: tabular-nums;
}
tbody tr:nth-child(2n) {
background-color: #fafafa;
} Die erste Zeile ist immer border-collapse: collapse. Standard ist separate, und dabei bekommt jede Zelle ihren eigenen Rahmen mit einem kleinen Spalt dazwischen. Das sah 1998 gut aus. Mit collapse teilen sich Nachbarzellen eine Linie.
Danach kommt die Ausrichtung, und die richtet sich nach dem Inhalt der Spalte. Text steht links und Zahlen stehen rechts, damit Einer unter Einern und Tausender unter Tausendern stehen. font-variant-numeric: tabular-nums macht dazu jede Ziffer gleich breit. Sonst wackeln die Spalten trotzdem.
Dann kommt Kleinarbeit, die viel ausmacht. Zebrastreifen über tbody tr:nth-child(2n) aus Lektion 2.5, genug Innenabstand in den Zellen, eine ruhigere Kopfzeile, und caption-side: bottom, wenn die Beschriftung eher eine Quellenangabe ist als eine Überschrift.
Klickst du eine Zelle an, steht im Streifen genau eine Rahmenzeile, border-bottom: 1px solid rgb(228, 228, 231). Die anderen drei Seiten haben keine, deshalb stehen sie auch nicht da. Die Kontrastzeile ist am Zebrastreifen gegen dessen Farbe gemessen und nicht gegen Weiß, deshalb 20,12:1 statt 21:1.
table-layout: fixed nimmt die Spaltenbreiten aus der ersten Zeile statt aus der ganzen Tabelle, wenn die Tabelle selbst eine Breite hat. Wann sich das lohnt, steht in der CSS-Referenz unter Tabellen-Styling.
Tabellen auf schmalen Bildschirmen
<div class="tabellen-huelle" tabindex="0" role="region" aria-label="Aufrufe je Projekt">
<table>
<thead>
<tr>
<th>Projekt</th>
<th>Sprache</th>
<th>Aufrufe</th>
</tr>
</thead>
<tbody>
<tr><td>Tipptrainer</td><td>TypeScript</td><td>128.401</td></tr>
<tr><td>Referenz</td><td>Astro</td><td>96.020</td></tr>
</tbody>
</table>
</div>
<p class="hinweis">
Die Tabelle ist breiter als der Platz. Statt die Seite
waagerecht zu sprengen, scrollt nur ihre Hülle. Das
tabindex sorgt dafür, dass man sie auch mit den
Pfeiltasten bewegen kann.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
.tabellen-huelle {
overflow-x: auto;
}
table {
min-width: 460px;
border-collapse: collapse;
}
th,
td {
padding: 8px 16px;
border-bottom: 1px solid #e4e4e7;
text-align: left;
white-space: nowrap;
}
.hinweis {
max-width: 60ch;
margin-top: 16px;
color: #52525b;
} Eine Tabelle mit fünf Spalten passt auf kein Telefon. Wenn man sie umbricht, geht der Zusammenhang von Zeile und Spalte kaputt. Also lässt man sie breit und packt sie in eine Hülle mit overflow-x: auto, und dann scrollt die Tabelle und nicht die Seite.
Dazu gehört ein tabindex="0" an der Hülle. Ohne das ist ein scrollender Bereich nicht überall mit der Tastatur erreichbar. Firefox macht einen Scrollbereich ohne fokussierbare Kinder schon lange von allein anspringbar, Chrome seit Version 132 vom Januar 2025, Safari bis heute nicht (Stand Oktober 2026). Wer dort keine Maus benutzt, kommt an die rechten Spalten nicht heran.
Der Fokus landet dann auf der Hülle, also bekommt sie auch einen Namen, mit role="region" und einem aria-label. Ohne die beiden ist sie im Barrierefreiheitsbaum ein namenloser Kasten.
An Hülle und Tabelle kommst du hier mit dem Zeiger nicht heran. Die Zellen decken sie vollständig ab, und im Streifen steht immer nur das innerste Element. Deshalb fragst du in der Eingabezeile der Console. getComputedStyle(document.querySelector('.tabellen-huelle')).overflowX antwortet mit auto und getComputedStyle(document.querySelector('table')).minWidth mit 460px. Die Hülle ist in der schmalen Spalte 348 Pixel breit, und damit ist auch klar, warum hier überhaupt etwas zu scrollen ist.
In der Aufgabe stehen eine Navigation und eine Tabelle noch mit allem da, was der Browser mitbringt.
Zum Mitnehmen
Listenpunkte verlieren Zeichen und Einzug mit zwei Zeilen. Eine Tabelle braucht border-collapse, rechtsbündige Zahlen und, wenn sie breiter ist als der Platz, eine Hülle, die scrollt und per Tab erreichbar ist.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.