Eine grundlegende Übersicht darüber, wie Sie farbadaptive, responsive und barrierefreie <button>-Komponenten erstellen.
In diesem Beitrag möchte ich meine Gedanken dazu teilen, wie man ein farbadaptives, responsives und barrierefreies <button>-Element erstellt.
Demo ausprobieren und Quellcode ansehen
Wenn Sie lieber ein Video ansehen möchten, finden Sie hier eine YouTube-Version dieses Beitrags:
Übersicht
Das Element <button> ist für die Nutzerinteraktion konzipiert. Das click-Ereignis wird durch Tastatur-, Maus-, Touch- und Sprachaktionen sowie andere Aktionen ausgelöst. Dabei werden intelligente Regeln für das Timing verwendet. Außerdem sind in jedem Browser einige Standardstile enthalten, die Sie direkt ohne Anpassung verwenden können. Mit color-scheme können Sie auch die vom Browser bereitgestellten hellen und dunklen Schaltflächen aktivieren.
Es gibt auch verschiedene Arten von Schaltflächen, die jeweils im vorherigen Codepen-Einbettungscode zu sehen sind. Ein <button> ohne Typ wird an die Einbettung in ein <form> angepasst und ändert sich in den Typ „Senden“.
<!-- buttons -->
<button></button>
<button type="submit"></button>
<button type="button"></button>
<button type="reset"></button>
<!-- button state -->
<button disabled></button>
<!-- input buttons -->
<input type="button" />
<input type="file">
Bei der GUI Challenge in diesem Monat erhält jede Schaltfläche Formatierungen, um ihre Funktion visuell zu verdeutlichen. Schaltflächen zum Zurücksetzen haben Warnfarben, da sie destruktiv sind. Schaltflächen zum Senden haben blauen Akzenttext, damit sie etwas prominenter als normale Schaltflächen erscheinen.
Schaltflächen haben auch Pseudoklassen, die für das Styling mit CSS verwendet werden können. Diese Klassen bieten CSS-Hooks zum Anpassen des Erscheinungsbilds der Schaltfläche: :hover für den Fall, dass sich der Mauszeiger über der Schaltfläche befindet, :active für den Fall, dass die Maus oder Tastatur gedrückt wird, und :focus oder :focus-visible zur Unterstützung des Stylings von Hilfstechnologien.
button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Markieren & Zeichnen
Zusätzlich zu den Schaltflächentypen, die in der HTML-Spezifikation definiert sind, habe ich eine Schaltfläche mit einem Symbol und eine Schaltfläche mit der benutzerdefinierten Klasse btn-custom hinzugefügt.
<button>Default</button>
<input type="button" value="<input>"/>
<button>
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<path d="..." />
</svg>
Icon
</button>
<button type="submit">Submit</button>
<button type="button">Type Button</button>
<button type="reset">Reset</button>
<button disabled>Disabled</button>
<button class="btn-custom">Custom</button>
<input type="file">
Für Tests wird jede Schaltfläche in ein Formular eingefügt. So kann ich dafür sorgen, dass die Formatierungen für die Standardschaltfläche, die sich wie eine Schaltfläche zum Senden verhält, entsprechend aktualisiert werden. Außerdem wechsle ich die Symbolstrategie von Inline-SVG zu einem maskierten SVG, damit beide gleich gut funktionieren.
<form>
<button>Default</button>
<input type="button" value="<input>"/>
<button>Icon <span data-icon="cloud"></span></button>
<button type="submit">Submit</button>
<button type="button">Type Button</button>
<button type="reset">Reset</button>
<button disabled>Disabled</button>
<button class="btn-custom btn-large" type="button">Large Custom</button>
<input type="file">
</form>
Die Matrix der Kombinationen ist an diesem Punkt ziemlich überwältigend. Zwischen Schaltflächentypen, Pseudoklassen und der Position innerhalb oder außerhalb eines Formulars gibt es über 20 Kombinationen von Schaltflächen. Zum Glück können wir mit CSS jede dieser Eigenschaften klar definieren.
Bedienungshilfen
Schaltflächenelemente sind von Natur aus barrierefrei, aber es gibt einige gängige Verbesserungen.
Hover und Fokus zusammen
Ich möchte :hover und :focus mit dem funktionalen Pseudoselektor :is() gruppieren. So wird sichergestellt, dass meine Benutzeroberflächen immer Tastatur- und Hilfstechnologiestile berücksichtigen.
button:is(:hover, :focus) {
…
}
Interaktiver Fokusring
Ich animiere den Fokusring gern für Nutzer, die eine Tastatur oder Hilfstechnologien verwenden. Dazu animiere ich den Umriss um 5 Pixel vom Button weg, aber nur, wenn der Button nicht aktiv ist. Dadurch wird ein Effekt erzeugt, bei dem sich der Fokusring beim Drücken auf die Schaltflächengröße verkleinert.
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Farbkontrast sicherstellen
Es gibt mindestens vier verschiedene Farbkombinationen für helle und dunkle Designs, bei denen der Farbkontrast berücksichtigt werden muss: Schaltfläche, Schaltfläche zum Senden, Schaltfläche zum Zurücksetzen und deaktivierte Schaltfläche. VisBug wird hier verwendet, um alle Werte gleichzeitig zu prüfen und anzuzeigen:
Symbole für Nutzer ausblenden, die sie nicht sehen können
Beim Erstellen einer Schaltfläche mit Symbol sollte das Symbol den Schaltflächentext visuell unterstützen. Das bedeutet auch, dass das Symbol für Menschen mit Sehverlust nicht hilfreich ist. Glücklicherweise bietet der Browser eine Möglichkeit, Elemente vor Screenreadern zu verbergen, damit Menschen mit Sehbehinderung nicht durch dekorative Schaltflächenbilder gestört werden:
<button>
<svg … aria-hidden="true">...</svg>
Icon Button
</button>
Stile
Im nächsten Abschnitt richte ich zuerst ein benutzerdefiniertes Eigenschaftensystem zum Verwalten der adaptiven Stile der Schaltfläche ein. Mit diesen benutzerdefinierten Eigenschaften kann ich Elemente auswählen und ihre Darstellung anpassen.
Eine adaptive benutzerdefinierte Eigenschaftsstrategie
Die in dieser GUI-Challenge verwendete Strategie für benutzerdefinierte Eigenschaften ähnelt der Strategie, die beim Erstellen eines Farbschemas verwendet wird. Für ein adaptives helles und dunkles Farbsystem wird für jedes Design eine benutzerdefinierte Eigenschaft definiert und entsprechend benannt. Anschließend wird eine einzelne benutzerdefinierte Eigenschaft verwendet, um den aktuellen Wert des Designs zu speichern, und einer CSS-Eigenschaft zugewiesen. Später kann die einzelne benutzerdefinierte Eigenschaft auf einen anderen Wert aktualisiert werden, wodurch sich auch der Schaltflächenstil ändert.
button {
--_bg-light: white;
--_bg-dark: black;
--_bg: var(--_bg-light);
background-color: var(--_bg);
}
@media (prefers-color-scheme: dark) {
button {
--_bg: var(--_bg-dark);
}
}
Mir gefällt, dass die hellen und dunklen Designs deklarativ und übersichtlich sind. Die Indirektion und Abstraktion werden in das benutzerdefinierte Attribut --_bg ausgelagert, das jetzt das einzige „reaktive“ Attribut ist. --_bg-light und --_bg-dark sind statisch. Außerdem ist deutlich zu erkennen, dass das helle Design das Standarddesign ist und das dunkle Design nur bedingt angewendet wird.
Einheitliches Design vorbereiten
Die gemeinsame Auswahl
Mit dem folgenden Selektor werden alle verschiedenen Arten von Schaltflächen angesprochen. Er ist anfangs etwas unübersichtlich. :where() wird verwendet, sodass für die Anpassung der Schaltfläche keine Spezifität erforderlich ist. Schaltflächen werden häufig für alternative Szenarien angepasst und die :where()-Auswahl sorgt für eine einfache Ausführung der Aufgabe. In :where() ist jeder Schaltflächentyp ausgewählt, einschließlich ::file-selector-button, das nicht in :is() oder :where() verwendet werden kann.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
…
}
Alle benutzerdefinierten Eigenschaften werden in diesem Selektor festgelegt. Zeit, alle benutzerdefinierten Properties zu überprüfen. Für diese Schaltfläche werden einige benutzerdefinierte Eigenschaften verwendet. Ich beschreibe jede Gruppe im Laufe des Abschnitts und zeige am Ende die Kontexte für dunkle und reduzierte Bewegungen.
Akzentfarbe der Schaltfläche
Schaltflächen und Symbole sind ein guter Ort für Farbakzente:
--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);
Schriftfarbe für Schaltfläche
Die Textfarben der Schaltflächen sind nicht weiß oder schwarz, sondern abgedunkelte oder aufgehellte Versionen von --_accent, die mit hsl() erstellt wurden und dem Farbton 210 entsprechen:
--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);
Hintergrundfarbe der Schaltfläche
Für Schaltflächenhintergründe gilt dasselbe hsl()-Muster, mit Ausnahme der Schaltflächen im hellen Design. Diese sind auf Weiß eingestellt, sodass ihre Oberfläche dem Nutzer nahe oder vor anderen Oberflächen erscheint:
--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);
Schaltflächenhintergrund
Mit dieser Hintergrundfarbe wird eine Oberfläche hinter anderen Oberflächen dargestellt. Das ist nützlich für den Hintergrund der Dateieingabe:
--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);
Abstand der Schaltfläche
Der Abstand um den Text auf dem Button wird mit der Einheit ch festgelegt, einer relativen Länge zur Schriftgröße. Das ist besonders wichtig, wenn durch große Schaltflächen die font-size und die Schaltflächenskalierung proportional erhöht werden können:
--_padding-inline: 1.75ch;
--_padding-block: .75ch;
Schaltflächenrahmen
Der Grenzradius des Buttons wird in einer benutzerdefinierten Eigenschaft gespeichert, damit die Dateieingabe mit den anderen Buttons übereinstimmt. Die Rahmenfarben folgen dem etablierten adaptiven Farbsystem:
--_border-radius: .5ch;
--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);
Hervorhebungseffekt beim Hovern über Schaltflächen
Mit diesen Eigenschaften wird eine Größeneigenschaft für den Übergang bei Interaktion festgelegt und die Markierungsfarbe folgt dem adaptiven Farbsystem. Wie diese interagieren, wird später in diesem Beitrag behandelt. Letztendlich werden sie für einen box-shadow-Effekt verwendet:
--_highlight-size: 0;
--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);
Schaltflächentextschatten
Jede Schaltfläche hat einen subtilen Textschatten. So wird der Text über der Schaltfläche platziert, was die Lesbarkeit verbessert und der Präsentation einen schönen Schliff verleiht.
--_ink-shadow-light: 0 1px 0 var(--_border-light);
--_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%);
--_ink-shadow: var(--_ink-shadow-light);
Buttersymbol
Die Symbole haben die Größe von zwei Zeichen, was wieder auf die relative Längeneinheit ch zurückzuführen ist. Dadurch wird das Symbol proportional zum Schaltflächentext skaliert. Die Farbe des Symbols basiert auf --_accent-color, um eine adaptive und zum Design passende Farbe zu erhalten.
--_icon-size: 2ch;
--_icon-color: var(--_accent);
Schaltflächenschatten
Damit sich Schatten richtig an hell und dunkel anpassen, müssen sowohl ihre Farbe als auch ihre Deckkraft geändert werden. Schatten im hellen Design sollten dezent sein und in der Farbe der Oberfläche, die sie überlagern, getönt sein. Schatten im dunklen Design müssen dunkler und gesättigter sein, damit sie dunklere Oberflächenfarben überlagern können.
--_shadow-color-light: 220 3% 15%;
--_shadow-color-dark: 220 40% 2%;
--_shadow-color: var(--_shadow-color-light);
--_shadow-strength-light: 1%;
--_shadow-strength-dark: 25%;
--_shadow-strength: var(--_shadow-strength-light);
Mit adaptiven Farben und Stärken kann ich zwei Schattenstärken erstellen:
--_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%));
--_shadow-2:
0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)),
0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%));
Außerdem wird den Schaltflächen durch 1pxbox-shadow
ein leichter 3D-Effekt verliehen:
--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);
Schaltflächenübergänge
Entsprechend dem Muster für adaptive Farben erstelle ich zwei statische Eigenschaften, um die Optionen des Designsystems zu speichern:
--_transition-motion-reduce: ;
--_transition-motion-ok:
box-shadow 145ms ease,
outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);
Alle Attribute zusammen in der Auswahl
:where( button, input[type="button"], input[type="submit"], input[type="reset"], input[type="file"] ), :where(input[type="file"])::file-selector-button { --_accent-light: hsl(210 100% 40%); --_accent-dark: hsl(210 50% 70%); --_accent: var(--_accent-light);--_text-light: hsl(210 10% 30%); --_text-dark: hsl(210 5% 95%); --_text: var(--_text-light);
--_bg-light: hsl(0 0% 100%); --_bg-dark: hsl(210 9% 31%); --_bg: var(--_bg-light);
--_input-well-light: hsl(210 16% 87%); --_input-well-dark: hsl(204 10% 10%); --_input-well: var(--_input-well-light);
--_padding-inline: 1.75ch; --_padding-block: .75ch;
--_border-radius: .5ch; --_border-light: hsl(210 14% 89%); --_border-dark: var(--_bg-dark); --_border: var(--_border-light);
--_highlight-size: 0; --_highlight-light: hsl(210 10% 71% / 25%); --_highlight-dark: hsl(210 10% 5% / 25%); --_highlight: var(--_highlight-light);
--_ink-shadow-light: 0 1px 0 hsl(210 14% 89%); --_ink-shadow-dark: 0 1px 0 hsl(210 11% 15%); --_ink-shadow: var(--_ink-shadow-light);
--_icon-size: 2ch; --_icon-color-light: var(--_accent-light); --_icon-color-dark: var(--_accent-dark); --_icon-color: var(--accent, var(--_icon-color-light));
--_shadow-color-light: 220 3% 15%; --_shadow-color-dark: 220 40% 2%; --_shadow-color: var(--_shadow-color-light); --_shadow-strength-light: 1%; --_shadow-strength-dark: 25%; --_shadow-strength: var(--_shadow-strength-light); --_shadow-1: 0 1px 2px -1px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 9%)); --_shadow-2: 0 3px 5px -2px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 3%)), 0 7px 14px -5px hsl(var(--_shadow-color)/calc(var(--_shadow-strength) + 5%)) ;
--_shadow-depth-light: hsl(210 14% 89%); --_shadow-depth-dark: var(--_bg-dark); --_shadow-depth: var(--_shadow-depth-light);
--_transition-motion-reduce: ; --_transition-motion-ok: box-shadow 145ms ease, outline-offset 145ms ease ; --_transition: var(--_transition-motion-reduce); }

