Abschnitt 12 · Lektion 3
Nutzervorlieben abfragen
Media Queries können mehr abfragen als die Breite. Sie fragen auch, was der Nutzer eingestellt hat, und das ist der wertvollere Teil.
Ohne Aufgabe
Zu dieser Lektion gibt es nur Beispiele und keine Aufgabe.
Beim Prüfen lässt sich keine Systemeinstellung setzen. Man kann dort nicht so tun, als hätte jemand den Dunkelmodus an oder Animationen abgeschaltet. Eine Aufgabe, deren Ergebnis sich nicht ehrlich prüfen lässt, wäre aber eine Behauptung statt einer Übung. In den Beispielen steht die Bedingung deshalb als Klasse da, und im Kommentar steht jeweils die echte Media Query.
Deine eigenen Einstellungen kommen hier trotzdem an, und du kannst sie abfragen. In der Eingabezeile der Console beantwortet der Browser jede dieser Bedingungen direkt.
matchMedia('(prefers-color-scheme: dark)').matches
Auf einem System, das auf hell steht, antwortet er false. Steht das Betriebssystem auf dunkel, antwortet dieselbe Zeile mit true, auch ohne dass die Seite neu geladen wurde. Eine Media Query im Stylesheet bekommt dieselbe Antwort, denn sie fragt an derselben Stelle. Der Schalter für hell und dunkel auf dieser Seite ändert daran nichts. Was im Reiter „Browser“ läuft, fragt das System und nicht die Seite drumherum.
Gefragt wird der Stoff trotzdem, nämlich im Abschnittstest.
Hell und dunkel
<p class="titel">so sieht die Karte im hellen Schema aus</p>
<div class="karte hell">
<h3>Tipptrainer</h3>
<p>Zehnfinger im Browser, ohne Anmeldung.</p>
<a class="button" href="#">Ansehen</a>
</div>
<p class="titel">und so im dunklen</p>
<div class="karte dunkel">
<h3>Tipptrainer</h3>
<p>Zehnfinger im Browser, ohne Anmeldung.</p>
<a class="button" href="#">Ansehen</a>
</div> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: system-ui, sans-serif;
line-height: 1.5;
background-color: #d4d4d8;
}
.titel {
margin-bottom: 4px;
font-size: 14px;
color: #3f3f46;
}
/* Die Farben liegen als Rollen in Tokens. Im echten
Projekt steht das hier in
@media (prefers-color-scheme: dark) und schaltet
sich von allein um. Als Klasse steht es hier, damit
beide Schemata nebeneinander zu sehen sind. */
.hell {
--farbe-text: #18181b;
--farbe-gedaempft: #52525b;
--farbe-flaeche: #ffffff;
--farbe-akzent: #2563eb;
}
.dunkel {
--farbe-text: #fafafa;
--farbe-gedaempft: #a1a1aa;
--farbe-flaeche: #18181b;
--farbe-akzent: #60a5fa;
}
/* Und hier steht jede Regel genau einmal. Sie liest
nur Tokens, sie kennt kein Schema. */
.karte {
max-width: 360px;
margin-bottom: 24px;
padding: 24px;
border-radius: 12px;
color: var(--farbe-text);
background-color: var(--farbe-flaeche);
}
.karte h3 {
margin: 0 0 4px 0;
}
.karte p {
margin: 0 0 16px 0;
color: var(--farbe-gedaempft);
}
.button {
display: inline-block;
padding: 8px 16px;
border-radius: 8px;
font-weight: 700;
text-decoration: none;
color: var(--farbe-flaeche);
background-color: var(--farbe-akzent);
} @media (prefers-color-scheme: dark) fragt, ob der Nutzer sein System auf dunkel gestellt hat. Es gibt auch light, das brauchst du selten, weil es der Standard ist.
Im Beispiel stehen die beiden Schemata nur in den Tokens. Die Regeln darunter kennen kein Schema, sie lesen nur var(--farbe-flaeche) und var(--farbe-text).
Dafür haben die Tokens in Lektion 5.5 Rollennamen bekommen. Hieße das Token --grau-900, müsstest du im Dunkelmodus jede Regel anfassen, die es benutzt, denn dort ist Grau plötzlich das Hellste. Heißt es --farbe-text, änderst du eine Zeile. Die vollständige Umsetzung baust du in 12.6.
Bewegung
<p class="titel">mit Bewegung: der Balken pulsiert</p>
<div class="leiste"><div class="balken bewegt"></div></div>
<p class="titel">ohne Bewegung: derselbe Zustand, nur ruhig</p>
<div class="leiste"><div class="balken ruhig"></div></div>
<p class="hinweis">
Für manche Menschen löst Bewegung auf dem Bildschirm Übelkeit oder
Schwindel aus. Sie stellen im Betriebssystem ein, dass Animationen
reduziert werden sollen, und prefers-reduced-motion gibt dir diese
Einstellung weiter. Wichtig dabei: Das Ergebnis muss ohne die
Animation trotzdem vollständig sein.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: system-ui, sans-serif;
line-height: 1.5;
background-color: #f4f4f5;
}
.titel {
margin-bottom: 4px;
font-size: 14px;
color: #52525b;
}
.leiste {
height: 16px;
margin-bottom: 24px;
border-radius: 999px;
background-color: #e4e4e7;
overflow: hidden;
}
.balken {
width: 60%;
height: 100%;
border-radius: 999px;
background-color: #2563eb;
}
/* Im echten Projekt steht die ruhige Fassung in
@media (prefers-reduced-motion: reduce). Der Balken
ist in beiden Fällen bei 60 Prozent: die Animation
schmückt, sie trägt keine Information. */
.bewegt {
animation: pulsieren 1.2s ease-in-out infinite;
}
/* Zu .ruhig steht hier mit Absicht keine Regel. Ruhig
heißt genau das: es läuft keine Animation. */
@keyframes pulsieren {
50% {
opacity: 0.35;
}
}
.hinweis {
max-width: 60ch;
color: #52525b;
} @media (prefers-reduced-motion: reduce) ist die wichtigste dieser Abfragen, und mit Geschmack hat sie nichts zu tun.
Menschen mit einer Störung des Gleichgewichtssinns wird von bewegten Flächen auf dem Bildschirm richtig übel, bis hin zu Schwindel, der noch Stunden anhält. Sie stellen deshalb im Betriebssystem ein, dass Animationen reduziert werden sollen, und diese Einstellung reicht der Browser an dich weiter.
Was die Animation zeigt, muss ohne sie trotzdem da sein. Im Beispiel steht der Balken in beiden Fällen bei 60 Prozent, das Pulsieren ist nur Schmuck. Eine Animation, die Information trägt, ist auch ohne Bewegungsempfindlichkeit eine schlechte Idee. Wer gerade nicht hinschaut, sieht sie nämlich auch nicht.
Mehr dazu kommt in Abschnitt 13, dort gibt es eine eigene Lektion dafür.
Kontrast und Transparenz
Zwei weitere gehören zur selben Familie.
@media (prefers-contrast: more)für Menschen, die höheren Kontrast eingestellt haben. Dünne graue Linien und blasse Sekundärtexte sind dann die falsche Wahl.@media (prefers-reduced-transparency: reduce)für alle, denen milchige Überlagerungen die Lesbarkeit nehmen. Einbackdrop-filterbekommt dort einen deckenden Hintergrund. Diese Abfrage kennen bisher nur Chrome und Edge (Stand Oktober 2026), in den anderen bleibt der Block einfach aus.
Maus oder Finger
<p class="titel">für die Maus: kompakte Trefferflächen</p>
<nav class="menue maus">
<a href="#">Start</a>
<a href="#">Projekte</a>
<a href="#">Kontakt</a>
</nav>
<p class="titel">für den Finger: mindestens 44px hoch</p>
<nav class="menue finger">
<a href="#">Start</a>
<a href="#">Projekte</a>
<a href="#">Kontakt</a>
</nav>
<p class="titel">so nicht: an der Breite festgemacht</p>
<nav class="menue breite">
<a href="#">Start</a>
<a href="#">Projekte</a>
<a href="#">Kontakt</a>
</nav>
<p class="hinweis">
Abgefragt wird das mit pointer: coarse und hover: hover, nicht mit
einer Breite. Ein Tablet ist breit und wird mit dem Finger bedient,
ein Laptop mit Touchscreen ist beides, und ein schmales Fenster auf
dem Desktop ist keins von beidem.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: system-ui, sans-serif;
line-height: 1.5;
background-color: #f4f4f5;
}
.titel {
margin-bottom: 4px;
font-size: 14px;
color: #52525b;
}
.menue {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 24px;
}
.menue a {
display: flex;
align-items: center;
border-radius: 8px;
font-weight: 700;
text-decoration: none;
color: #ffffff;
background-color: #18181b;
}
/* Im echten Projekt steht das in
@media (pointer: fine) beziehungsweise
@media (pointer: coarse). */
.maus a {
min-height: 32px;
padding: 0 8px;
}
.finger a {
min-height: 44px;
padding: 0 16px;
}
/* So nicht: groß, sobald es schmal ist. Das trifft ein
schmales Fenster mit Maus und verfehlt ein breites
Tablet. */
.breite a {
min-height: 32px;
padding: 0 8px;
}
@media (max-width: 600px) {
.breite a {
min-height: 44px;
padding: 0 16px;
}
}
.hinweis {
max-width: 60ch;
color: #52525b;
} Die nächste Abfrage wird gern durch eine Breite ersetzt, und dann fragt man das Falsche.
@media (pointer: coarse)fragt, ob das Hauptzeigegerät ungenau ist, also ein Finger.@media (pointer: fine)fragt nach einem genauen Zeiger, also Maus oder Stift.@media (hover: hover)fragt, ob überhaupt gehovert werden kann.
„Touch“ heißt nicht „kleiner Bildschirm“. Ein Tablet ist breit und wird mit dem Finger bedient. Ein Laptop mit Touchscreen hat beides, pointer meldet dort aber nur das Hauptgerät, meistens das Touchpad. Den Finger daneben sieht erst any-pointer: coarse. Und ein schmal gezogenes Fenster auf dem Desktop ist keins von beidem, hat aber dieselbe Breite wie ein Handy.
Mit drei Zeilen in der Eingabezeile der Console kannst du das ausprobieren. Auf einem Rechner mit Maus antwortet matchMedia('(pointer: coarse)').matches mit false und matchMedia('(hover: hover)').matches mit true. Im Vollbild mit Handy in der Viewport-Leiste meldet innerWidth dann 400. Die Seite ist also wirklich so schmal wie ein Handy, und die beiden anderen Antworten sind unverändert false und true. Die Breite hat sich geändert, das Zeigegerät nicht, und wer Trefferflächen an eine Breite hängt, verwischt diesen Unterschied.
Im Beispiel steht dieser Fehler als dritte Leiste. Sie fragt max-width: 600px statt nach dem Zeiger. In der schmalen Spalte neben dem Editor sind ihre Buttons deshalb 44 Pixel hoch, obwohl die Maus in der Hand liegt und weit und breit kein Finger im Spiel ist. Im Vollbild mit Laptop in der Viewport-Leiste schrumpfen sie auf 32, egal womit man zeigt. Auf einem breiten Tablet bekäme der Finger dieselben 32.
Trefferflächen richten sich deshalb nach dem Zeiger und nicht nach der Breite. Die Barrierefreiheitsrichtlinien WCAG 2.2 verlangen mindestens 24 mal 24 Pixel (Kriterium 2.5.8, Stufe AA) und empfehlen 44 mal 44 (2.5.5, Stufe AAA). Für den Finger nehme ich die 44. Und alles, was nur bei :hover erscheint, braucht einen zweiten Weg, denn auf einem Touchgerät gibt es kein Hovern.
@media (orientation: landscape) gehört auch in diese Familie. Daran denkt man, wenn jemand sein Handy quer hält, mit viel Breite und sehr wenig Höhe.
Zum Mitnehmen
Media Queries fragen auch nach Vorlieben, nach hell oder dunkel, Bewegung oder nicht, Finger oder Maus. Der Zeiger ist dabei nicht die Breite.
Jetzt du
Basis Konto, kostenlosIm Editor änderst du die Beispiele dieser Lektion und lässt sie gleich laufen. So merkst du am schnellsten, ob es sitzt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.