Abschnitt 14 · Lektion 1
Buttons und ihre Zustände
Buttons stehen auf jeder Seite, und an ihnen hängt mehr, als man ihnen ansieht. Neue Eigenschaften gibt es in diesem Abschnitt kaum. Dafür kommt fast alles aus den Abschnitten davor zusammen, und der Button ist die erste Komponente.
Der Default und warum er weg muss
<p>Ein Absatz in der Schrift der Seite. <button>Ein Button daneben</button></p>
<p>Und derselbe Button mit Reset. <button class="mit-reset">Ein Button daneben</button></p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: Georgia, serif;
font-size: 18px;
line-height: 1.6;
}
/* Schrift vererbt sich an fast alles, nur nicht an
Formularelemente. Die holen sich ihre Schrift vom
Betriebssystem, und deshalb steht der obere Button
in einer anderen Schrift und in einer anderen
Größe als der Text um ihn herum. */
.mit-reset {
font: inherit;
} Ein button ohne CSS ist grau, hat einen Rahmen, den es sonst nirgends gibt, und steht in einer anderen Schrift als der Rest der Seite. Das Letzte überrascht dich vielleicht, denn Schrift vererbt sich eigentlich (Lektion 3.3). Formularelemente holen sich ihre Schrift aber vom Betriebssystem und nicht vom Elternteil.
font: inherit holt sie zurück. Diese eine Zeile gehört an jeden Button, jedes Eingabefeld und jedes Textfeld, denn ohne sie kann ein Formular gar nicht aussehen wie ein Teil der Seite.
Im Inspector stehen die Zahlen dazu, an beiden Buttons und am Text daneben. Am oberen Button font-family: Arial und font-size: 13.3333px, am Absatz daneben Georgia, serif und 18px, am unteren Button ebenfalls Georgia, serif und 18px. Der Größenunterschied ist keine Kleinigkeit. Der Button ohne Reset misst rund 138 × 21, der mit Reset rund 190 × 35.
Der Rest ist Aufräumen, also eigener Rahmen oder gar keiner, eigener Radius, eigene Farben, eigener Innenabstand.
Die fünf Zustände
<div class="reihe">
<button class="button" type="button">Speichern</button>
<button class="button" type="button" disabled>Wird gesendet</button>
</div>
<p class="hinweis">
Fahr mit der Maus über den linken Button und halt die
Maustaste gedrückt. Für den Ring nimm die Tastatur:
klick neben die Buttons und drück dann Tabulator, oder
drück Umschalt und Tabulator zusammen. Drei
verschiedene Antworten, und der rechte zeigt die vierte.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.reihe {
display: flex;
gap: 12px;
}
.button {
min-height: 44px;
padding: 8px 16px;
border: 0;
border-radius: 8px;
font: inherit;
color: #ffffff;
background-color: #2563eb;
cursor: pointer;
transition: background-color 0.15s ease-out;
}
.button:hover {
background-color: #1d4ed8;
}
.button:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
.button:active {
background-color: #1e40af;
}
.button:disabled {
background-color: #71717a;
cursor: not-allowed;
}
.hinweis {
max-width: 60ch;
margin-top: 24px;
color: #52525b;
} Ein Button hat fünf Zustände, und für vier davon musst du selbst etwas tun.
- Ruhe ist der Normalfall.
:hoversagt “hier bin ich”. Ein Farbwechsel reicht.:focus-visibleist der wichtigste, und beim Bauen mit der Maus fällt sein Fehlen nicht auf. Wer mit der Tastatur bedient, sieht ohne ihn nicht, wo er gerade steht. Er löste:focusab, weil:focusauch bei der Maus zuschlug und der Ring dann als hässlich empfunden und wegoptimiert wurde. Mit:focus-visiblebekommt ihn nur, wer ihn braucht.:activeist der Moment des Drückens. Etwas dunkler, oder minimal kleiner.:disabledheißt, dass es jetzt nicht geht.
Für den Fokusring nehme ich outline und nicht border. outline braucht keinen Platz und verschiebt deshalb nichts. Mit outline-offset rückt der Ring ein Stück ab und bleibt auch auf farbigen Flächen sichtbar.
Zwei Dinge, die man messen kann
Und zwar wörtlich messen, beide im Auskunftsstreifen des Inspectors.
Die Trefferfläche. WCAG 2.2 verlangt für Trefferflächen mindestens 24 mal 24 Pixel (Kriterium 2.5.8, Stufe AA) und empfiehlt 44 mal 44 (2.5.5, Stufe AAA). Ich nehme bei Buttons die 44, auch wenn der Text kleiner ist. Das ist ungefähr die Fläche einer Fingerkuppe, und darunter fängt das Danebentippen an. Mit min-height: 44px und genug Innenabstand klappt das, ohne dass der Button klobig wirkt. Im Beispiel oben steht an beiden Buttons height: 44px. Der Zeiger liegt beim Messen ja auf dem Button, also siehst du dort gleich den Hover-Zustand, background-color: rgb(29, 78, 216) statt der Ruhefarbe rgb(37, 99, 235).
Der Kontrast im deaktivierten Zustand. “Ausgegraut” wird gern als Freibrief verstanden, den Text bis zur Unlesbarkeit aufzuhellen. WCAG nimmt gesperrte Bedienelemente beim Kontrast ausdrücklich aus. Ich halte sie trotzdem lesbar, sonst weiß niemand, was da gerade nicht geht. Dafür nehme ich ein dunkleres Grau statt einer niedrigen Deckkraft. opacity senkt den Kontrast von Text und Fläche gleichzeitig, und eine andere Farbe ändert nur den einen Wert, der gemeint ist.
<div class="reihe">
<button class="button grau" type="button" disabled>Wird gesendet</button>
<button class="button blass" type="button" disabled>Wird gesendet</button>
</div> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.reihe {
display: flex;
gap: 12px;
}
.button {
min-height: 44px;
padding: 8px 16px;
border: 0;
border-radius: 8px;
font: inherit;
color: #ffffff;
background-color: #2563eb;
cursor: not-allowed;
}
.grau:disabled {
background-color: #71717a;
}
/* So nicht: Deckkraft statt Farbe. Schrift und Fläche
verblassen zusammen. */
.blass:disabled {
opacity: 0.35;
} Auch das steht im Streifen, und im Beispiel liegen beide Wege nebeneinander. Am linken Button steht in der Kontrastzeile 4,83:1 (reicht für Fließtext), das Grau ist also dunkel genug. Der rechte behält sein Blau und bekommt stattdessen opacity: 0.35. Dort steht 1,67:1 (zu wenig für Fließtext) und darunter eine Zeile opacity, und daher kommt es. Zwei Buttons sollen beide gesperrt aussehen, und nur einer bleibt lesbar.
Zwei Zustände stehen dagegen nicht im Streifen, :focus-visible und :active. Der Fokusring ist eine outline, und die fehlt in der Liste. Hier bleiben dir die Tastatur und das Auge.
Die Hand am Button
Streng genommen steht die Hand für einen Link und nicht für einen Button. Ein Button ist ein Bedienelement, und Bedienelemente zeigen den normalen Zeiger, so wie überall sonst im Betriebssystem.
Trotzdem setzt praktisch jede Seite cursor: pointer an Buttons, und die Erwartung der Leute hat sich danach gerichtet. Ein Button ohne Hand fühlt sich heute kaputt an. Ich setze sie trotzdem, weil man sie inzwischen erwartet. Für :disabled passt cursor: not-allowed dazu.
Varianten über Werte statt über Regeln
<div class="reihe">
<button class="button button--primaer" type="button">Speichern</button>
<button class="button button--sekundaer" type="button">Abbrechen</button>
<button class="button button--gefahr" type="button">Löschen</button>
</div>
<p class="hinweis">
Die drei Varianten tauschen nur Werte aus. Alles, was
Größe, Form, Schrift und Verhalten angeht, steht ein
einziges Mal in .button und bleibt dadurch überall gleich.
</p> *,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px;
font-family: system-ui, sans-serif;
font-size: 16px;
line-height: 1.5;
}
.reihe {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.button {
min-height: 44px;
padding: 8px 16px;
border: 2px solid var(--button-rand);
border-radius: 8px;
font: inherit;
color: var(--button-text);
background-color: var(--button-flaeche);
cursor: pointer;
}
.button--primaer {
--button-flaeche: #2563eb;
--button-rand: #2563eb;
--button-text: #ffffff;
}
.button--sekundaer {
--button-flaeche: transparent;
--button-rand: #2563eb;
--button-text: #1d4ed8;
}
.button--gefahr {
--button-flaeche: #b91c1c;
--button-rand: #b91c1c;
--button-text: #ffffff;
}
.hinweis {
max-width: 60ch;
margin-top: 24px;
color: #52525b;
} Naheliegend wäre, für jede Variante einen eigenen Regelblock zu schreiben. Nach drei Varianten stehen dann Größe, Form und Schrift dreimal da, und beim nächsten Umbau ändert jemand zwei davon.
Besser geht es mit dem Weg aus Lektion 5.4. Die Grundregel liest ihre Farben aus Custom Properties, und die Varianten setzen nur diese Werte neu. Alles Strukturelle steht genau einmal. Für eine vierte Variante schreibst du drei Zeilen.
Dass das wirklich so ist, siehst du an den drei Buttons im Streifen. height: 44px, padding: 8px 16px und font-size: 16px stehen dreimal gleich da. Auseinander gehen nur background-color, color und border. Die Custom Properties selbst stehen dort übrigens nicht, nur ihr Ergebnis. Am sekundären Button steht background-color: rgba(0, 0, 0, 0) und nicht transparent, und Kontrast: 6,7:1 ist gegen das Weiß gemessen, das der Inspector dahinter findet.
In der Aufgabe stehen drei Buttons noch so da, wie der Browser sie liefert.
Zum Mitnehmen
Ein Button erbt die Schrift der Seite nicht von allein, font: inherit holt sie zurück. Er muss aussehen wie etwas, das man drücken kann, mindestens 44 Pixel hoch sein und in jedem seiner fünf Zustände erkennbar bleiben.
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.