Ein grundlegender Überblick über die Erstellung einer barrierefreien Split-Button-Komponente.
In diesem Beitrag möchte ich auf Ihre Gedanken zum Erstellen einer geteilten Schaltfläche eingehen . Demo ansehen.
<ph type="x-smartling-placeholder">Falls Sie Videos bevorzugen, finden Sie hier eine YouTube-Version dieses Beitrags:
Übersicht
Geteilte Schaltflächen sind Schaltflächen die eine primäre Schaltfläche und eine Liste zusätzlicher Schaltflächen verbergen. Sie sind nützlich zum Anzeigen einer gemeinsamen Aktion beim Verschachteln sekundärer, weniger häufig verwendeter Aktionen Maßnahmen ergreifen, bis sie benötigt werden. Eine geteilte Schaltfläche kann entscheidend sein, sich minimal anfühlen. Eine erweiterte Schaltfläche „Aufteilen“ kann sogar die letzte Nutzeraktion speichern. und es in die Hauptposition bringen.
Eine gängige Aufteilungsschaltfläche finden Sie in Ihrer E-Mail-Anwendung. Die primäre Aktion Sie können die Nachricht aber auch später senden oder als Entwurf speichern:
Der gemeinsame Aktionsbereich ist eine tolle Sache, da die Nutzenden sich nicht umsehen müssen. Sie Wichtige E-Mail-Aktionen sind in der Aufteilungsschaltfläche enthalten.
Teile
Sehen wir uns die wesentlichen Elemente einer geteilten Schaltfläche genauer an, bevor wir die gesamte Orchestrierung und die endgültige User Experience. Bedienungshilfen von VisBug Prüftool wird hier verwendet, um eine Makroansicht der Komponente zu zeigen, des HTML-Codes, des Stils und der Zugänglichkeit für jeden Hauptteil.
Container für die geteilte Schaltfläche der obersten Ebene
Die Komponente auf der höchsten Ebene ist eine Inline-Flexbox mit der Klasse
gui-split-button
mit der primären Aktion
und die .gui-popup-button
.
Die primäre Aktionsschaltfläche
Das anfänglich sichtbare und fokussierbare <button>
passt in den Container.
zwei übereinstimmende Eckenformen für
Focus [Fokus],
Mauszeiger darüberbewegen und
aktiven Interaktionen
erscheinen in .gui-split-button
.
Die Ein/Aus-Schaltfläche für das Pop-up
Die Pop-up-Schaltfläche unterstützende Elemente sind zur Aktivierung und Anspielung auf die Liste der
sekundäre Schaltflächen. Es ist kein <button>
und es ist nicht fokussierbar. Sie können jedoch
Das ist der Positionierungsanker für .gui-popup
und der Host für :focus-within
.
um das Pop-up zu präsentieren.
Die Pop-up-Karte
Dies ist eine dem Anker untergeordnete Floating-Karte
.gui-popup-button
, positionierte absolute und
die Schaltflächenliste semantisch
verpackt.
Sekundäre Aktionen
Eine fokussierbare <button>
mit einer etwas kleineren Schriftgröße als die primäre
Aktionsschaltfläche enthält ein Symbol und eine ergänzende
auf die primäre Schaltfläche hinzu.
Benutzerdefinierte Eigenschaften
Die folgenden Variablen tragen dazu bei, eine harmonische Farbe zu erzeugen und eine zentrale Stelle zu schaffen, in der Komponente verwendete Werte ändern.
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --light (prefers-color-scheme: light);
.gui-split-button {
--theme: hsl(220 75% 50%);
--theme-hover: hsl(220 75% 45%);
--theme-active: hsl(220 75% 40%);
--theme-text: hsl(220 75% 25%);
--theme-border: hsl(220 50% 75%);
--ontheme: hsl(220 90% 98%);
--popupbg: hsl(220 0% 100%);
--border: 1px solid var(--theme-border);
--radius: 6px;
--in-speed: 50ms;
--out-speed: 300ms;
@media (--dark) {
--theme: hsl(220 50% 60%);
--theme-hover: hsl(220 50% 65%);
--theme-active: hsl(220 75% 70%);
--theme-text: hsl(220 10% 85%);
--theme-border: hsl(220 20% 70%);
--ontheme: hsl(220 90% 5%);
--popupbg: hsl(220 10% 30%);
}
}
Layouts und Farbe
Markup
Das Element beginnt als <div>
mit einem benutzerdefinierten Klassennamen.
<div class="gui-split-button"></div>
Füge die primäre Schaltfläche und die .gui-popup-button
-Elemente hinzu.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions"></span>
</div>
Beachten Sie die ARIA-Attribute aria-haspopup
und aria-expanded
. Diese Hinweise sind
ist wichtig, damit Screenreader
die Möglichkeiten und den Status der Aufteilung
Schaltflächen. Das Attribut title
ist für alle Nutzer hilfreich.
Fügen Sie ein <svg>
-Symbol und das Containerelement .gui-popup
hinzu.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup"></ul>
</span>
</div>
Für eine einfache Pop-up-Platzierung ist .gui-popup
der Schaltfläche untergeordnet, die
erweitert es. Der einzige Haken bei dieser Strategie ist der .gui-split-button
Der Container kann overflow: hidden
nicht verwenden, da dadurch das Pop-up-Fenster reduziert wird.
visuell dargestellt wird.
Ein <ul>
mit <li><button>
Inhalten meldet sich als „Schaltfläche“
Liste“ Screenreadern, d. h. genau die dort dargestellte Benutzeroberfläche.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li>
<button>Schedule for later</button>
</li>
<li>
<button>Delete</button>
</li>
<li>
<button>Save draft</button>
</li>
</ul>
</span>
</div>
Für das gewisse Etwas und um Farbe zu haben, habe ich den sekundären Schaltflächen Symbole hinzugefügt, https://heroicons.com an. Symbole sind für beide optional die primäre und die sekundäre Schaltfläche.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
Schedule for later
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Delete
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
</svg>
Save draft
</button></li>
</ul>
</span>
</div>
Stile
Sobald HTML und Inhalte vorhanden sind, können mithilfe von Stilen Farben und Layout bereitgestellt werden.
Container für die Schaltfläche „Aufteilen“ gestalten
Der Displaytyp inline-flex
eignet sich gut für diese Wrapping-Komponente,
sollte zusammen mit anderen geteilten Schaltflächen, Aktionen oder Elementen passen.
.gui-split-button {
display: inline-flex;
border-radius: var(--radius);
background: var(--theme);
color: var(--ontheme);
fill: var(--ontheme);
touch-action: manipulation;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
Der <button>
-Stil
Schaltflächen sind sehr gut dafür geeignet, zu verschleiern, wie viel Code erforderlich ist. Möglicherweise müssen Sie Browser-Standardstile rückgängig machen oder ersetzen. Sie müssen aber auch Vererbung, das Hinzufügen von Interaktionszuständen und die Anpassung an verschiedene Eingabetypen. Schaltflächenstile lassen sich schnell addieren.
Diese Schaltflächen unterscheiden sich von normalen Schaltflächen, da sie einen gemeinsamen Hintergrund haben. mit einem übergeordneten Element. Normalerweise besitzt eine Schaltfläche den Hintergrund und die Textfarbe. Sie teilen sie jedoch und wenden nur ihren eigenen Hintergrund auf die Interaktion an.
.gui-split-button button {
cursor: pointer;
appearance: none;
background: none;
border: none;
display: inline-flex;
align-items: center;
gap: 1ch;
white-space: nowrap;
font-family: inherit;
font-size: inherit;
font-weight: 500;
padding-block: 1.25ch;
padding-inline: 2.5ch;
color: var(--ontheme);
outline-color: var(--theme);
outline-offset: -5px;
}
Interaktionsstatus mit wenigen CSS hinzufügen Pseudoklassen und Abgleich verwenden benutzerdefinierte Eigenschaften für den Bundesstaat:
.gui-split-button button {
…
&:is(:hover, :focus-visible) {
background: var(--theme-hover);
color: var(--ontheme);
& > svg {
stroke: currentColor;
fill: none;
}
}
&:active {
background: var(--theme-active);
}
}
Die Hauptschaltfläche benötigt einige spezielle Stile, um den Designeffekt zu vervollständigen:
.gui-split-button > button {
border-end-start-radius: var(--radius);
border-start-start-radius: var(--radius);
& > svg {
fill: none;
stroke: var(--ontheme);
}
}
Die Schaltfläche und das Symbol für das helle Design werden ebenfalls shadow auf:
.gui-split-button {
@media (--light) {
& > button,
& button:is(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--theme-active);
}
& > .gui-popup-button > svg,
& button:is(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--theme-active));
}
}
}
Eine tolle Schaltfläche hat auf die Mikrointeraktionen und die winzigen Details geachtet.
Hinweis zu :focus-visible
Beachten Sie, dass für die Schaltflächenstile :focus-visible
anstelle von :focus
verwendet wird. :focus
ist ein entscheidender Faktor für die Entwicklung einer barrierefreien Benutzeroberfläche.
Es ist nicht intelligent, ob die Nutzenden es sehen oder
gilt für jeden Fokus.
Im folgenden Video wird versucht, diese Mikrointeraktion aufzuschlüsseln, um zu zeigen,
:focus-visible
ist eine intelligente Alternative.
Pop-up-Schaltfläche gestalten
Eine 4ch
-Flexbox zum Zentrieren eines Symbols und Verankern einer Pop-up-Schaltflächenliste. Gefällt mir
die primäre Schaltfläche ist, bleibt sie transparent, bis sie den Mauszeiger auf eine andere Schaltfläche bewegt oder darauf interagiert wird.
und gestreckt bis zum Füllen.
.gui-popup-button {
inline-size: 4ch;
cursor: pointer;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
border-inline-start: var(--border);
border-start-end-radius: var(--radius);
border-end-end-radius: var(--radius);
}
Ebene im Hover-, fokussierten und aktiven Status mit CSS
Verschachtelung und die
Funktionsauswahl für :is()
:
.gui-popup-button {
…
&:is(:hover,:focus-within) {
background: var(--theme-hover);
}
/* fixes iOS trying to be helpful */
&:focus {
outline: none;
}
&:active {
background: var(--theme-active);
}
}
Diese Stile sind der primäre Aufhänger zum Ein- und Ausblenden des Pop-ups. Wenn der Parameter
.gui-popup-button
hat focus
für eines seiner untergeordneten Elemente, legt opacity
, Position fest
und pointer-events
für das Symbol und das Pop-up.
.gui-popup-button {
…
&:focus-within {
& > svg {
transition-duration: var(--in-speed);
transform: rotateZ(.5turn);
}
& > .gui-popup {
transition-duration: var(--in-speed);
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
}
}
Nach Abschluss der Innen- und Außenstile ist der letzte Teil die bedingte Übergang transformiert je nach den Bewegungseinstellungen des Nutzers:
.gui-popup-button {
…
@media (--motionOK) {
& > svg {
transition: transform var(--out-speed) ease;
}
& > .gui-popup {
transform: translateY(5px);
transition:
opacity var(--out-speed) ease,
transform var(--out-speed) ease;
}
}
}
Ein genauer Blick auf den Code würde feststellen, dass die Opazität für Nutzer noch umgestellt wird. die eingeschränkte Bewegung bevorzugen.
Pop-up gestalten
Das .gui-popup
-Element ist eine Schaltflächenliste einer unverankerten Karte mit benutzerdefinierten Eigenschaften.
und relative Einheiten werden subtil kleiner angezeigt und interaktiv mit den primären
Schaltfläche und auf die Marke durch ihre Verwendung von Farbe. Beachten Sie, dass die Symbole
weniger Kontrast haben,
sind dünner und der Schatten hat einen Hauch von Markenblau. Wie bei Schaltflächen
Eine gute Benutzeroberfläche und UX ist das Ergebnis dieser kleinen Details.
.gui-popup {
--shadow: 220 70% 15%;
--shadow-strength: 1%;
opacity: 0;
pointer-events: none;
position: absolute;
bottom: 80%;
left: -1.5ch;
list-style-type: none;
background: var(--popupbg);
color: var(--theme-text);
padding-inline: 0;
padding-block: .5ch;
border-radius: var(--radius);
overflow: hidden;
display: flex;
flex-direction: column;
font-size: .9em;
transition: opacity var(--out-speed) ease;
box-shadow:
0 -2px 5px 0 hsl(var(--shadow) / calc(var(--shadow-strength) + 5%)),
0 1px 1px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 10%)),
0 2px 2px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 12%)),
0 5px 5px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 13%)),
0 9px 9px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 14%)),
0 16px 16px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 20%))
;
}
Die Symbole und Schaltflächen werden mit Markenfarben versehen, um in jedem Dunkeln ansprechend zu wirken. Karte mit hellem Design:
.gui-popup {
…
& svg {
fill: var(--popupbg);
stroke: var(--theme);
@media (prefers-color-scheme: dark) {
stroke: var(--theme-border);
}
}
& button {
color: var(--theme-text);
width: 100%;
}
}
Das Pop-up-Fenster „Dunkles Design“ enthält Text- und Symbolschatten und einen etwas mehr Intensiver Box-Schatten:
.gui-popup {
…
@media (--dark) {
--shadow-strength: 5%;
--shadow: 220 3% 2%;
& button:not(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--ontheme);
}
& button:not(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--ontheme));
}
}
}
Allgemeine <svg>
-Symbolstile
Alle Symbole haben eine relativ große Größe an der Schaltfläche font-size
, in der sie von
Dabei wird die Einheit ch
als
inline-size
Jeder hat auch Stile, mit denen die Symbole
flüssiger machen.
.gui-split-button svg {
inline-size: 2ch;
box-sizing: content-box;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
Linksläufiges Layout
Logische Eigenschaften übernehmen die komplexe Arbeit.
Hier ist die Liste der verwendeten logischen Eigenschaften:
– display: inline-flex
erstellt ein flexibles Inline-Element.
– padding-block
und padding-inline
als Paar, statt padding
die Vorteile der Auffüllung der logischen Seiten.
– border-end-start-radius
und
Freunde tun Folgendes:
die Ecken je nach Dokumentrichtung abgerundet werden.
- Mit inline-size
statt width
wird sichergestellt, dass die Größe nicht an die physischen Maße gebunden ist.
– border-inline-start
fügt am Anfang einen Rahmen hinzu. Dieser kann sich je nach Skriptrichtung auf der rechten oder linken Seite befinden.
JavaScript
Fast alle der folgenden JavaScript-Codes sollen die Barrierefreiheit verbessern. Zwei meiner Hilfsbibliotheken verwendet werden, um die Aufgaben zu erleichtern. BlingBlingJS ist eine Kurzfassung. DOM-Abfragen und einfache Einrichtung des Event-Listeners roving-ux erleichtert Tastatur- und Gamepad-Interaktionen für das Pop-up.
import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'
const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')
Wenn die obigen Bibliotheken importiert und die Elemente ausgewählt und in folgendem Ordner gespeichert wurden: Variablen. Ein Upgrade der Websitevariante ist nur ein paar Funktionen davon entfernt, vollständig zu sein.
Roving Index
Wenn eine Tastatur oder ein Screenreader .gui-popup-button
fokussiert, möchten wir
Fokus auf die erste (oder zuletzt fokussierte) Schaltfläche im
.gui-popup
. Die Bibliothek hilft uns dabei, dies mithilfe von element
und target
zu tun.
Parameter.
popupButtons.forEach(element =>
rovingIndex({
element,
target: 'button',
}))
Das Element übergibt jetzt den Fokus an die untergeordneten <button>
-Ziele und aktiviert
Standardnavigation mit Pfeiltasten, um Optionen zu durchsuchen.
aria-expanded
wird umgeschaltet
Obwohl es visuell erkennbar ist, dass ein Pop-up angezeigt und ausgeblendet wird, benötigt ein Screenreader mehr als visuelle Hinweise. Hier wird JavaScript verwendet, um die CSS-gesteuerte :focus-within
-Interaktion zu ergänzen, indem ein für den Screenreader geeignetes Attribut umgeschaltet wird.
popupButtons.on('focusin', e => {
e.currentTarget.setAttribute('aria-expanded', true)
})
popupButtons.on('focusout', e => {
e.currentTarget.setAttribute('aria-expanded', false)
})
Escape
-Taste wird aktiviert
Der Fokus des Nutzers wurde absichtlich in eine Falle gelegt, wir müssen ihn also
eine Möglichkeit zum Verlassen. Die gängigste Methode ist die Verwendung des Schlüssels Escape
.
Achten Sie dazu auf Tastendruck auf der Pop-up-Schaltfläche, da alle Tastaturereignisse
untergeordneten Elementen zu diesem übergeordneten Element.
popupButtons.on('keyup', e => {
if (e.code === 'Escape')
e.target.blur()
})
Wenn Escape
-Tasten auf die Pop-up-Schaltfläche drücken, wird der Fokus von sich selbst entfernt
mit
blur()
Klicks auf die Schaltfläche „Aufteilen“
Wenn Nutzende auf die Schaltflächen klicken, tippen oder die Tastatur mit den Schaltflächen interagieren,
die entsprechende Aktion ausführen muss. Ereignis-Bubbling wird verwendet
wieder hier, aber diesmal im Container .gui-split-button
, um die Schaltfläche zu fangen
Klicks von einem untergeordneten Pop-up oder der primären Aktion.
splitButtons.on('click', event => {
if (event.target.nodeName !== 'BUTTON') return
console.info(event.target.innerText)
})
Fazit
Jetzt, wo du weißt, wie ich es gemacht habe, wie würdest du... ‽ 🙂
Lassen Sie uns unsere Herangehensweisen diversifizieren und alle Möglichkeiten kennenlernen, wie wir das Web entwickeln können. Erstelle eine Demo, twittere mir Links und ich füge sie hinzu im Abschnitt „Community-Remixe“ weiter unten.