Podstawowe omówienie tworzenia łatwo dostępnego komponentu podziału.
W tym poście chcę pokazać, jak opracować przycisk podziału . Wypróbuj wersję demonstracyjną.
.Jeśli wolisz film, oto wersja tego posta w YouTube:
Omówienie
Przyciski podzielone to przyciski. ukrywające przycisk główny i listę dodatkowych przycisków. Są przydatne do ujawniania wspólnego działania przy zagnieżdżaniu drugorzędnych, rzadziej używanych i wykonywać odpowiednie czynności. Przycisk podziału może mieć kluczowe znaczenie przy projektowaniu w niewielkim stopniu. Zaawansowany przycisk podziału może nawet zapamiętać ostatnie działanie użytkownika. i awansuje ją na główną pozycję.
Wspólny przycisk podziału można znaleźć w aplikacji do obsługi poczty e-mail. Działanie główne możesz wysłać później lub zapisać wersję roboczą:
Wspólny obszar działania jest atrakcyjny, ponieważ użytkownik nie musi się rozglądać. Ta pamiętaj, że kluczowe działania związane z e-mailami znajdują się na przycisku podziału.
Części
Omówmy najważniejsze elementy podzielonego przycisku, zanim omówimy ich funkcje. nad ogólną administracją i wrażeniami użytkownika. Ułatwienia dostępu w aplikacji VisBug które pozwala wyświetlić widok makro komponentu, aspekty HTML, stylu i ułatwień dostępu.
Kontener przycisku podziału najwyższego poziomu
Komponent najwyższego poziomu to wbudowany Flexbox o klasie
gui-split-button
zawierający działanie główne
oraz .gui-popup-button
.
Główny przycisk polecenia
Początkowo widoczny i możliwy do zaznaczenia element <button>
mieści się w kontenerze z
dwa pasujące kształty narożników dla
fokus,
najedź i
aktywne interakcje w
wyświetlane w folderze .gui-split-button
.
Przycisk przełączania wyskakującego okienka
Przycisk wyskakującego okienka Element pomocniczy służy do aktywowania i kojarzenia z listą
dodatkowe przyciski. Zwróć uwagę, że to nie jest obiekt <button>
i nie można go zaznaczyć. Pamiętaj jednak:
jest to kotwica do pozycjonowania dla .gui-popup
, a host dla :focus-within
używany
aby wyświetlić wyskakujące okienko.
Wyskakująca karta
To jest element podrzędny karty pływającej do jej reklamy zakotwiczonej
.gui-popup-button
, pozycja bezwzględna i
semantycznie zawijać listę przycisków.
Działania dodatkowe
Element <button>
z możliwością zaznaczenia, trochę mniejszy rozmiar czcionki niż podstawowa
przycisk polecenia, który zawiera ikonę i dodatkowy
do przycisku głównego.
Właściwości niestandardowe
Poniższe zmienne pomagają w harmonijności kolorów i stanowią centralny punkt modyfikować wartości używane w całym komponencie.
@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%);
}
}
Układy i kolory
Markup
Element zaczyna się od elementu <div>
i ma niestandardową nazwę klasy.
<div class="gui-split-button"></div>
Dodaj przycisk główny i elementy .gui-popup-button
.
<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>
Zwróć uwagę na atrybuty aria aria-haspopup
i aria-expanded
. Te wskazówki to
są kluczowe dla czytników ekranu, ponieważ muszą znać możliwości i stan podziału
za pomocą przycisku. Atrybut title
jest przydatny dla wszystkich.
Dodaj ikonę <svg>
i element kontenera .gui-popup
.
<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>
Aby umieścić proste wyskakujące okienko, .gui-popup
jest elementem podrzędnym wobec przycisku, który
powoduje jego rozwinięcie. Jedynym ograniczeniem w przypadku tej strategii jest .gui-split-button
.
kontener nie może użyć polecenia overflow: hidden
, ponieważ spowoduje to obcięcie wyskakującego okienka
obecny wizualnie.
Element <ul>
zawierający treści <li><button>
zostanie ogłoszony jako „przycisk”
lista” przez czytniki ekranu, czyli właśnie za pomocą interfejsu.
<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>
Do przycisków dodatkowych dodaliśmy ikony, aby zwiększyć wystrój i cieszyć się kolorami ze strony https://heroicons.com. Ikony są opcjonalne w obu przypadkach oraz przycisk główny i dodatkowy.
<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>
Style
Po umieszczeniu kodu HTML i treści style są gotowe do wyboru kolorów i układu.
Styl kontenera przycisku podziału
Typ wyświetlania inline-flex
dobrze sprawdza się w przypadku tego komponentu opakowań, ponieważ
powinny pasować do innych podzielonych przycisków, działań lub elementów.
.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;
}
Styl <button>
Przyciski bardzo dobrze ukrywają ilość kodu. Może być konieczne cofania lub zastępowania stylów domyślnych przeglądarki, ale trzeba też wymusić dziedziczenia, dodawać stany interakcji i dostosowywać do różnych preferencji użytkowników typów danych wejściowych. Style przycisków szybko się sumują.
Różnią się one od zwykłych przycisków, bo mają wspólne tło. z elementem nadrzędnym. Zazwyczaj przycisk ma własny kolor tła i tekstu. Te osoby je jednak udostępniają i stosują swoje własne tło tylko w przypadku interakcji.
.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;
}
Dodawanie stanów interakcji za pomocą kilku stylów CSS pseudoklasy i użycie dopasowywania właściwości niestandardowe dla stanu:
.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);
}
}
Przycisk główny musi mieć kilka specjalnych stylów, aby uzyskać pełny efekt projektowy:
.gui-split-button > button {
border-end-start-radius: var(--radius);
border-start-start-radius: var(--radius);
& > svg {
fill: none;
stroke: var(--ontheme);
}
}
Przycisk i ikona jasnego motywu shadow:
.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));
}
}
}
Świetny przycisk zwraca uwagę na mikrointerakcje i drobne szczegóły.
Uwaga na temat miejsca :focus-visible
Zwróć uwagę, że w stylach przycisków występuje wartość :focus-visible
zamiast :focus
. :focus
ma kluczowe znaczenie dla stworzenia łatwo dostępnego interfejsu, ale ma taki
upadek: nie chodzi o to, czy użytkownik musi to zobaczyć czy nie
ale nie, dotyczy dowolnego zaznaczenia.
Poniższy film pokazuje, w jaki sposób można podzielić tę mikrointerakcję, aby pokazać,
:focus-visible
to inteligentna alternatywa.
Określanie stylu przycisku wyskakującego okienka
Flexbox 4ch
do wyśrodkowania ikony i zakotwiczenia listy przycisków wyskakującej. Polub
przycisk główny, jest przezroczysty, dopóki nie najedzie kursorem na inny przycisk lub nie wykonuje dodatkowej czynności.
i rozciągnięte, by wypełnić.
.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);
}
Nakładaj warstwy na elementy po najechaniu kursorem, zaznaczenie i stan aktywności za pomocą CSS
Zagnieżdżenie oraz
Funkcjonalny selektor :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);
}
}
Te style są głównym elementem przykuwania uwagi do wyświetlania i ukrywania wyskakującego okienka. Gdy
.gui-popup-button
ma wartość focus
na dowolnym ze swoich elementów podrzędnych, ustawiono opacity
, pozycję
i pointer-events
na ikonie i w wyskakującym okienku.
.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;
}
}
}
Ostatnim elementem jest warunkowe określenie stylu przekształcenia w zależności od preferencji ruchu użytkownika:
.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;
}
}
}
Użytkownicy, którzy nie spojrzyli na ten kod, zauważą, że przezroczystość nadal została zmieniona. którzy preferują mniej ruchu.
Określanie stylu wyskakującego okienka
Element .gui-popup
to przycisk karty pływającej, który korzysta z właściwości niestandardowych.
i jednostki względne, aby były nieznacznie mniejsze, interaktywnie dopasowywane do
i markę za pomocą koloru. Ikony mają słabszy kontrast.
są cieńsze, a cień jest ciemną nutą marki. Podobnie jak w przypadku przycisków,
Połączenie tych drobnych detali to dobry interfejs i wygoda użytkowania.
.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%))
;
}
Ikony i przyciski mają odpowiedni kolor marki, aby pasowały do każdego ciemnego koloru i karta o jasnej tematyce:
.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%;
}
}
Wyskakujące okienko ciemnego motywu zawiera cienie tekstu i ikon oraz intensywny cień ramki:
.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));
}
}
}
Ogólne style ikony <svg>
Wszystkie ikony mają stosowny rozmiar do przycisku font-size
, w którym są używane przez
za pomocą jednostki ch
jako
inline-size
. Do każdego typu dodano też style, które ułatwiają zarysowanie ikon
płynne.
.gui-split-button svg {
inline-size: 2ch;
box-sizing: content-box;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
Układ od prawej do lewej
Właściwości logiczne wykonują wszystkie złożone zadania.
Oto lista użytych właściwości logicznych:
– display: inline-flex
tworzy wbudowany element elastyczny.
- padding-block
i padding-inline
jako para, a nie padding
Krótko mówiąc, warto skorzystać z dopełnienia logiki po bokach.
- border-end-start-radius
i
znajomi będą
zaokrąglonych rogów w zależności od kierunku dokumentu.
– Dzięki parametrowi inline-size
zamiast width
rozmiar nie jest powiązany z wymiarami fizycznymi.
– border-inline-start
dodaje obramowanie na początku, które może znajdować się po prawej lub lewej stronie w zależności od kierunku skryptu.
JavaScript
Prawie cały poniższy kod JavaScript ma na celu zwiększenie dostępności. Dwie są też używane biblioteki pomocnicze, aby ułatwić wykonywanie zadań. BlingBlingJS jest używany do zwięzłości, zapytań DOM i łatwej konfiguracji detektora zdarzeń, roving-ux ułatwia ułatwienie dostępu interakcje z klawiaturą i padem do gier w wyskakującym okienku.
import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'
const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')
Po zaimportowaniu powyższych bibliotek oraz wybraniu i zapisaniu elementów zmiennych, uaktualnienie doświadczenia zajmie tylko kilka funkcji.
Indeks Roving
Gdy zaznaczysz element .gui-popup-button
na klawiaturze lub czytniku ekranu, chcemy
przenieść zaznaczenie na pierwszy (lub ostatnio zaznaczony) przycisk na pasku
.gui-popup
Pomaga nam w tym biblioteka za pomocą element
i target
.
popupButtons.forEach(element =>
rovingIndex({
element,
target: 'button',
}))
Element przekazuje teraz zaznaczenie na docelowe elementy podrzędne (<button>
) i umożliwia włączenie
standardowego klawisza strzałki, by przeglądać opcje.
Przełączam: aria-expanded
Choć wyraźnie widać, że wyskakujące okienko wyświetla się i ukrywa, czytnik ekranu potrzebuje więcej niż wskazówek wizualnych. Język JavaScript jest tu używany, aby uzupełnić interakcję :focus-within
opartą na CSS przez przełączenie odpowiedniego atrybutu dla czytnika ekranu.
popupButtons.on('focusin', e => {
e.currentTarget.setAttribute('aria-expanded', true)
})
popupButtons.on('focusout', e => {
e.currentTarget.setAttribute('aria-expanded', false)
})
Włączam klawisz Escape
W centrum uwagi użytkownika znajduje się pułapka, co oznacza, że musimy
muszą umożliwiać wyjście z domu. Najczęstszym sposobem jest zezwolenie na użycie klucza Escape
.
Zwróć uwagę na naciśnięcia klawiszy na wyskakującej stronie, bo wszystkie zdarzenia na klawiaturze
że dzieci pojawią się w dymkach obok tego rodzica.
popupButtons.on('keyup', e => {
if (e.code === 'Escape')
e.target.blur()
})
Jeśli przycisk wyskakującego okienka zobaczy naciśnięcie dowolnego klawisza Escape
, zaznaczenie zostanie usunięte z samego przycisku
z
blur()
Kliknięcia przycisku podziału
Wreszcie, jeśli użytkownik kliknie przycisk albo wejdzie z nim w interakcję z klawiaturą,
aplikacja musi wykonać odpowiednie działanie. Dymek wydarzeń jest używany
znowu tutaj, ale tym razem w kontenerze .gui-split-button
, aby złapać przycisk.
kliknięć z wyskakującego okienka podrzędnego lub działania głównego.
splitButtons.on('click', event => {
if (event.target.nodeName !== 'BUTTON') return
console.info(event.target.innerText)
})
Podsumowanie
Wiesz już, jak to zrobiłem. Jak Ty? 🙂
Stosujmy różne podejścia i poznajmy sposoby budowania obecności w internecie. Utwórz wersję demonstracyjną, a potem dodaj linki do funkcji tweetuj mi. znajdziesz poniżej w sekcji z remiksami na karcie Społeczność.