Tworzenie komponentu przycisku

Podstawowe informacje o tym, jak tworzyć komponenty <button> dostosowujące się do koloru, responsywne i dostępne.

W tym poście chcę podzielić się moimi przemyśleniami na temat tworzenia elementu <button>, który dostosowuje się do koloru, jest responsywny i dostępny. Wypróbuj wersję demonstracyjną i wyświetl kod źródłowy

W jasnym i ciemnym motywie przyciski są obsługiwane za pomocą klawiatury i myszy.

Jeśli wolisz film, obejrzyj tę wersję posta w YouTube:

Przegląd

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Element <button> jest przeznaczony do interakcji z użytkownikiem. click zdarzenia wywoływane przez klawiaturę, mysz, dotyk, głos i inne elementy, z inteligentnymi regułami dotyczącymi czasu; Każda przeglądarka ma też domyślne style, których możesz używać bezpośrednio bez żadnych dostosowań. Użyj color-scheme, aby włączyć też jasne i ciemne przyciski udostępniane przez przeglądarkę.

Istnieją też różne rodzaje przycisków, z których każdy jest widoczny w poprzednim osadzonym elemencie Codepen. Element <button> bez typu dostosuje się do elementu <form> i zmieni typ na przesyłanie.

<!-- 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">

W tym miesiącu w ramach GUI Challenge każdy przycisk otrzyma style, które pomogą wizualnie odróżnić jego przeznaczenie. Przyciski resetowania będą miały kolory ostrzegawcze, ponieważ są destrukcyjne, a przyciski przesyłania będą miały niebieski tekst akcentujący, dzięki czemu będą nieco bardziej wyróżnione niż zwykłe przyciski.

Podgląd ostatecznego zestawu wszystkich typów przycisków wyświetlanych w formularzu i poza nim, z dodatkowymi opcjami dla przycisków z ikonami i przycisków dostosowanych.
Podgląd ostatecznego zestawu wszystkich typów przycisków wyświetlanych w formularzu i poza nim, z dodatkowymi opcjami dla przycisków z ikonami i przycisków dostosowanych

Przyciski mają też pseudoklasy, których CSS może używać do określania stylu. Te klasy zapewniają zaczepy CSS do dostosowywania wyglądu przycisku: :hover, gdy kursor myszy znajduje się nad przyciskiem, :active, gdy mysz lub klawiatura jest wciśnięta, oraz :focus lub :focus-visible, aby ułatwić stylizację technologii wspomagających.

button:hover {}
button:active {}
button:focus {}
button:focus-visible {}
Podgląd ostatecznego zestawu wszystkich typów przycisków w motywie ciemnym.
Podgląd ostatecznego zestawu wszystkich typów przycisków w ciemnym motywie

Znacznik

Oprócz typów przycisków określonych w specyfikacji HTML dodałem przycisk z ikoną i przycisk z klasą niestandardową btn-custom.

<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">

Następnie na potrzeby testowania każdy przycisk jest umieszczany w formularzu. Dzięki temu mogę mieć pewność, że style są odpowiednio aktualizowane w przypadku przycisku domyślnego, który działa jak przycisk przesyłania. Zmieniam też strategię dotyczącą ikon z wstawionego pliku SVG na zamaskowany plik SVG, aby oba rozwiązania działały równie dobrze.

<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>

Macierz kombinacji jest w tym momencie dość przytłaczająca. Istnieje ponad 20 kombinacji przycisków, które różnią się typem, pseudoklasą oraz tym, czy znajdują się w formularzu. Na szczęście usługi porównywania cen pomagają nam jasno określić każdy z tych aspektów.

Ułatwienia dostępu

Elementy typu button są z natury dostępne, ale istnieje kilka typowych ulepszeń.

Najedź kursorem i skup się na tym, co ważne

Chcę zgrupować :hover:focus za pomocą selektora pseudoklasy :is(). Dzięki temu moje interfejsy zawsze uwzględniają style klawiatury i technologii wspomagających.

button:is(:hover, :focus) {
  
}
Wypróbuj wersję demonstracyjną!