Anpassungen für das dunkle Design
Der Wert des Musters für die statischen Attribute -light und -dark wird deutlich, wenn die Attribute für das dunkle Design festgelegt sind:
@media (prefers-color-scheme: dark) {
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
--_bg: var(--_bg-dark);
--_text: var(--_text-dark);
--_border: var(--_border-dark);
--_accent: var(--_accent-dark);
--_highlight: var(--_highlight-dark);
--_input-well: var(--_input-well-dark);
--_ink-shadow: var(--_ink-shadow-dark);
--_shadow-depth: var(--_shadow-depth-dark);
--_shadow-color: var(--_shadow-color-dark);
--_shadow-strength: var(--_shadow-strength-dark);
}
}
Das ist nicht nur gut lesbar, sondern Nutzer dieser benutzerdefinierten Schaltflächen können die einfachen Attribute auch mit der Gewissheit verwenden, dass sie sich entsprechend an die Nutzerpräferenzen anpassen.
Anpassungen für weniger Bewegung
Wenn die Bewegung für diesen Besucher in Ordnung ist, weisen Sie --_transition var(--_transition-motion-ok) zu:
@media (prefers-reduced-motion: no-preference) {
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
--_transition: var(--_transition-motion-ok);
}
}
Einige freigegebene Stile
Die Schriftarten für Schaltflächen und Eingaben müssen auf inherit festgelegt sein, damit sie mit den anderen Schriftarten auf der Seite übereinstimmen. Andernfalls werden sie vom Browser formatiert. Das gilt auch für letter-spacing. Wenn Sie line-height auf 1.5 festlegen, wird die Größe des Letterbox-Bereichs so angepasst, dass über und unter dem Text etwas Platz ist:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
/* …CSS variables */
font: inherit;
letter-spacing: inherit;
line-height: 1.5;
border-radius: var(--_border-radius);
}

