Outro dia me pediram para atualizar o design visual dos botões de opção em um aplicativo da web no trabalho. Achei que não poderia ser tão complicado. É apenas um botão de opção, certo?
<input type="radio" name="beverage" value="coffee" />Bum! Feito. Os botões de opção são um elemento HTML integrado. Eles existem há 30 anos. O navegador facilita isso. Hora de um café.
Digite Shadcn
Pesquisei nossa base de código e percebi que estávamos usando dois componentes React do Shadcn para alimentar nossos botões de opção: e
.
Para quem não está familiarizado com o Shadcn, é uma estrutura de UI que fornece vários componentes de UI pré-construídos para uso em seus sites. Ao contrário dos frameworks UI tradicionais como o Bootstrap, você não o importa com uma tag de script ou
npm install. Em vez disso, você executa um comando que copia os componentes em sua base de código.
Aqui está o código que foi exportado do Shadcn para o nosso projeto:
"use client";
import * as React from "react";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { CircleIcon } from "lucide-react";
import { cn } from "@/lib/utils";
function RadioGroup({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return (
<RadioGroupPrimitive.Root
data-slot="radio-group"
className={cn("grid gap-3", className)}
{...props}
/>
);
}
function RadioGroupItem({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
<RadioGroupPrimitive.Indicator
data-slot="radio-group-indicator"
className="relative flex items-center justify-center"
>
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
RadioGroupPrimitive.Indicator>
RadioGroupPrimitive.Item>
);
}
export { RadioGroup, RadioGroupItem };Uau… 3 importações e 45 linhas de código. E está importando uma biblioteca de ícones de terceiros apenas para renderizar um círculo. (Quem precisa de CSS border-radius ou o SVG
elemento quando você pode adicionar uma dependência de terceiros?)
Todo o estilo é feito pelas 30 classes diferentes do Tailwind na marcação. Eu provavelmente deveria apenas ajustá-los para corrigir os problemas de estilo.
Mas agora estou distraído, irritado e curioso. Onde está o real ? Qual é o sentido de tudo isso? Vamos cavar um pouco mais fundo.
Insira a raiz
Os componentes Shadcn importam componentes de outra biblioteca chamada Radix. Para quem não está familiarizado com Radix, é uma estrutura de UI que fornece vários componentes de UI pré-construídos…
Espere um segundo! Não foi isso que acabei de dizer sobre Shadcn? O que dá? Por que precisamos de ambos? Vamos ver o que dizem os documentos do Radix:
Radix Primitives é uma biblioteca de componentes de UI de baixo nível com foco em acessibilidade, personalização e experiência do desenvolvedor. Você pode usar esses componentes como camada base do seu sistema de design ou adotá-los de forma incremental.
Portanto, o Radix fornece componentes sem estilo e o Shadcn adiciona estilos além disso. Como funciona o Radix? Você pode ver por si mesmo no GitHub: https://github.com/radix-ui/…
Isso está ficando ainda mais complicado: 215 linhas de código React importando 7 outros arquivos. Mas o que isso realmente faz?
Dando uma olhada no navegador
Vamos dar uma olhada nas ferramentas de desenvolvimento do navegador para ver se conseguimos saber o que está acontecendo.
Ok, em vez de uma entrada de rádio, ele está renderizando um botão com um círculo SVG dentro dele? Esquisito.
Ele também usa atributos ARIA para informar aos leitores de tela e outras ferramentas auxiliares que o botão é na verdade um botão de opção.
Os atributos ARIA permitem alterar o significado semântico dos elementos HTML. Por exemplo, você pode dizer que um botão é na verdade um botão de opção. (Se você quisesse fazer isso por algum motivo estranho.)
Curiosamente, aqui está a primeira regra de uso do ARIA:
Se você pode use um elemento ou atributo HTML nativo com a semântica e o comportamento necessários já construído emem vez de redirecionar um elemento e adicionar uma função, estado ou propriedade ARIA para torná-lo acessível, então faça isso.
Apesar disso, Radix está redirecionando um elemento e adicionando uma função ARIA em vez de usar um elemento HTML nativo.
Finalmente, o componente também inclui uma opção oculta mas apenas se for usado dentro de um element. Weird!
This is getting pretty complicated to just render a radio button. Why would you
want to do this?
Styling radio buttons is hard (Wait, is it?)
My best guess is that Radix rebuilds the radio button from scratch in order to
make it easier to style. Radio buttons used to be difficult to style
consistently across browsers. But for several years we’ve been able to style
radio buttons however we want using a few CSS tools:
appearance: noneremove o estilo padrão do botão de opção, permitindo-nos fazer o que quisermos.- Podemos usar o
::beforepseudoelemento para renderizar um “ponto” dentro do botão de opção sem estilo. - Podemos usar o
:checkedpseudoclasse para mostrar e ocultar esse ponto dependendo se o botão de opção está marcado. border-radius: 50%torna as coisas redondas.
Aqui está um exemplo de implementação:
input[type="radio"] {
appearance: none;
margin: 0;
border: 1px solid black;
background: white;
border-radius: 50%;
display: inline-grid;
place-content: center;
&::before {
content: "";
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
}
&:checked::before {
background: black;
}
}Isso não requer dependências, JavaScript ou funções ARIA. É apenas um elemento de entrada com alguns estilos. (Você pode fazer a mesma coisa com o Tailwind se essa for sua preferência.)
Requer conhecimento de CSS, mas este não é um segredo misterioso. Pesquisando no Google “como estilizar um botão de opção” mostra várias postagens de blog explicando essas técnicas. Você pode dizer que isso é muito CSS, mas o componente Shadcn que estávamos usando tinha 30 classes Tailwind!
Não estou tentando convencê-lo a escrever seus próprios estilos de componentes
Olha, eu entendi. Você tem muita coisa acontecendo. Você não é grande em CSS. Você só deseja pegar alguns componentes pré-construídos para poder se concentrar no problema real que está resolvendo.
Eu entendo perfeitamente por que as pessoas procuram bibliotecas de componentes como o Shadcn e não as culpo de forma alguma. Mas eu gostaria que essas bibliotecas de componentes mantivessem as coisas simples e reutilizassem os elementos integrados do navegador sempre que possível.
Quem se importa?
O desenvolvimento da Web é difícil. Há uma complexidade inerente na criação de sites de qualidade que resolvam problemas e funcionem bem em uma ampla variedade de dispositivos e navegadores.
Mas algumas coisas não precisam ser difíceis. Os navegadores facilitam coisas como botões de opção. Não vamos complicar demais.
Para entender como nossos botões de opção funcionam, preciso entender duas bibliotecas de componentes separadas e centenas de linhas de React.
Os visitantes do site precisam esperar que o JavaScript carregue, analise e execute para poder alternar um botão de opção. (Em meus testes, apenas adicionar esses componentes adicionou vários KB de JS a um aplicativo básico.)
É apenas um botão de opção
Por que estou dando tanta importância a isso? É apenas um botão de opção.
Mas essas pequenas decisões resultam em mais complexidade, mais carga cognitiva, mais bugs e pior desempenho do site.
Nós nos afastamos tanto da luz
Olhe para isso. É lindo:
<input type="radio" name="beverage" value="coffee" />Gosta de um jogo?
Jogue meu quebra-cabeça diário gratuito, Tiled Words!
Fonte: theverge

