Ação rotulada. Três variantes de ênfase, dois tamanhos, quatro estados. A variante primary materializa-se como Tecla V-B.
Quando usar
Para toda ação direta do usuário: confirmar pagamento, cancelar, voltar, abrir, enviar. Ações rotineiras e ações de alta consequência.
Quando não usar
- Para ação de filtro pontual numa listagem curta — usar
Chipse o filtro for representado como etiqueta. - Para separar dois blocos de conteúdo — usar
Hairlineou espaçamento. - Para texto inline que leva a outro contexto — usar link textual (não coberto na v1).
Anatomia
A variante primary é a única com três camadas físicas: socket, rim e cap. As variantes secondary e tertiary são de camada única.
Slots comuns a todas as variantes:
- label — string curta, verbo direto ("Pagar agora", "Ver depois", "Cancelar").
- iconLeading (opcional) — glifo 16/24 à esquerda.
- iconTrailing (opcional) — glifo 16/24 à direita.
Variantes
| Variante | Ênfase | Materialização | Uso |
|---|---|---|---|
primary | Alta | Tecla V-B (socket + rim + cap) | Ação-chave da cena. Uma por tela, no máximo duas. |
secondary | Média | Sólido V-A (preto convencional) | Ação alternativa relevante. |
tertiary | Baixa | Ghost V-A (fundo transparente, hover preenche) | Ação secundária, cancelar, ver mais. |
Tamanhos
| Size | Altura | Uso |
|---|---|---|
md | 40 px | Default — contextos de UI padrão. |
lg | 64 px (secondary / tertiary) · 65 px (primary / Tecla) | Contexto em que o botão domina a cena: onboarding, landing, card-ação. |
Estados
Estados demonstrados na variante secondary. Hover e active são estados de runtime — renderizados aqui como classes de demonstração estática.
Estados — primary (Tecla)
A Tecla cobre cinco estados visuais: rest, hover, active (pressed), disabled e loading. Apresentação visual derivada da iteração-fonte do Claude Design (Variante B canônica do merge). Hover troca os tokens do cap por --color-accent-amber-hover-*; disabled neutraliza o acento V-B com --color-accent-amber-disabled-*. Active (pressed) mantém --shadow-tecla-pressed. Loading preserva o cap âmbar vivo (sobrescreve disabled mesmo quando coexistem) e substitui o label por um spinner geométrico — paralelo ao padrão do secondary, mas com classe própria .peppe-tecla__spinner pra preservar isolamento entre os dois CSS canônicos.
Estados — tertiary
O tertiary herda a estrutura do secondary mas com fundo transparente em repouso. O hover preenche o fundo com --color-nav-hover (3% ink-primary) — transição suave via --duration-micro. Active usa --color-nav-active (5%). Disabled mantém apenas a cor de rótulo desativada.
Playground
Teclas reais e interativas. Hover passa o mouse, click executa pressed via :active, e o toggle de disabled liga/desliga o estado disabled na tecla principal. Use o DevTools pra inspecionar propriedades computadas em cada estado.
Nota técnica: O playground usa elementos <button> e <a> reais. Estados runtime (:hover, :active, :focus-visible, :disabled) são acionados pela própria interação do usuário. Os modifiers BEM (--hover, --pressed, --disabled) usados nas amostras de Estados existem em paralelo pra demos estáticas — ambos os caminhos disparam as mesmas regras CSS.
Tokens aplicáveis
| Token | Valor | Papel |
|---|---|---|
--color-accent-rim | #5F0000 | Rim borgonha da Tecla |
--color-accent-amber-top | #F7692B | Cap âmbar — topo (rest) |
--color-accent-amber-bottom | #FA4C00 | Cap âmbar — base (rest) |
--color-accent-amber-hover-top | #FF986B | Cap — topo hover |
--color-accent-amber-disabled-top | #C9C9C9 | Cap — disabled |
--shadow-tecla | — | Sombra do socket (rest) |
--shadow-tecla-pressed | inset 0 4px 8px rgba(0,0,0,0.2), inset 0 1px 2px rgba(95,0,0,0.45) | Sombra interna (active) — duas camadas: profundidade do "afundou" + reforço curto do rim borgonha |
--radius-tecla-socket | 18px | Raio do socket |
--radius-tecla-rim | 16px | Raio do rim |
--radius-tecla-cap | 12px | Raio do cap |
--color-ink-primary | #171717 | Secondary — fundo (rest) |
--color-ink-primary-hover | #2A2A2A | Secondary — fundo (hover) |
--color-ink-primary-active | #000000 | Secondary — fundo (active) |
--color-ink-inverse | #F7F7F7 | Secondary — label sobre o preto |
--color-nav-hover | rgba(23,23,23,0.03) | Tertiary — fundo (hover) |
--color-nav-active | rgba(23,23,23,0.05) | Tertiary — fundo (active) |
--radius-button-md | 12px | Secondary / tertiary — raio tamanho md (40px) |
--radius-button-lg | 20px | Secondary / tertiary — raio tamanho lg (64px) |
--type-family-sans | Instrument Sans | Label de todas as variantes |
--type-size-md | 16px | Tamanho do label |
--type-weight-medium | 500 | Peso do label |
Conteúdo
- Label é verbo direto e específico. "Pagar agora", não "Clique aqui". "Confirmar pagamento", não "OK".
- Máximo 3 palavras. Se não couber, a ação está mal delimitada.
- Sem pontuação final. O botão é ação, não frase.
- Sem emoji. A única exceção é
iconLeadingouiconTrailingcom função semântica real.
Ver Voz & Tom §4.3 — densidade e extensão.
Acessibilidade
aria-labelobrigatório quando o botão tem apenas ícone (sem label visível).- Foco navegável com ring visível — delegado ao token
--focus-ring-widthe--focus-ring-offset. - Contraste: label branco sobre cap âmbar passa WCAG AA em
md; secondary--color-ink-inverse(#F7F7F7) sobre--color-ink-primary(#171717) passa AAA (~15:1). - Fallback de canal: em SMS, primary colapsa em keyword ("RESPONDER PAGAR"); secondary/tertiary colapsam em texto; em voz, primary é a opção lida primeiro.
- Estado
loading:aria-labelno botão descreve a ação em curso ("Carregando pagamento").
Do / Don't
Do
Label em verbo direto. Uma ação clara, sem ambiguidade.
Don't
Label em abstração. "Continuar", "Próximo", "OK" não informam o que vai acontecer.
Do
Tertiary para cancelar/voltar. A hierarquia de ênfase comunica a importância relativa das ações.
Don't
Três Teclas V-B na mesma tela — vira mancha. Primary é o acento, não o chão.
Componentes relacionados
- Chip — quando a ação é filtro ou etiqueta.
- Quick Actions Card — quando múltiplas ações rotineiras pedem agrupamento.
Markup de referência
HTML namespaced copiável — semente da "story" futura no Storybook. Para a variante primary, o CSS da Tecla V-B vive em sombra-e-elevacao.css e prototype/visual-reference/styles.css.
<button className="peppe-button peppe-button--secondary" type="button"> <span className="peppe-button__label">Ver depois</span> </button><button className="peppe-button peppe-button--tertiary" type="button"> <span className="peppe-button__label">Cancelar</span> </button><button className="peppe-button peppe-button--secondary" type="button" data-state="loading" aria-label="Carregando"> <svg className="peppe-button__spinner" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> <circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.5" strokeDasharray="42 14" strokeLinecap="round"/> </svg> <span className="peppe-button__label">Processar</span> </button><button className="peppe-tecla" type="button" aria-label="Pagar agora"> <span className="peppe-tecla__rim"> <span className="peppe-tecla__cap">Pagar agora</span> </span> </button> <!-- Variante compacta --> <button className="peppe-tecla peppe-tecla--sm" type="button" aria-label="Pagar"> <span className="peppe-tecla__rim"> <span className="peppe-tecla__cap">Pagar</span> </span> </button> /* CSS — componente-button.css */ .peppe-button {display: inline-flex; align-items: center; justify-content: center; gap: var(--space-8); padding: 0 var(--space-24); height: var(--touch-target-md); border: none; border-radius: var(--radius-button-md); font-family: var(--type-family-sans); font-size: var(--type-size-md); font-weight: var(--type-weight-medium); cursor: pointer;}/* Tecla V-B: CSS em sombra-e-elevacao.css → .peppe-tecla (socket — gradient + shadow direcionais) → .peppe-tecla__rim (borgonha — padding 4px shifta no pressed) → .peppe-tecla__cap (âmbar — anel bisel via border-box gradient) Estados: :hover, :active, :disabled (interativo) ou --hover, --pressed, --disabled (modifier pra demo estática). */