Schaltflächen gestalten
Selektor anpassen
Der Selektor input[type="file"] ist nicht der Button-Teil der Eingabe, sondern das Pseudoelement ::file-selector-button. Daher habe ich input[type="file"] aus der Liste entfernt:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"],
input[type="file"]
),
:where(input[type="file"])::file-selector-button {
}
Cursor- und Toucheinstellungen
Zuerst weise ich dem Cursor den Stil pointer zu, damit Mausnutzer erkennen, dass die Schaltfläche interaktiv ist. Dann füge ich touch-action: manipulation hinzu, damit bei Klicks nicht auf einen potenziellen Doppelklick gewartet werden muss. Dadurch reagieren die Schaltflächen schneller:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
cursor: pointer;
touch-action: manipulation;
}
Farben und Rahmen
Als Nächstes passe ich die Schriftgröße, den Hintergrund, den Text und die Rahmenfarben an. Dazu verwende ich einige der adaptiven benutzerdefinierten Eigenschaften, die ich zuvor festgelegt habe:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
font-size: var(--_size, 1rem);
font-weight: 700;
background: var(--_bg);
color: var(--_text);
border: 2px solid var(--_border);
}

Schatten
Bei den Schaltflächen wurden einige großartige Techniken angewendet. Das text-shadow passt sich an helle und dunkle Hintergründe an und sorgt für ein angenehmes, dezentes Erscheinungsbild des Schaltflächentexts. Für das box-shadow werden drei Schatten zugewiesen. Der erste, --_shadow-2, ist ein normaler Schlagschatten.
Der zweite Schatten ist ein optischer Trick, der die Schaltfläche leicht nach oben gewölbt erscheinen lässt. Der letzte Schatten ist für die Hover-Hervorhebung vorgesehen. Er hat anfangs die Größe 0, wird aber später vergrößert und animiert, sodass er vom Button aus zu wachsen scheint.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
box-shadow:
var(--_shadow-2),
var(--_shadow-depth),
0 0 0 var(--_highlight-size) var(--_highlight)
;
text-shadow: var(--_ink-shadow);
}

