In ihrem Buch „CSS Secrets“ versammelte die international bekannte CSS-Spezialistin Lea Verou 47 Techniken, wie sich mit elegantem, klarem CSS-Code typische Webdesign-Probleme lösen lassen. Drei dieser Code-Schnipsel veröffentlichen wir im oreillyblog. Nach Silbentrennung geht es heute um Checkbox-Designs.
Problem
Designer wollen möglichst jedes Website-Element genau kontrollieren. Müssen Grafikdesigner mit beschränkter CSS-Erfahrung ein Website-Mockup erstellen, haben die Formularelemente fast immer ein Design, bei dem sich der CSS-Entwickler am liebsten die Haare ausreißen würde.
Aus Gründen der Lesbarkeit sprechen wir in diesem Secret von Checkboxen (oder „Ankreuzfelder“). Trotzdem lässt sich alles hier Besprochene nicht nur auf Checkboxen, sondern auch auf Radiobuttons anwenden, falls nicht anders vermerkt.
Als CSS eingeführt wurde, waren Stildefinitionen für Formulare nur sehr beschränkt nutzbar. Selbst heute sind sie in den verschiedenen CSS-Spezifikationen nicht besonders klar formuliert. Allerdings haben die Browser im Laufe der Jahre immer mehr CSS-Eigenschaften für Formularelemente zugelassen, wodurch es inzwischen recht große Gestaltungsmöglichkeiten gibt.
Leider gehören Checkboxen und Radiobuttons nicht zu diesen Formularelementen. Bis heute ermöglichen die meisten Browser so gut wie keine Stildefinitionen. Als Folge finden sich die Website-Autoren entweder mit dem Standardaussehen ab, oder sie verwenden furchtbare und komplexe Hacks, bei denen die Elemente mit <div>-Elementen und JS nachgebaut werden.
Gibt es eine Möglichkeit, diese Einschränkungen zu umgehen und das Aussehen unserer Checkboxen anzupassen, ohne den Code unnötig aufzublähen und die Semantik und Zugänglichkeit aufzugeben?
Die Lösung
Bis vor einigen Jahren galt diese Aufgabe ohne Skripting als unlösbar. Inzwischen gibt es in Selectors Level 3 aber die neue Pseudoklasse :checked, die nur greift, wenn die Checkbox markiert wurde. Hierbei ist egal, ob dies durch direkte Interaktion oder durch ein Skript geschieht.
Tipp: Fragen Sie sich, worin der Unterschied zwischen :checked und dem Attributselektor [checked] besteht? Letzterer wird nicht durch Benutzerinteraktionen aktualisiert, weil diese das HTML-Attribut nicht verändern.
Wenn wir diese Pseudoklasse direkt auf eine Checkbox anwenden, hilft das noch nicht viel, weil es – wie bereits gesagt – nicht sehr viele Eigenschaften gibt, mit denen wir das Element versehen können. Allerdings ist es jederzeit möglich, basierend auf dem Zustand der Checkbox Kombinatoren (z. B. für Geschwisterelemente) für das Styling anderer Elemente zu benutzen.
Jetzt fragen Sie sich vermutlich, welche Elemente das sein sollen. Da gibt es ein Element, das im Zusammenhang mit Checkboxen ein spezielles Verhalten hat: <label>-Elemente. Ein <label>-Element, das mit einer Checkbox verbunden ist, dient gleichzeitig als Schalter.
Wenn wir die Checkbox im <label>-Element verschachteln, müssten wir keine IDs benutzen. Allerdings könnten wir das Label dann nicht mehr anhand des Status der Checkbox ansprechen, weil wir noch keine Elternselektoren haben.
Da Labels im Gegensatz zu Checkboxen keine ersetzten Elemente sind, können wir erzeugte Inhalte einsetzen und basierend auf dem Zustand der Checkbox mit Stilen versehen. Danach können wir die eigentliche Checkbox so verstecken, dass sie immer noch über die Tabulatortaste erreichbar ist, während unser erzeugter Inhalt sich wie eine mit Stilen versehene Checkbox verhält.
(Aus der CSS 2.1-Spezifikation; »[Ein ersetztes Element ist] ein Element, dessen Inhalt sich außerhalb des Geltungsbereichs des CSS-Formatierungsmodells befindet, wie ein Bild, ein eingebettetes Dokument oder ein Applet.« Für ersetzte Inhalte kann kein erzeugter Inhalt verwendet werden, auch wenn manche Browser dies gestatten.)
Das wollen wir jetzt einmal in Aktion sehen. Wir beginnen mit dem folgenden einfachen Markup:
<input type="checkbox" id="awesome" /> <label for="awesome">Awesome!</label>
Im nächsten Schritt erzeugen wir ein Pseudoelement, das uns als Checkbox
mit Stildefinitionen dienen soll:
input[type="checkbox"] + label::before { content: 'a0'; /* nicht umbrechendes Leerzeichen */ display: inline-block; vertical-align: .2em; width: .8em; height: .8em; margin-right: .2em; border-radius: .2em; background: silver; text-indent: .15em; line-height: .65; }
In Abbildung 6.9 sehen Sie, wie unsere Checkbox und das Label im Moment aussehen. Die Original-Checkbox ist immer noch sichtbar, aber wir werden sie später verstecken. Zunächst brauchen wir einen anderen Stil für die angekreuzte Checkbox. Hierfür reicht ein Farbwechsel oder das Hinzufügen eines “Abgehakt”-Zeichens mit der Eigenschaft content:
input[type="checkbox"]:checked + label::before { content: '2713'; background: yellowgreen; }
Hier benutzen wir nur einfache Stildefinitionen für unsere Checkboxen. Tatsächlich sind die Möglichkeiten aber endlos. Im Extremfall könnten Sie das Styling per CSS auch komplett weglassen und für alle Checkbox-Zustände Bilder verwenden.
Wie in Abbildung 6.10 zu sehen ist, funktioniert das bereits als Checkbox mit sehr einfachen Stilen. Jetzt müssen wir die Original-Checkbox so verstecken, dass sie trotzdem zugänglich bleibt. Das heißt, wir können nicht einfach mit display: none arbeiten, da sie sonst aus der Tabulator-Reihenfolge entfernt würde. Stattdessen können wir etwas wie das hier benutzen:
input[type="checkbox"] { position: absolute; clip: rect(0,0,0,0); }
Achtung: Seien Sie vorsichtig, wenn Sie derart “großzügige” Selektoren verwenden. input[type=”checkbox”] versteckt auch Checkboxen, auf die kein Label folgt (z. B. solche, die in einem <label>-Element verschachtelt sind), was sie unbrauchbar macht.
Das war’s schon. Wir haben tatsächlich unsere eigene Checkbox erstellt! Wir könnten das Konzept natürlich noch verfeinern, z. B. indem wir den Stil ändern, sobald das Element den Fokus erhält oder deaktiviert ist, wie in Abbildung 6.11 zu sehen ist:
input[type="checkbox"]:focus + label::before { box-shadow: 0 0 .1em .1em #58a; } input[type="checkbox"]:disabled + label::before { background: gray; box-shadow: none; color: #555; }
Sie könnten diese Effekte jetzt noch weiter erweitern, indem Sie Übergänge oder Animationen einbauen, oder Sie gehen aufs Ganze und bauen tatsächliche Schalter nach. Die Möglichkeiten sind wirklich endlos!
PLAY: play.csssecrets.io/checkboxes
Auch wenn es schier unendliche Möglichkeiten gibt, sollten Sie vermeiden, die Checkboxen als Kreise darzustellen: Die meisten Nutzer verbinden mit runden Bedienelementen eher Radiobuttons. Das gleiche gilt für quadratische Radiobuttons.
Hut ab vor Ryan Seddon, der die erste Version dieses Effekts veröffentlichte, die mittlerweile als “Der Checkbox Hack” bekannt ist. Ryan benutzt diese Idee inzwischen, um eine Vielzahl von Widgets zu implementieren, bei denen persistente Zustände gebraucht werden, z. B. modale Dialoge, Drop-down-Menüs, Tabs und Carousels. Allerdings führt ein solcher Missbrauch von Checkboxen schnell auch wieder zu Problemen mit der Zugänglichkeit.
Ein/Aus-Buttons
Mit einer Variation des “Checkbox Hacks” könnten Sie Ein-/Aus-Buttons emulieren, da HTML keine native Möglichkeit bietet, sie zu erzeugen. Ein-/Aus-Buttons funktionieren im Prinzip wie Checkboxen: Sie werden benutzt, um eine Einstellung ein- oder auszuschalten. Wenn sie “eingeschaltet” sind, sehen sie gedrückt aus, sind sie “aus”, sind sie erhaben. Semantisch gesehen gibt es zwischen Ein-/Aus-Buttons und Checkboxen keinen Unterschied. Sie können diesen Trick also benutzen, ohne dass die semantische Reinheit dadurch beeinträchtigt wird.
Um mit diesem Trick Ein-/Aus-Buttons zu erzeugen, müssen Sie die Labels einfach als Buttons gestalten, anstatt Pseudoelemente zu verwenden. Mit dem folgenden Code könnten wir beispielsweise die Ein-/Aus-Buttons in Abbildung 6.12 erstellen:
input[type="checkbox"] { position: absolute; clip: rect(0,0,0,0); } input[type="checkbox"] + label { display: inline-block; padding: .3em .5em; background: #ccc; background-image: linear-gradient(#ddd, #bbb); border: 1px solid rgba(0,0,0,.2); border-radius: .3em; box-shadow: 0 1px white inset; text-align: center; text-shadow: 0 1px 1px white; } input[type="checkbox"]:checked + label, input[type="checkbox"]:active + label { box-shadow: .05em .1em .2em rgba(0,0,0,.6) inset; border-color: rgba(0,0,0,.3); background: #bbb; }
Allerdings sollten Sie bei der Verwendung von Ein-/Aus-Buttons vorsichtig
sein. In den meisten Fällen behindern sie die Zugänglichkeit, weil sie
leicht mit einfachen Buttons verwechselt werden können, die beim Drü-
cken eine bestimmte Aktion auslösen.
- PLAY! play.csssecrets.io/toggle-buttons
- Spezifikationen: Selectors
Im nächsten Auszug aus „CSS Secrets“ beschäftigen wir uns mit Spaltenbreiten in Tabellen. Mehr zu Lea Verous Buch findet Ihr auch hier im oreillyblog.