Twój „idealny" przycisk? To bug, który dopiero się ujawni
Dlaczego <button> to nie ograniczenie, tylko wsparcie
Każdy programista to zna. Tworzysz bibliotekę komponentów i potrzebujesz przycisku. Nie byle jakiego przycisku. Ma wyglądać konkretnie — animacja przy hoverze, gradient przy kliknięciu, własny kształt. Sięgasz więc po <div>, dorzucasz CSS i gotowe.
W Figmie wygląda świetnie. Na demo działa. A potem prawdziwy użytkownik korzysta z klawiatury, tabuluje przez interfejs albo otwiera stronę z czytnikiem ekranu — i wszystko się sypie.
Problem nie leży w CSS. Problem polega na tym, że zleciłeś <div> zadanie, do którego nigdy nie było projektowane.
Wszystkie te warstwy
Co zwykle się dzieje? Potrzebujesz przycisku o określonym wyglądzie. Tworzysz <div> z klasą .btn, stylizujesz do woli. Wygląda rewelacyjnie. Ale teraz potrzebujesz, żeby to faktycznie działało jak przycisk.
Dodajesz event listener na klik. Potem stan hover. Potem focus ring dla użytkowników klawiaturowych. Orientujesz się, że Spacja i Enter nie działają — dorzucasz listenery na keydown. Przycisk musi czasem być wyłączony, więc dodajesz klasę .disabled, która ma nadzieję zablokować odpowiednie interakcje. Gdzieś po drodze dorzucasz role="button" i tabindex="0", żeby czytniki ekranu nie pomijały twojego tworu kompletnie.
Gratulacje. Zbudowałeś obiekt w kształcie przycisku, który wymaga około 200 linii JavaScript, żeby w przybliżeniu odtworzyć to, co przeglądarka dałaby ci za darmo.
To jest dokładnie ta sytuacja, o której mówimy, gdy używamy metafory „spłacania kredytu" za złe decyzje architektoniczne. Każda linijka obejścia to odsetki od pierwotnego wyboru złego elementu.
Co tak naprawdę dostajesz
Native HTML elements niosą ze sobą całą masę zachowań, o których myśli się dopiero wtedy, gdy ich brakuje.
Używając prawdziwego <button>:
- Klawiatura działa out of the box — Spacja i Enter działają tak, jak użytkownicy oczekują
- Focus działa jak trzeba — element jest w naturalnym tab orderze, można go programistycznie fokusować
- Stan disabled to cały pakiet — blokuje wszystkie interakcje, przestaje być fokusowalny, czytniki ogłaszają go jako wyłączony
- Współpraca z formularzami — wysyła formularze, respektuje walidację, uczestniczy w form.reset()
- Semantyka dla accessibility — czytniki ekranu rozpoznają go jako przycisk bez dodatkowego ARIA
- Konwencje platformy — użytkownicy wiedzą, jak działają przyciski, bo przyciski działają tak samo wszędzie
Z <div> udającym przycisk dostajesz... CSS. Reszta leży na twoich barkach. I oto sekret: nieważne, jak dokładnie to zaimplementujesz — twoja wersja będzie gorsza niż to, co dają przeglądarki. Przeglądarki mają całe zespoły inżynierów optymalizujących te zachowania na różnych platformach, edge case'ach i technologiach asystujących. Twój custom button ma tylko ciebie.
Pytanie nie brzmi „czy mogę to ostylować?"
To jest moment, w którym współpraca między designem a devem albo świeci, albo tworzy dług techniczny. Programiści często interpretują „semantic HTML" jako „akceptację brzydkich domyślnych stylów przeglądarki". Nic z tych rzeczy.
Pytanie nie brzmi „czy dam radę to customowo ostylować?" — jasne, że dasz. Pytanie brzmi: „które native behaviors powinny przetrwać stylizację?"
<button> może wyglądać dokładnie tak, jak chcesz. Możesz usunąć cały default styling przez appearance: none. Możesz dorzucić gradienty, cienie, animacje, własne fonty — cokolwiek design wymaga. Contract przeglądarki nie zależy od wyglądu twojego przycisku.
Czego nie zrobisz łatwo z <div>? Odtworzysz to, co przeglądarka daje automatycznie. Wybierz więc starannie, gdzie walczysz: poświęcaj wysiłek inżynieryjny na to, co wyróżnia twój produkt, nie na wymyślanie koła na nowo.
Buildujemy dla prawdziwego weba
Twoi użytkownicy nie korzystają z produktu w próżni. Tabulują między polami, bo trzymają ręce na klawiaturze. Nawigują głosem. Powiększają ekran do 300%, bo tak im wygodniej. Mają różne przeglądarki, różne systemy operacyjne, różne technologie asystujące.
Web platform daje ci ogromną przewagę z semantic HTML. Native elements są accessible by default, keyboard-navigable by default, cross-platform by default. Kiedy z tym walczysz, nie tylko robisz sobie więcej roboty — tworzysz komponenty, które będą zawodzić w subtelny sposób dla prawdziwych ludzi.
Następnym razem, gdy będziesz sięgać po <div> z role="button", zapytaj siebie: czy buduję coś, co naprawdę wymaga customowej implementacji, czy po prostu boję się tagu <button>? Bo nudne rozwiązanie zwykle jest tym właściwym. I „przeglądarka to obsługuje poprawnie" to funkcja, nie ograniczenie.
Semantic web to nie żmudne przestrzeganie regułek. To używanie narzędzi zbudowanych do konkretnej roboty — i kierowanie energią tam, gdzie naprawdę ma to znaczenie.