Layout
Ich habe dem Button ein Flexbox-Layout zugewiesen, genauer gesagt ein inline-flex-Layout, das sich an den Inhalt anpasst. Dann zentriere ich den Text und richte die untergeordneten Elemente vertikal und horizontal mittig aus. So werden Symbole und andere Schaltflächenelemente richtig ausgerichtet.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
display: inline-flex;
justify-content: center;
align-items: center;
text-align: center;
}

Abstand
Für den Abstand zwischen den Schaltflächen habe ich gap verwendet, damit sich nebeneinanderliegende Elemente nicht berühren, und logische Eigenschaften für den Innenabstand, damit der Abstand zwischen den Schaltflächen für alle Textlayouts funktioniert.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
gap: 1ch;
padding-block: var(--_padding-block);
padding-inline: var(--_padding-inline);
}

Touch- und Maus-UX
Der nächste Abschnitt richtet sich hauptsächlich an Nutzer, die Mobilgeräte mit Touchscreen verwenden. Die erste Eigenschaft, user-select, gilt für alle Nutzer. Sie verhindert, dass der Schaltflächentext hervorgehoben wird. Das ist vor allem auf Touch-Geräten zu sehen, wenn eine Schaltfläche gedrückt wird und das Betriebssystem den Text der Schaltfläche hervorhebt.
Im Allgemeinen ist das nicht die Nutzererfahrung mit Schaltflächen in integrierten Apps. Daher deaktiviere ich sie, indem ich user-select auf „none“ setze. Das Tippen auf Markierungsfarben (-webkit-tap-highlight-color) und Betriebssystem-Kontextmenüs (-webkit-touch-callout) sind weitere sehr webzentrierte Schaltflächenfunktionen, die nicht den allgemeinen Erwartungen der Nutzer an Schaltflächen entsprechen. Daher entferne ich sie ebenfalls.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
Übergänge
Die adaptive Variable --_transition wird der Eigenschaft transition zugewiesen:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
…
transition: var(--_transition);
}
Wenn der Nutzer den Mauszeiger auf die Schaltfläche bewegt, ohne sie aktiv zu drücken, passen Sie die Größe des Schatten-Highlights an, damit es einen schönen Fokus-Effekt gibt, der aus der Schaltfläche heraus zu wachsen scheint:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
):where(:not(:active):hover) {
--_highlight-size: .5rem;
}
Wenn der Fokus auf der Schaltfläche liegt, wird der Fokusumriss von der Schaltfläche weg verschoben. Dadurch entsteht der Eindruck, dass der Fokusumriss aus der Schaltfläche heraus wächst:
:where(button, input):where(:not(:active)):focus-visible {
outline-offset: 5px;
}
Symbole
Für die Verarbeitung von Symbolen hat der Selektor einen zusätzlichen :where()-Selektor für direkte SVG-Untergeordnete Elemente oder Elemente mit dem benutzerdefinierten Attribut data-icon. Die Symbolgröße wird mit der benutzerdefinierten Eigenschaft mithilfe von Inline- und Block-Logikeigenschaften festgelegt. Die Strichfarbe wird festgelegt und es wird ein drop-shadow für die Anpassung an text-shadow verwendet. flex-shrink ist auf 0 gesetzt, damit das Symbol nie gequetscht wird. Schließlich wähle ich Liniensymbole aus und weise ihnen hier mit fill: none und round Linienenden und Linienverbindungen zu:
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
) > :where(svg, [data-icon]) {
block-size: var(--_icon-size);
inline-size: var(--_icon-size);
stroke: var(--_icon-color);
filter: drop-shadow(var(--_ink-shadow));
flex-shrink: 0;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}