Interaktywny pierścień ostrości

Chcę animować pierścień zaznaczenia dla użytkowników klawiatury i technologii wspomagających. Osiągam to, animując kontur przycisku w odległości 5 pikseli od niego, ale tylko wtedy, gdy przycisk nie jest aktywny. Spowoduje to efekt, w którym pierścień fokusu zmniejszy się do rozmiaru przycisku po jego naciśnięciu.

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

Zapewnianie odpowiedniego kontrastu kolorów

W przypadku jasnych i ciemnych motywów należy wziąć pod uwagę co najmniej 4 różne kombinacje kolorów, które wymagają sprawdzenia kontrastu kolorów: przycisk, przycisk przesyłania, przycisk resetowania i wyłączony przycisk. VisBug służy do sprawdzania i wyświetlania wszystkich wyników jednocześnie:

Ukrywanie ikon przed osobami, które nie widzą

Podczas tworzenia przycisku z ikoną ikona powinna wizualnie wspierać tekst przycisku. Oznacza to również, że ikona nie jest przydatna dla osób z wadą wzroku. Na szczęście przeglądarka umożliwia ukrywanie elementów przed czytnikami ekranu, dzięki czemu osoby z wadami wzroku nie są rozpraszane przez dekoracyjne obrazy przycisków:

<button>
  <svg … aria-hidden="true">...</svg>
  Icon Button
</button>
Narzędzia deweloperskie w Chrome pokazujące drzewo ułatwień dostępu dla przycisku. Drzewo ignoruje obraz przycisku, ponieważ atrybut aria-hidden ma wartość „true”.
Narzędzia deweloperskie w Chrome pokazujące drzewo ułatwień dostępu dla przycisku. Drzewo ignoruje obraz przycisku, ponieważ atrybut aria-hidden ma wartość „true”.

Style

W następnej sekcji najpierw utworzę system właściwości niestandardowych do zarządzania stylami adaptacyjnymi przycisku. Dzięki tym właściwościom niestandardowym mogę zacząć wybierać elementy i dostosowywać ich wygląd.

Strategia adaptacyjnych właściwości niestandardowych

Strategia dotycząca właściwości niestandardowych użyta w tym wyzwaniu związanym z GUI jest bardzo podobna do tej, która została zastosowana w tworzeniu schematu kolorów. W przypadku adaptacyjnego systemu kolorów jasnych i ciemnych definiowana jest właściwość niestandardowa dla każdego motywu i odpowiednio nazywana. Następnie pojedyncza właściwość niestandardowa służy do przechowywania bieżącej wartości motywu i jest przypisywana do właściwości CSS. Później pojedynczą właściwość niestandardową można zaktualizować do innej wartości, a następnie zaktualizować styl przycisku.

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);
  }
}

Podoba mi się, że motywy jasny i ciemny są deklaratywne i przejrzyste. Pośredniość i abstrakcja są przenoszone do --_bg właściwości niestandardowej, która jest teraz jedyną właściwością „reaktywną”; --_bg-light--_bg-dark są statyczne. Jest też wyraźnie napisane, że jasny motyw jest domyślny, a ciemny jest stosowany tylko warunkowo.

Przygotowanie do zachowania spójności projektu

Udostępniony selektor

Ten selektor służy do kierowania na wszystkie rodzaje przycisków i na początku może wydawać się nieco przytłaczający. :where() jest używany, więc dostosowanie przycisku nie wymaga określania szczegółów. Przyciski są często dostosowywane do alternatywnych scenariuszy, a selektor :where() ułatwia wykonanie zadania. W elemencie :where() wybrano każdy typ przycisku, w tym ::file-selector-button, którego nie można używać w elementach :is() ani :where().

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  
}

Wszystkie właściwości niestandardowe będą miały zakres ograniczony do tego selektora. Czas sprawdzić wszystkie właściwości niestandardowe. W tym przycisku jest używanych sporo właściwości niestandardowych. Każdą grupę opiszę po kolei, a na końcu sekcji przedstawię konteksty ciemnego motywu i ograniczonego ruchu.

