Button
Button dispara ações. Use exatamente um botão primary por tela para a ação principal e recorra a
secondary, soft, ghost ou danger para sinalizar a intenção. Ícones, modo full (largura
total) e um estado loading que mantém o rótulo no lugar (sem deslocamento de layout) já vêm
embutidos.
Exemplos
Variantes
Variante é sobre intenção, não decoração. Um primary por tela; danger só para ações que
destroem algo.
Tamanhos
As alturas são fixadas por token: sm 32px, md 40px, lg 48px. Use um tamanho só por linha de ações.
Ícones e estados
loading mantém o rótulo renderizado e bloqueia a interação, então o botão nunca muda de largura
no meio do clique. Se o motivo de um botão estar desabilitado não estiver óbvio na tela, diga isso
em um texto próximo.
Renderizando como link
asChild estiliza o elemento filho em vez de renderizar um <button>. Use sempre que a ação
navegar: o resultado é uma âncora de verdade, então clique do meio, "abrir em nova aba" e a prévia
do link na barra de status continuam funcionando.
Quando usar
Use um Button para uma ação que o usuário executa na página atual — enviar, abrir um diálogo, iniciar um processo.
Prefira outro componente quando:
- Ele navega para algum lugar — é um link. Use
<Button asChild>em volta de uma âncora, para ter a aparência de botão com o comportamento de link. - É uma entre várias escolhas exclusivas — use Radio ou Tabs.
- Ele liga e desliga uma configuração — use Switch, que anuncia o próprio estado.
- É só ícone, numa barra de ferramentas densa — use IconButton, dimensionado para alvo de toque de 44px.
Acessibilidade
- Renderiza um
<button>nativo, então ativação por Enter/Espaço, envio de formulário e ordem de foco vêm da plataforma. loadingdefinearia-busye bloqueia a interação; o rótulo visível permanece, então leitores de tela continuam anunciando o que o botão faz.- Um Button só de ícone não tem nome acessível a menos que você forneça um — passe
aria-label(isso é cobrado pela suíte do axe, não pelo sistema de tipos). - O foco é um anel
box-shadow: var(--shadow-focus)no:focus-visible. Nunca troque poroutline: none. - Com
asChild, o contrato de acessibilidade é do filho: uma âncora precisa de umhrefreal para ser alcançável pelo teclado.
API e código
| Nome | Tipo | Obrigatória | Descrição |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'soft' | 'ghost' | 'danger' | — | Visual variant. Default `"primary"`. Use `primary` for the single main action per view. |
size | 'sm' | 'md' | 'lg' | — | Control height. Default `"md"` (sm 32px · md 40px · lg 48px). |
iconLeft | ReactNode | — | Icon rendered before the label (usually `<Icon size={16} />`). |
iconRight | ReactNode | — | Icon rendered after the label. |
loading | boolean | — | Show the spinner and block interaction. The label stays visible (no layout shift). |
full | boolean | — | Stretch to 100% of the container width. |
asChild | boolean | — | Render the single child element instead of a `<button>`, keeping Lyra button styling — use for links: `<Button asChild><a href>…</a></Button>`. |
Componha uma classe de variante e uma de tamanho; o rótulo fica em elemento próprio para o spinner entrar sem reflow:
<button class="lyra-btn lyra-btn--primary lyra-btn--md">
<span class="lyra-btn__label">Salvar alterações</span>
</button>
<button class="lyra-btn lyra-btn--danger lyra-btn--md lyra-btn--loading" aria-busy="true">
<span class="lyra-btn__spinner" aria-hidden="true"></span>
<span class="lyra-btn__label">Excluindo</span>
</button>
<a class="lyra-btn lyra-btn--secondary lyra-btn--md" href="/docs">
<span class="lyra-btn__label">Ler a documentação</span>
</a><lyra:button> Gerado do lyra-ds/blade v0.10.0.
| Prop | Padrão | Obrigatória | Valores de exemplo |
|---|---|---|---|
variant | 'primary' | — | danger ghost primary secondary soft |
size | 'md' | — | lg md sm |
loading | false | — | — |
disabled | false | — | — |
full | false | — | — |
<lyra:button variant="primary" size="md">Save changes</lyra:button>