Schaltflächen zum Senden anpassen
Ich wollte, dass die Schaltflächen zum Senden etwas hervorgehoben werden. Das habe ich erreicht, indem ich die Textfarbe der Schaltflächen auf die Akzentfarbe gesetzt habe:
:where(
[type="submit"],
form button:not([type],[disabled])
) {
--_text: var(--_accent);
}

Schaltflächen zum Zurücksetzen anpassen
Ich wollte, dass die Schaltflächen zum Zurücksetzen einige integrierte Warnzeichen haben, um Nutzer auf ihr potenziell destruktives Verhalten aufmerksam zu machen. Außerdem habe ich für die Schaltfläche für das helle Design mehr rote Akzente als für das dunkle Design verwendet. Die Anpassung erfolgt durch Ändern der entsprechenden hellen oder dunklen zugrunde liegenden Farbe. Die Schaltfläche aktualisiert den Stil:
:where([type="reset"]) {
--_border-light: hsl(0 100% 83%);
--_highlight-light: hsl(0 100% 89% / 20%);
--_text-light: hsl(0 80% 50%);
--_text-dark: hsl(0 100% 89%);
}
Außerdem fände ich es gut, wenn die Farbe der Fokusumrisslinie mit dem Akzent von Rot übereinstimmt. Die Textfarbe wird von Dunkelrot zu Hellrot angepasst. Ich passe die Farbe der Kontur mit dem Keyword currentColor an:
:where([type="reset"]):focus-visible {
outline-color: currentColor;
}

