Heutzutage ist es durchaus möglich, benutzerdefinierte Checkboxen, Radiobuttons und Toggle-Switches zu erstellen und dabei semantisch und barrierefrei zu bleiben. Wir brauchen nicht einmal eine einzige Zeile JavaScript oder zusätzliche HTML-Elemente! Es ist tatsächlich in letzter Zeit einfacher geworden als früher. Schauen wir es uns an.
Hier werden wir landen
Die Dinge sind wirklich einfacher geworden als früher!
Der Grund dafür ist, dass wir endlich die Pseudo-Elemente ::before und ::after des <input>-Tags selbst gestalten können. Das bedeutet, wir können ein <input> behalten und gestalten, ohne zusätzliche Elemente zu benötigen. Früher mussten wir uns auf zusätzliche <div>s oder <span>s verlassen, um ein benutzerdefiniertes Design zu realisieren.
Schauen wir uns das HTML an
Hier gibt es nichts Besonderes. Mit diesem HTML können wir unsere Eingaben gestalten
<!-- Checkbox -->
<input type="checkbox">
<!-- Radio -->
<input type="radio">
<!-- Switch -->
<input type="checkbox" class="switch">
Das war's für den HTML-Teil, aber natürlich wird empfohlen, `name`- und `id`-Attribute sowie ein passendes <label>-Element zu haben.
<!-- Checkbox -->
<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>
<!-- Radio -->
<input type="radio" name="r1" id="r1">
<label for="r1">Radio</label>
<!-- Switch -->
<input type="checkbox" class="switch" name="s1" id="s1">
<label for="s1">Switch</label>
Zur Gestaltung
Zunächst prüfen wir die Unterstützung für appearance: none;, einschließlich der mit Präfixen versehenen Varianten. Die Eigenschaft appearance ist entscheidend, da sie dazu dient, das Standard-Styling eines Browsers von einem Element zu entfernen. Wenn die Eigenschaft nicht unterstützt wird, werden die Stile nicht angewendet und die Standard-Input-Stile angezeigt. Das ist völlig in Ordnung und ein gutes Beispiel für progressive Enhancement.
@supports(-webkit-appearance: none) or (-moz-appearance: none) {
input[type='checkbox'],
input[type='radio'] {
-webkit-appearance: none;
-moz-appearance: none;
}
}
Derzeit ist `appearance` ein Arbeitsentwurf, aber hier ist die Unterstützung:
Diese Browser-Supportdaten stammen von Caniuse, wo es detailliertere Informationen gibt. Eine Zahl gibt an, dass der Browser die Funktion ab dieser Version unterstützt.
Desktop
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 83* | 80 | Nein | 83* | 15.4 |
Mobil / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 127 | 127 | 127 | 15.4 |
Wie bei Links müssen wir verschiedene interaktive Zustände bei Formularelementen berücksichtigen. Wir werden diese bei der Gestaltung unserer Elemente berücksichtigen.
:checked:hover:focus:disabled
Zum Beispiel, wie wir unseren Toggle-Input gestalten, den "Knopf" erstellen und den Zustand :checked berücksichtigen können.
/* The toggle container */
.switch {
width: 38px;
border-radius: 11px;
}
/* The toggle knob */
.switch::after {
left: 2px;
top: 2px;
border-radius: 50%;
width: 15px;
height: 15px;
background: var(--ab, var(--border));
transform: translateX(var(--x, 0));
}
/* Change color and position when checked */
.switch:checked {
--ab: var(--active-inner);
--x: 17px;
}
/* Drop the opacity of the toggle knob when the input is disabled */
.switch:disabled:not(:checked)::after {
opacity: .6;
}
Wir verwenden das <input>-Element als Container. Der Knopf im Input wird mit dem Pseudo-Element ::after erstellt. Wiederum kein Bedarf an zusätzlichem Markup!
Wenn Sie sich die Stile in der Demo genauer ansehen, werden Sie feststellen, dass wir einige CSS-Custom-Properties definieren, da dies zu einer guten Methode geworden ist, wiederverwendbare Werte in einem Stylesheet zu verwalten.
@supports(-webkit-appearance: none) or (-moz-appearance: none) {
input[type='checkbox'],
input[type='radio'] {
--active: #275EFE;
--active-inner: #fff;
--focus: 2px rgba(39, 94, 254, .25);
--border: #BBC1E1;
--border-hover: #275EFE;
--background: #fff;
--disabled: #F6F8FF;
--disabled-inner: #E1E6F9;
}
}
Aber es gibt noch einen weiteren Grund, warum wir Custom Properties verwenden – sie eignen sich gut zum Aktualisieren von Werten basierend auf dem Zustand des Elements! Wir werden hier nicht ins Detail gehen, aber hier ist ein Beispiel, wie wir Custom Properties für verschiedene Zustände verwenden können.
/* Default */
input[type='checkbox'],
input[type='radio'] {
--active: #275EFE;
--border: #BBC1E1;
border: 1px solid var(--bc, var(--border));
}
/* Override defaults */
input[type='checkbox']:checked,
input[type='radio']:checked {
--b: var(--active);
--bc: var(--active);
}
/* Apply another border color on hover if not checked & not disabled */
input[type='checkbox']:not(:checked):not(:disabled):hover,
input[type='radio']:not(:checked):not(:disabled):hover {
--bc: var(--border-hover);
}
Aus Gründen der Barrierefreiheit sollten wir einen benutzerdefinierten Fokusstil hinzufügen. Wir entfernen die Standard-Outline, da sie nicht so abgerundet werden kann wie die anderen Elemente, die wir gestalten. Ein `border-radius` zusammen mit einem `box-shadow` kann jedoch einen abgerundeten Stil ergeben, der wie eine Outline funktioniert.
input[type='checkbox'],
input[type='radio'] {
--focus: 2px rgba(39, 94, 254, .25);
outline: none;
transition: box-shadow .2s;
}
input[type='checkbox']:focus,
input[type='radio']:focus {
box-shadow: 0 0 0 var(--focus);
}
Es ist auch möglich, das <label>-Element, das dem <input>-Element im HTML direkt folgt, auszurichten und zu gestalten.
<input type="checkbox" name="c1" id="c1">
<label for="c1">Checkbox</label>
input[type='checkbox'] + label,
input[type='radio'] + label {
display: inline-block;
vertical-align: top;
/* Additional styling */
}
input[type='checkbox']:disabled + label,
input[type='radio']:disabled + label {
cursor: not-allowed;
}
Hier ist die Demo noch einmal.
Hoffentlich sehen Sie, wie schön es ist, heutzutage benutzerdefinierte Formularstile zu erstellen. Es erfordert weniger Markup, dank Pseudo-Elementen, die direkt auf Formulareingaben angewendet werden können. Es erfordert weniger ausgefeilte Stilwechsel, dank Custom Properties. Und es hat eine ziemlich gute Browserunterstützung, dank @supports.
Alles in allem ist das eine viel angenehmere Entwicklererfahrung, als wir sie früher hatten!
Ich stelle fest, dass du dich vom :-) ferngehalten hast.
Das ist immer noch nicht einfach (zumindest nicht auf eine schöne Art und Weise).
Hallo, könntest du vielleicht einen Verweis auf deine Aussage geben?
„Der Grund dafür ist, dass wir endlich die ::before- und ::after-Pseudo-Elemente des selbst stylen können.“
Ich habe nichts anderes als einen Stack Overflow-Feed dazu gefunden…
Ja, das habe ich mich auch gefragt. Wann haben Browser das zu unterstützen begonnen und wie können wir die Unterstützung für Pseudo-Elemente bei Eingaben prüfen?
Browser unterstützen dies seit mehr als 2 Jahren. So habe ich Native Elements erstellt.
https://native-elements.stackblitz.io
Leider gibt es zu diesem Thema nur sehr wenige Informationen...
Es sollte unterstützt werden, wenn
appearance: none;ebenfalls unterstützt wird, damit wir ein Boxmodell haben.Es ist großartig, dass wir die Unterstützung mit
@supportsprüfen können.Ja, mehr Informationen dazu wären sehr nett!
Es scheint nur bei bestimmten Eingaben wie Radio und Checkbox zu funktionieren, aber nicht bei anderen wie Text und Button.
Schöne Demo, Aaron. Für Toggle-Switches würde ich sie persönlich jedoch immer noch so angehen, wie in inclusive components oder Mozilla docs beschrieben.
Ich fürchte, ich kann deinen Toggle-Switch nicht passieren, da er nicht ziehbar ist. Aber ich liebe, wie einfach das geworden ist, kein zusätzliches Markup, keine seltsamen Style-Hacks.
Ein Input in ein Container-Element zu verwandeln, indem ein End-Tag (``) hinzugefügt wird, ist kein gültiges HTML. Der Browser akzeptiert es möglicherweise aus irgendeinem Grund, aber Ihr Dokument wird nicht validiert.
Ich habe auf den Tag gewartet, an dem ich Pseudo-Elemente in Nicht-Container-Elemente wie Bilder oder Eingaben einfügen kann, aber es scheint, wir müssen noch etwas länger warten, es sei denn, wir sind bereit, ungültiges Markup zu schreiben. Ich jedenfalls bin es nicht...
OK, aber das sehe ich in diesem Artikel nicht, siehst du das?
Es gibt ungeschlossene Eingaben (völlig gültig), wie
Aber keine "Container"-Eingaben, die, da stimme ich zu, ungültig sind, was so wäre...
Meine Entschuldigung! Ich habe mir den generierten Code im Inspektor angesehen, und dort hatte der Input ein End-Tag. Anscheinend macht der Browser das, wenn jemand etwas in ein Nicht-Container-Element einfügt.
Lektion gelernt: Verlasse dich niemals zu sehr auf den Code-Inspektor...
Hey! Ich habe deinen Code in React-Komponenten geforkt. Schau es dir an!
https://codesandbox.io/s/custom-css-only-form-inputs-react-hofx5
Funktioniert nicht in Microsoft Edge 44 – ich weiß nicht warum. Bootstraps benutzerdefinierte Checkboxen und Radios funktionieren in Edge 44: https://getbootstrap.com/docs/4.4/components/forms/#checkboxes-and-radios-1
Andere haben auch dieses Problem in Edge: https://stackoverflow.com/questions/53657488/how-do-i-make-this-checkbox-styling-work-in-edge
Es gibt ein Problem, wenn man kleinere Steuerelemente wie in Bootstrap haben möchte. Wenn ich die Höhe der Steuerelemente von 21px auf 16px reduziere, beginnen auf Displays mit aktivierter Windows-Skalierung Artefakte des Radiobuttons sichtbar zu werden. Ich habe das Problem hier beschrieben: https://stackoverflow.com/questions/61760484/unable-to-center-an-element-when-windows-scaling-is-enabled-125
Das Problem kann man auch hier beobachten: https://codepen.io/iwis/pen/eYpjeYr – das ist der Originalcode mit einem hinzugefügten Button. Hier ist es jedoch kein Problem, da das Steuerelement größer (21px) ist und der Radiobutton somit nicht so stark verzerrt wird.
Was kann man gegen dieses Problem bei kleineren Steuerelementen tun?
Hallo Aaron,
Vielen Dank, dass du diesen hilfreichen Beitrag geteilt hast. Ich habe den CodePen geforkt, um zu lernen, wie er funktioniert. Unterwegs habe ich festgestellt, dass es hilfreich war, einige Änderungen vorzunehmen, und ich dachte, ich würde sie teilen. Ich freue mich über Feedback. Ich habe auch eine Frage.
Der Inhalt für Checkbox und Radio ist ein leerer String. Ist das Häkchen der Standardinhalt für `:after` bei einem Input vom Typ="checkbox"? Ebenso ist eine Box Standard für einen Radio-Input? Wenn ja, kannst du mich bitte auf die Referenz verweisen, wo dies dokumentiert ist, damit ich nachlesen kann, wie das funktioniert?
Hier ist mein CodePen, falls er jemandem hilft: https://codepen.io/greatgraphicdesign/pen/MWaLrWp
Die Änderungen, die ich vorgenommen habe...
– Benutzerdefinierte Eigenschaften entfernt, da sie es mir erschwerten, dem hier zu folgen.
– CSS-Reset-Code vor das `@support` verschoben, da ich es dort verwenden würde.
– `.focus` und `tabIndex` für Barrierefreiheit hinzugefügt. (Outlines erscheinen nur beim Fokus, soweit ich weiß.)
– ul/li-Tags zur Klarheit entfernt. Auf diese Weise habe ich die Notwendigkeit von `vertical-align: middle` entdeckt.
– Labels um die Elemente gewickelt. Das ist einfach meine Präferenz für diese Art von Eingaben. Die Art, wie du es hast, ermöglicht es, den Cursor zu gestalten, was cool ist. Du benötigst trotzdem ein Skript, um das `disabled`-Attribut hinzuzufügen/zu entfernen. Ich verwende eine `.point`-Klasse auf den Labels des aktivierten Elements, die ebenfalls mit einem Skript hinzugefügt/entfernt werden müsste.
Danke nochmals für diesen Beitrag!