Kolor akcentu przycisku

Przyciski przesyłania i ikony to świetne miejsce na dodanie odrobiny koloru:

--_accent-light: hsl(210 100% 40%);
--_accent-dark: hsl(210 50% 70%);
--_accent: var(--_accent-light);

Kolor tekstu przycisku

Kolory tekstu przycisku nie są białe ani czarne, ale są przyciemnionymi lub rozjaśnionymi wersjami koloru --_accent, w których użyto hsl() i zachowano odcień 210:

--_text-light: hsl(210 10% 30%);
--_text-dark: hsl(210 5% 95%);
--_text: var(--_text-light);

Kolor tła przycisku

Tła przycisków są zgodne z tym samym wzorem hsl(), z wyjątkiem przycisków w jasnym motywie – są one ustawione na biały, dzięki czemu wydają się bliżej użytkownika lub przed innymi powierzchniami:

--_bg-light: hsl(0 0% 100%);
--_bg-dark: hsl(210 9% 31%);
--_bg: var(--_bg-light);

Tło przycisku

Ten kolor tła służy do umieszczania powierzchni za innymi powierzchniami. Jest przydatny w przypadku tła pola wprowadzania pliku:

--_input-well-light: hsl(210 16% 87%);
--_input-well-dark: hsl(204 10% 10%);
--_input-well: var(--_input-well-light);

Dopełnienie przycisku

Odstępy wokół tekstu w przycisku są określane za pomocą jednostki ch, która jest długością względną w stosunku do rozmiaru czcionki. Ma to kluczowe znaczenie, gdy duże przyciski mogą po prostu zwiększać font-size i skalę przycisków proporcjonalnie:

--_padding-inline: 1.75ch;
--_padding-block: .75ch;

Obramowanie przycisku

Promień obramowania przycisku jest przechowywany we właściwości niestandardowej, dzięki czemu dane wejściowe pliku mogą pasować do innych przycisków. Kolory obramowania są zgodne z ustalonym adaptacyjnym systemem kolorów:

--_border-radius: .5ch;

--_border-light: hsl(210 14% 89%);
--_border-dark: var(--_bg-dark);
--_border: var(--_border-light);

Efekt podświetlenia po najechaniu kursorem na przycisk

Te właściwości określają właściwość rozmiaru, która ma być zmieniana podczas interakcji, a kolor wyróżnienia jest zgodny z adaptacyjnym systemem kolorów. W dalszej części tego posta omówimy, jak te elementy ze sobą współdziałają, ale ostatecznie służą one do uzyskania efektu box-shadow:

--_highlight-size: 0;

--_highlight-light: hsl(210 10% 71% / 25%);
--_highlight-dark: hsl(210 10% 5% / 25%);
--_highlight: var(--_highlight-light);

Cień tekstu przycisku

Każdy przycisk ma subtelny styl cienia tekstu. Dzięki temu tekst będzie znajdować się nad przyciskiem, co poprawi czytelność i doda element estetyczny.

--_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);

Ikona przycisku

Ikony mają rozmiar 2 znaków dzięki jednostce długości względnej ch, co pomaga skalować ikonę proporcjonalnie do tekstu przycisku. Kolor ikony jest oparty na kolorze --_accent-color, aby zapewnić adaptacyjny kolor w ramach motywu.

--_icon-size: 2ch;
--_icon-color: var(--_accent);

Cień przycisku

Aby cienie prawidłowo dostosowywały się do trybu jasnego i ciemnego, muszą zmieniać zarówno kolor, jak i przezroczystość. Cienie w jasnym motywie najlepiej wyglądają, gdy są subtelne i mają odcień zbliżony do koloru powierzchni, na której są wyświetlane. Cienie w trybie ciemnym muszą być ciemniejsze i bardziej nasycone, aby można było je nakładać na ciemniejsze kolory powierzchni.

--_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);

Dzięki adaptacyjnym kolorom i intensywnościom mogę uzyskać 2 głębokości cieni:

--_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%));

