mitmario.dev

Buttons und ihre Zustände

CSS Im Browser 4 Min Lesezeit 3 BeispieleLektion 1 von 6

Ab hier geht es um Bausteine, die auf jeder Seite vorkommen. Neue Eigenschaften sind kaum dabei, dafür kommt fast alles aus den Abschnitten 2 bis 13 zusammen. Der Knopf ist der erste, und er ist der anspruchsvollste.

Der Default und warum er weg muss

Was der Knopf nicht erbt
*,
*::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 Knopf
   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 immer wieder: Schrift vererbt sich (Lektion 3.3), aber Formularelemente holen sich ihre Schrift vom Betriebssystem, statt sie vom Elternteil zu nehmen.

font: inherit holt sie zurück, und diese eine Zeile gehört an jeden Knopf, jedes Eingabefeld und jedes Textfeld. Sie ist der Grund dafür, dass ein Formular überhaupt aussehen kann wie ein Teil der Seite.

Zeig mit dem Inspector nacheinander auf beide Knöpfe und auf den Text daneben, dann steht es als Zahlen da. Am oberen Knopf font-family: Arial und font-size: 13.3333px, am Absatz daneben Georgia, serif und 18px, am unteren Knopf ebenfalls Georgia, serif und 18px. Der Größenunterschied ist keine Kleinigkeit: Der Knopf ohne Reset misst 138 × 21, der mit Reset rund 190 × 35.

Der Rest ist Aufräumen: eigener Rahmen oder gar keiner, eigener Radius, eigene Farben, eigener Innenabstand.

Die fünf Zustände

Die fünf Zustände
*,
*::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;
}

.knopf {
  min-height: 44px;
  padding: 10px 18px;
  border: 0;
  border-radius: 8px;
  font: inherit;
  color: #ffffff;
  background-color: #2563eb;
  cursor: pointer;
  transition: background-color 0.15s ease-out;
}

.knopf:hover {
  background-color: #1d4ed8;
}

.knopf:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 2px;
}

.knopf:active {
  background-color: #1e40af;
}

.knopf:disabled {
  background-color: #71717a;
  cursor: not-allowed;
}

.hinweis {
  max-width: 60ch;
  margin-top: 24px;
  color: #52525b;
}

Ein Knopf hat fünf Zustände, und für vier davon musst du selbst etwas tun.

  • Ruhe ist der Normalfall.
  • :hover sagt “hier bin ich”. Ein Farbwechsel reicht.
  • :focus-visible ist der wichtigste und der am häufigsten vergessene. Wer mit der Tastatur bedient, sieht ohne ihn nicht, wo er gerade steht. Er löste :focus ab, weil :focus auch bei der Maus zuschlug und der Ring dann als hässlich empfunden und wegoptimiert wurde. Mit :focus-visible bekommt ihn nur, wer ihn braucht (Lektion 2.4).
  • :active ist der Moment des Drückens. Etwas dunkler, oder minimal kleiner.
  • :disabled heißt, dass es jetzt nicht geht.

Zum Fokusring: Nimm outline und nicht border, denn outline braucht keinen Platz und verschiebt deshalb nichts. outline-offset rückt ihn ein Stück ab, damit er auf farbigen Flächen sichtbar bleibt.

Zwei Dinge, die man messen kann

Und zwar wörtlich messen, beide im Auskunftsstreifen des Inspectors.

Die Trefferfläche. Ein Knopf sollte mindestens 44 mal 44 Pixel groß sein, auch wenn sein Text kleiner ist. Das ist ungefähr die Fläche einer Fingerkuppe, und darunter fängt das Danebentippen an. min-height: 44px und genug Innenabstand erledigen das, ohne dass der Knopf klobig wirkt. Im Beispiel oben steht an beiden Knöpfen height: 44px, und weil der Zeiger beim Messen auf dem Knopf liegt, 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. Ein deaktivierter Knopf muss aber lesbar bleiben, sonst weiß niemand, was da gerade nicht geht. Nimm ein dunkleres Grau statt einer niedrigen Deckkraft: opacity senkt den Kontrast von Text und Fläche gleichzeitig, eine andere Farbe nur den einen Wert, den du meinst.

Auch das steht im Streifen. Am gesperrten Knopf des Beispiels sagt die Kontrastzeile 4,83:1 (reicht für Fließtext), das Grau ist also dunkel genug. Schreib versuchsweise opacity: 0.35 dazu, statt die Farbe zu wechseln: Dann steht dort 1,67:1 (zu wenig für Fließtext), und eine Zeile opacity erklärt, woher es kommt. Zwei Wege, die gleich aussehen sollen, und nur einer bleibt lesbar.

Zwei Zustände zeigt der Streifen dagegen nicht: :focus-visible und :active. Der Fokusring ist eine outline, und die steht nicht in seiner Liste. Dafür nimm die Tastatur und das Auge.

cursor: pointer, der Streitfall

Streng genommen steht die Hand für einen Link, nicht für einen Knopf. Ein Knopf ist ein Bedienelement, und Bedienelemente zeigen den normalen Zeiger, so wie überall sonst im Betriebssystem.

Trotzdem setzt praktisch jede Seite cursor: pointer an Knöpfe, und die Erwartung der Leute hat sich danach gerichtet. Ein Knopf ohne Hand fühlt sich heute kaputt an. Also: Mach es, aber wisse, warum es eigentlich falsch ist. Für :disabled ist cursor: not-allowed die passende Ergänzung.

Varianten über Werte statt über Regeln

Drei Varianten, eine Regel
*,
*::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;
}

.knopf {
  min-height: 44px;
  padding: 10px 18px;
  border: 2px solid var(--knopf-rand);
  border-radius: 8px;
  font: inherit;
  color: var(--knopf-text);
  background-color: var(--knopf-flaeche);
  cursor: pointer;
}

.knopf--primaer {
  --knopf-flaeche: #2563eb;
  --knopf-rand: #2563eb;
  --knopf-text: #ffffff;
}

.knopf--sekundaer {
  --knopf-flaeche: transparent;
  --knopf-rand: #2563eb;
  --knopf-text: #1d4ed8;
}

.knopf--gefahr {
  --knopf-flaeche: #b91c1c;
  --knopf-rand: #b91c1c;
  --knopf-text: #ffffff;
}

.hinweis {
  max-width: 60ch;
  margin-top: 24px;
  color: #52525b;
}

Der naheliegende Weg 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.

Der bessere Weg kommt 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. Wer eine vierte Variante braucht, schreibt drei Zeilen.

Dass das wirklich so ist, kannst du nachsehen: Zeig nacheinander auf die drei Knöpfe. height: 48px, padding: 10px 18px und font-size: 16px stehen dreimal gleich da, und auseinander gehen nur background-color, color und border. Die Custom Properties selbst siehst du im Streifen übrigens nicht, nur ihr Ergebnis: Am sekundären Knopf steht background-color: rgba(0, 0, 0, 0), nicht transparent, und Kontrast: 6,7:1 misst gegen das Weiß, das der Streifen dahinter gefunden hat.

Zum Mitnehmen

Ein Knopf ist die Stelle, an der jemand etwas auslöst. Er muss deshalb aussehen wie etwas, das man drücken kann, groß genug sein, dass man ihn trifft, und in jedem seiner fünf Zustände erkennbar bleiben. Das sind mehr Anforderungen als bei jedem anderen Baustein.

Jetzt du

Basis Konto, kostenlos

Zu 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 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe im Editor, direkt im Browser geprüft

    Öffnet sich mit dem Basis Konto.