Deaktivierte Schaltflächen anpassen
Es kommt häufig vor, dass deaktivierte Schaltflächen einen schlechten Farbkontrast aufweisen, weil versucht wird, sie weniger aktiv erscheinen zu lassen. Ich habe jedes Farbset getestet und darauf geachtet, dass es bestanden wird. Dazu habe ich den HSL-Helligkeitswert angepasst, bis der Wert in den DevTools oder VisBug bestanden wurde.
:where(
button,
input[type="button"],
input[type="submit"],
input[type="reset"]
)[disabled] {
--_bg: none;
--_text-light: hsl(210 7% 40%);
--_text-dark: hsl(210 11% 71%);
cursor: not-allowed;
box-shadow: var(--_shadow-1);
}

Schaltflächen für die Dateieingabe anpassen
Die Schaltfläche für die Dateieingabe ist ein Container für ein <span>-Element und eine Schaltfläche. Mit CSS kann der Eingabecontainer und die verschachtelte Schaltfläche, aber nicht der <span>-Tag formatiert werden. Der Container erhält max-inline-size, damit er nicht größer wird als nötig. inline-size: 100% kann dagegen verkleinert werden, um in kleinere Container zu passen. Die Hintergrundfarbe ist auf eine adaptive Farbe festgelegt, die dunkler als andere Oberflächen ist, sodass sie hinter dem Dateiauswahlbutton angezeigt wird.
:where(input[type="file"]) {
inline-size: 100%;
max-inline-size: max-content;
background-color: var(--_input-well);
}
Die Schaltflächen für die Dateiauswahl und den Eingabetyp haben die Klasse appearance: none, um alle vom Browser bereitgestellten Formatierungen zu entfernen, die nicht von den anderen Schaltflächenformatierungen überschrieben wurden.
:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
appearance: none;
}
Schließlich wird dem inline-end des Buttons ein Rand hinzugefügt, um den Spantext vom Button wegzuschieben und etwas Platz zu schaffen.
:where(input[type="file"])::file-selector-button {
margin-inline-end: var(--_padding-inline);
}