Dodatkowo, aby przyciski wyglądały na trójwymiarowe, zastosowano 1pxbox-shadow tworzący iluzję:

--_shadow-depth-light: 0 1px var(--_border-light);
--_shadow-depth-dark: 0 1px var(--_bg-dark);
--_shadow-depth: var(--_shadow-depth-light);

Przejścia przycisków

Zgodnie z wzorcem kolorów adaptacyjnych tworzę 2 statyczne właściwości, które będą przechowywać opcje systemu projektowania:

--_transition-motion-reduce: ;
--_transition-motion-ok:
  box-shadow 145ms ease,
  outline-offset 145ms ease
;
--_transition: var(--_transition-motion-reduce);

Wszystkie usługi razem w selektorze

Wszystkie właściwości niestandardowe w selektorze

: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); }

Domyślne przyciski są wyświetlane obok siebie w jasnym i ciemnym motywie.

Dostosowania ciemnego motywu

Wartość wzorca statycznych właściwości -light-dark staje się jasna, gdy ustawione są właściwości ciemnego motywu:

@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);
  }
}

Nie tylko dobrze się to czyta, ale też użytkownicy tych niestandardowych przycisków mogą bez obaw korzystać z właściwości podstawowych, ponieważ dostosują się one do preferencji użytkownika.

Dostosowania dotyczące ograniczenia ruchu

Jeśli użytkownik nie ma nic przeciwko ruchowi, przypisz --_transition do var(--_transition-motion-ok):

@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);
  }
}

Kilka stylów udostępnionych

Czcionki przycisków i pól wejściowych muszą być ustawione na inherit, aby pasowały do czcionek na pozostałych stronach. W przeciwnym razie będą stylizowane przez przeglądarkę. Dotyczy to również letter-spacing. Ustawienie line-height na 1.5 powoduje ustawienie rozmiaru pola na litery, aby zapewnić tekstowi trochę miejsca nad i pod nim:

: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);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Style przycisków

Regulacja selektora

Selektor input[type="file"] nie jest częścią przycisku w polu wejściowym, ale pseudoelement ::file-selector-button już tak. Dlatego usunąłem input[type="file"] z listy:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"],
  input[type="file"]
),
:where(input[type="file"])::file-selector-button {
  
}

Dostosowywanie kursora i dotyku

Najpierw nadaję kursorowi styl pointer, który informuje użytkowników myszy, że przycisk jest interaktywny. Następnie dodaję touch-action: manipulation, aby kliknięcia nie musiały czekać i obserwować potencjalnego podwójnego kliknięcia, co sprawia, że przyciski działają szybciej:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  cursor: pointer;
  touch-action: manipulation;
}

Kolory i obramowania

Następnie dostosowuję rozmiar czcionki, tło, tekst i kolory obramowania, używając niektórych z wcześniej utworzonych adaptacyjnych właściwości niestandardowych:

: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);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Cienie

Przyciski wykorzystują kilka świetnych technik. text-shadow dostosowuje się do jasnego i ciemnego tła, dzięki czemu tekst przycisku jest subtelnie widoczny na tle. W przypadku box-shadow przypisane są 3 cienie. Pierwszy z nich, --_shadow-2, to zwykły cień. Drugi cień to trik, który sprawia, że przycisk wydaje się lekko wypukły. Ostatni cień służy do wyróżnienia po najechaniu kursorem. Początkowo ma rozmiar 0, ale później zostanie mu przypisany rozmiar i zostanie zastosowana do niego animacja, aby wyglądał, jakby powiększał się z przycisku.

: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);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Układ

Przycisk ma układ flexbox, a konkretnie układ inline-flex, który będzie pasować do jego zawartości. Następnie wyśrodkowuję tekst i wyrównuję elementy podrzędne w pionie i poziomie do środka. Dzięki temu ikony i inne elementy przycisków będą prawidłowo wyrównane.

: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;
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Odstępy

W przypadku odstępów między przyciskami użyłem gap, aby elementy sąsiadujące nie stykały się ze sobą, oraz właściwości logicznych w przypadku dopełnienia, dzięki czemu odstępy między przyciskami działają w przypadku wszystkich układów tekstu.

: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);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

UX na urządzeniach dotykowych i myszach

Następna sekcja jest przeznaczona głównie dla użytkowników urządzeń mobilnych z ekranem dotykowym. Pierwsza właściwość, user-select, jest przeznaczona dla wszystkich użytkowników. Zapobiega ona wyróżnianiu tekstu przycisku. Jest to najbardziej widoczne na urządzeniach dotykowych, gdy przycisk jest naciskany i przytrzymywany, a system operacyjny wyróżnia tekst przycisku.

Zazwyczaj nie jest to sposób działania przycisków w aplikacjach wbudowanych, więc wyłączam tę funkcję, ustawiając user-select na none. Kolory podświetlenia (-webkit-tap-highlight-color) i menu kontekstowe systemu operacyjnego (-webkit-touch-callout) to inne funkcje przycisków, które są bardzo zorientowane na internet i nie są zgodne z ogólnymi oczekiwaniami użytkowników, więc też je usuwam.

: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;
}

Przejścia

Zmienna adaptacyjna --_transition jest przypisana do właściwości transition:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
),
:where(input[type="file"])::file-selector-button {
  

  transition: var(--_transition);
}

Po najechaniu kursorem, gdy użytkownik nie naciska aktywnie przycisku, dostosuj rozmiar cienia, aby uzyskać efekt skupienia, który sprawia wrażenie, że cień rośnie wewnątrz przycisku:

:where(
  button,
  input[type="button"],
  input[type="submit"],
  input[type="reset"]
):where(:not(:active):hover) {
  --_highlight-size: .5rem;
}

Po zaznaczeniu zwiększ przesunięcie konturu zaznaczenia od przycisku, aby uzyskać efekt zaznaczenia, który wydaje się rosnąć wewnątrz przycisku:

:where(button, input):where(:not(:active)):focus-visible {
  outline-offset: 5px;
}

Ikony

W przypadku ikon selektor ma dodatkowy selektor :where() dla bezpośrednich elementów podrzędnych SVG lub elementów z atrybutem niestandardowym data-icon. Rozmiar ikony jest ustawiany za pomocą właściwości niestandardowej z użyciem właściwości logicznych w wierszu i bloku. Ustawiono kolor obrysu, a także drop-shadow, aby pasował do text-shadow. flex-shrink jest ustawiona na 0, więc ikona nigdy nie jest spłaszczona. Na koniec wybieram ikony liniowe i przypisuję im style z fill: noneround:

: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;
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Dostosowywanie przycisków przesyłania

Chciałem, aby przyciski przesyłania miały nieco bardziej wyróżniający się wygląd, więc ustawiłem kolor tekstu przycisków na kolor akcentu:

:where(
  [type="submit"], 
  form button:not([type],[disabled])
) {
  --_text: var(--_accent);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Dostosowywanie przycisków resetowania

Chciałem, aby przyciski resetowania miały wbudowane znaki ostrzegawcze, które informowałyby użytkowników o potencjalnie destrukcyjnym działaniu. Zdecydowałem się też na dodanie do przycisku jasnego motywu więcej czerwonych akcentów niż do ciemnego motywu. Dostosowywanie polega na zmianie odpowiedniego jasnego lub ciemnego koloru bazowego, a przycisk zaktualizuje styl:

: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%);
}

Pomyślałem też, że kolor obrysu fokusu powinien pasować do akcentu czerwieni. Kolor tekstu zmienia się z ciemnoczerwonego na jasnoczerwony. Kolor obrysu dopasowuję do słowa kluczowego:currentColor

:where([type="reset"]):focus-visible {
  outline-color: currentColor;
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Dostosowywanie wyłączonych przycisków

Często zdarza się, że wyłączone przyciski mają słaby kontrast kolorów, ponieważ próbuje się je przyciemnić, aby wyglądały na mniej aktywne. Sprawdziłem każdy zestaw kolorów i upewniłem się, że spełnia wymagania, zmieniając wartość jasności HSL, aż wynik w Narzędziach deweloperskich lub VisBug był prawidłowy.

: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);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Dostosowywanie przycisków wprowadzania plików