Sonderfälle für das dunkle Design
Die primären Aktionsschaltflächen haben einen dunkleren Hintergrund, damit der Text einen höheren Kontrast aufweist und die Schaltflächen etwas auffälliger wirken.
@media (prefers-color-scheme: dark) {
:where(
[type="submit"],
[type="reset"],
[disabled],
form button:not([type="button"])
) {
--_bg: var(--_input-well);
}
}

Varianten erstellen
Aus Spaß und weil es praktisch ist, habe ich mich entschieden, zu zeigen, wie man einige Varianten erstellt. Eine Variante ist sehr lebendig, ähnlich wie primäre Schaltflächen oft aussehen. Eine andere Variante ist groß. Die letzte Variante hat ein Symbol mit Farbverlauf.
Leuchtende Schaltfläche
Um diesen Schaltflächenstil zu erreichen, habe ich die Basiseigenschaften direkt mit blauen Farben überschrieben. Das war zwar schnell und einfach, aber dadurch werden die adaptiven Attribute entfernt und die Bilder sehen in hellen und dunklen Designs gleich aus.
.btn-custom {
--_bg: linear-gradient(hsl(228 94% 67%), hsl(228 81% 59%));
--_border: hsl(228 89% 63%);
--_text: hsl(228 89% 100%);
--_ink-shadow: 0 1px 0 hsl(228 57% 50%);
--_highlight: hsl(228 94% 67% / 20%);
}

Große Schaltfläche
Diese Art von Schaltfläche wird durch Ändern der benutzerdefinierten Property --_size erreicht.
Abstand und andere Leerraumelemente werden relativ zu dieser Größe skaliert.
.btn-large {
--_size: 1.5rem;
}

Symbolschaltfläche
Dieser Symbol-Effekt hat nichts mit unseren Schaltflächenstilen zu tun. Er zeigt jedoch, wie er mit nur wenigen CSS-Properties erreicht werden kann und wie gut die Schaltfläche mit Symbolen umgeht, die keine Inline-SVGs sind.
[data-icon="cloud"] {
--icon-cloud: url("https://api.iconify.design/mdi:apple-icloud.svg") center / contain no-repeat;
-webkit-mask: var(--icon-cloud);
mask: var(--icon-cloud);
background: linear-gradient(to bottom, var(--_accent-dark), var(--_accent-light));
}
![]()
Fazit
Jetzt wissen Sie, wie ich es gemacht habe. Wie würden Sie vorgehen? 🙂
Wir möchten unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, die das Web bietet.
Erstelle eine Demo, schick mir einen Tweet mit den Links und ich füge sie unten im Bereich „Community-Remixe“ hinzu.
Community-Remixe
Hier gibt es noch nichts zu sehen.
Ressourcen
- Quellcode auf GitHub