Przycisk wprowadzania pliku to kontener obejmujący element span i przycisk. CSS może nieco ostylować kontener wejściowy i zagnieżdżony przycisk, ale nie może ostylować elementu span. Kontener ma ustawioną wartość max-inline-size, dzięki czemu nie będzie większy niż to konieczne, a wartość inline-size: 100% pozwoli mu się zmniejszyć i dopasować do kontenerów mniejszych od niego. Kolor tła jest ustawiony na kolor adaptacyjny, który jest ciemniejszy niż inne powierzchnie, dzięki czemu wygląda jak tło przycisku wyboru pliku.

:where(input[type="file"]) {
  inline-size: 100%;
  max-inline-size: max-content;
  background-color: var(--_input-well);
}

Przycisk wyboru pliku i przyciski typu wejścia mają przypisane style,appearance: none aby usunąć wszystkie style dostarczone przez przeglądarkę, które nie zostały zastąpione przez inne style przycisków.

:where(input[type="button"]),
:where(input[type="file"])::file-selector-button {
  appearance: none;
}

Na koniec do elementu inline-end przycisku dodawany jest margines, aby odsunąć tekst elementu span od przycisku i utworzyć odstęp.

:where(input[type="file"])::file-selector-button {
  margin-inline-end: var(--_padding-inline);
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Wyjątki dotyczące specjalnego ciemnego motywu

Przyciski głównego działania mają ciemniejsze tło, co zapewnia większy kontrast tekstu i sprawia, że są bardziej widoczne.

@media (prefers-color-scheme: dark) {
  :where(
    [type="submit"],
    [type="reset"],
    [disabled],
    form button:not([type="button"])
  ) {
    --_bg: var(--_input-well);
  }
}

Zrzut ekranu przedstawiający przyciski po zastosowaniu poprzednich stylów.

Tworzenie wariantów

Dla zabawy i z praktycznego punktu widzenia postanowiłem pokazać, jak utworzyć kilka wariantów. Jeden z nich jest bardzo żywy, podobnie jak przyciski główne. Inny wariant to duży. Ostatni wariant ma ikonę wypełnioną gradientem.

Wyrazisty przycisk

Aby uzyskać ten styl przycisku, bezpośrednio zastąpiłem podstawowe właściwości niebieskimi kolorami. Chociaż jest to szybkie i łatwe, usuwa właściwości adaptacyjne i wygląda tak samo w motywie jasnym i ciemnym.

.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%);
}

Przycisk niestandardowy w trybie jasnym i ciemnym. Jest bardzo intensywnie niebieski, jak typowe przyciski działań podstawowych.

Duży przycisk

Ten styl przycisku uzyskuje się przez zmodyfikowanie właściwości niestandardowej --_size. Wypełnienie i inne elementy przestrzeni są określane względem tego rozmiaru i skalowane proporcjonalnie do nowego rozmiaru.

.btn-large {
  --_size: 1.5rem;
}

Obok niestandardowego przycisku wyświetla się duży przycisk, około 150 razy większy.

Przycisk ikony

Ten efekt ikony nie ma nic wspólnego ze stylami przycisków, ale pokazuje, jak go uzyskać za pomocą zaledwie kilku właściwości CSS, oraz jak dobrze przycisk radzi sobie z ikonami, które nie są wbudowanymi plikami SVG.

[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));
}

Przycisk z ikoną wyświetlany w jasnym i ciemnym motywie.

Podsumowanie

Teraz, gdy wiesz, jak to zrobiłem, jak Ty byś to zrobił? 🙂

Urozmaićmy nasze podejście i poznajmy wszystkie sposoby tworzenia treści w internecie.

Utwórz demo, wyślij mi na Twitterze linki, a ja dodam je do sekcji remiksów społeczności poniżej.

Remiksy społeczności

Na razie jest tu pusto.

Zasoby