Ponteiro entra hover
A placa sobe de 32% a 40% e a luz do cursor acende. O anel branco já está a 1: o Figma o desenha nos três estados.
| propriedade | de → para | tempo |
|---|---|---|
| placa | #0D0F12 32% → #1F2128 40% | 180ms |
| luz do cursor | 0 → 1 | 260ms |
A casca circular para qualquer ação com ícone: Small 56 com ícone de 24, Large 112 com o poço de 64 e ícone de 32. Quatro variantes desenhadas (Small Default, Hover, Pressed; Large Default) e os estados que um botão precisa além delas: ligado (aria-pressed), ocupado (aria-busy), aberto (aria-expanded), foco pelo teclado, desabilitado. O accent é o do modo, a mesma tabela do Tab Bar e do Text Input. Os nomes são os do mapa de vocabulário.
O quadro do Figma, congelado: não respondem ao ponteiro. Size=Small com State=Default, Hover e Pressed; Size=Large só com Default, como a referência desenhou. Em código, data-size e data-state.
Small ligado e Large em repouso, ampliados 1,35×. Numerado é o que o CSS desenha; cotado é o que o Figma mede.
Coluna a coluna: a medida do Figma, o que o kit já tinha, e o que a peça adotou. O identificador sai do mapa de vocabulário; o Figma dá a base.
| Figma 327:754 | O kit / as duas peças | A peça |
|---|---|---|
| Fill Default rgba(13,15,18,.32) · Hover rgba(31,33,40,.4) · Pressed rgba(8,10,13,.32) | --nash-placa · --nash-placa-hover · --nash-placa-pressed | iguais, byte a byte — os três tokens já existiam |
| Stroke 1,5 branco 15 › 6 › 2 › 0 por dentro, nos três estados | anel branco por máscara (.nash-surface, .nash-tab, .nash-text-input) | o mesmo anel, a 1 nos três estados. O Text Input o tem a 0,5 no repouso porque o Figma dele não desenha stroke no Default |
| Size=Small 56 (raio 28) · Size=Large 112 (raio 56) | .nash-icon-button era 44 (--nash-alvo-solto); .nash-icon-button--small, 36 | 56 e 112 por data-size="small|large"; raio 50%. --nash-icon-button-size pra densidade de tela (36 no Project Builder). O modificador BEM saiu: variante é data-<eixo> |
| Icon/*/24 no Small, Icon/*/32 no Large, branco | o kit tinha 18; o Text Input usa 20 a 60% | 24 e 32, branco. O ícone que a página escreve (.nash-icon) ou data-icon="close|airplay|plus|mic|play|pause|previous|next" |
| icon-inner-container: 64, branco 8%, raio 32, só no Large | nada | .nash-icon-button-well nos dois tamanhos; no Small com o tamanho do ícone e sem fill |
| State=Pressed | o kit tem pressão de 100ms; o Text Input mapeou o Pressed ao foco | Pressed = :active, scale 0,96 em 100ms, solta em 160. Num botão a pressão não fica; o que fica é ARIA |
| Large só Default | — | o Large recebe o Hover e o Pressed do Small (mesmas placas). Pendente no Figma |
| Sem ligado, ocupado, aberto, foco, desabilitado | o kit lia aria-pressed por um efeito de seleção, e fazia ocupado por classe (.nash-arco) | aria-pressed (accent + halo + onda + aura, o ícone enche, o poço pula, 540ms; depois respira; desligar em 600, a aura comida de dentro pra fora), aria-busy (arco de 1,8s; o clique é segurado), aria-expanded (135° com mola, onda no poço), :focus-visible (anel + outline), :disabled (50%). Estado é ARIA; nenhum data-estado |
| — | .nash-giro (× inclina 15°) e .nash-realce (ícone cresce 5%) | data-icon-motion="tilt|scale", o eixo do vocabulário; vive dentro da peça |
| Nomes: Size, State, Icon/XCircle/24, icon-inner-container | — | data-size, data-state, ICONS.close, .nash-icon-button-well. O identificador é do mapa (docs/vocabulario-mapa.json): inglês, parte nash-<bloco>-<parte>, token --nash-icon-button-*, runtime --nash-runtime-*, evento nash:toggle |
| Escala de tempo | --nash-tab-escala, --nash-text-input-escala, --nash-escala | --nash-icon-button-time-scale, com --nash-escala da família como fallback |
| Movimento reduzido | .nash-reduzido | [data-motion="reduced"] num ancestral, o nome do vocabulário; as duas peças anteriores ainda leem .nash-reduzido |
Toda curva é a de saída do kit, menos uma: a mola do abrir. Dois laços, cada um preso a um estado: a respiração de aria-pressed (um número, --nash-icon-button-breath, que o halo da borda, a aura e o poço leem; ao desligar ele pausa onde está em vez de sumir) e o giro de aria-busy.
A placa sobe de 32% a 40% e a luz do cursor acende. O anel branco já está a 1: o Figma o desenha nos três estados.
| propriedade | de → para | tempo |
|---|---|---|
| placa | #0D0F12 32% → #1F2128 40% | 180ms |
| luz do cursor | 0 → 1 | 260ms |
Uma névoa de 90% da casca no accent e o anel aceso no ponto mais próximo do ponteiro. Segue em 70ms; apaga em 260 quando o ponteiro sai. Apertada ou ligada, a névoa é mais forte.
| camada | cor | tempo |
|---|---|---|
| névoa | accent 12% (ligado 18%, apertada 24%), 90% da casca | 260ms · segue em 70 |
| anel | accent 70%, 55% da casca | 260ms |
A placa desce pro #080A0D e a casca assenta a 0,96 em 100ms; solta em 160. Não fica: o que fica é aria-pressed.
| propriedade | de → para | tempo |
|---|---|---|
| placa | → #080A0D 32% | 180ms |
| casca | scale 1 → 0,96 | 100ms · solta em 160 |
| luz do cursor | 12% → 24% | 260ms |
Ligar: a placa desce pro #080A0D, o anel branco apaga, o ícone toma o accent em 140 e enche em 260 (a parte .nash-icon-fill: a cápsula do mic), o poço pula 18% e o anel de accent acende. O halo da borda espera 120 e vai a 32px antes de assentar em 24 — a curva do ponto da aba — com uma onda que se abre da borda e apaga, e a aura se abre da borda até meia casca além em 420. Total: 540ms. Depois, ligado, respira: halo, aura e poço, laço de 1,8s. Desligar: placa, anéis, cor e fill voltam nos mesmos tempos; o halo da borda apaga em 320; a aura é comida de dentro pra fora em 600 — a borda de dentro da máscara caminha até a de fora enquanto a luz cai — e a respiração pausa onde está, então nada salta. O clique alterna quando a peça é montada com toggle; o evento é nash:toggle.
Ligar · 540ms
Desligar · 600ms
O anel de accent vira um arco que dá a volta em 1,8s; o ícone baixa a 60%; o clique é segurado. Único laço da peça; termina com o estado.
| propriedade | de → para | tempo |
|---|---|---|
| anel de accent | gradiente → arco cônico, --nash-runtime-spin 0 → 360° | laço 1800ms |
| ícone | 1 → 0,6 | 140ms |
O ícone gira 135° em 420ms pela mola (passa do alvo e volta): o + vira ×. A placa desce à apertada e do poço sai uma onda no accent. Fechar desfaz o giro pela mesma mola. A página escreve o atributo; a peça o lê.
| propriedade | de → para | tempo |
|---|---|---|
| ícone | rotate 0 → 135°, cubic-bezier(0.34, 1.56, 0.64, 1) | 420ms |
| placa | → #080A0D 32% | 180ms |
| poço | onda: anel de accent 45%, spread 0 → 16px, apagando | 420ms |
tilt inclina 15° (o × de fechar: "vai fechar"); scale cresce 5% ("esse ícone é a ação"). Um ou outro, nunca os dois. Eram .nash-giro e .nash-realce.
| eixo | de → para | tempo |
|---|---|---|
| tilt | rotate 0 → 15° | 140ms |
| scale | scale 1 → 1,05 | 140ms |
Opacidade 0 → 1 e scale 0,9 → 1 em 420ms, com `backwards`: o botão chega, não pisca.
| propriedade | de → para | tempo |
|---|---|---|
| opacidade · scale | 0 → 1 · 0,9 → 1 | 420ms |
Dois arquivos do kit. Carregar o JS basta: todo .nash-icon-button da página é completado e montado sozinho.
<link rel="stylesheet" href="css/nash-icon-button.css" /> ← depois de nash-componentes.css
<button class="nash-icon-button" data-icon="close" aria-label="Fechar"></button>
<script src="js/nash-icon-button.js"></script>
const botao = NashIconButton.mount('#fechar', {
icon: 'close', // close · airplay · plus · mic · play · pause · previous · next; ou o <svg class="nash-icon"> da página
label: 'Fechar', // aria-label
size: 'large', // data-size: small (padrão) · large
iconMotion: 'tilt', // data-icon-motion: tilt · scale
toggle: true, // o clique alterna aria-pressed e emite nash:toggle
onToggle(pressed, b) {}
})
botao.setPressed(true) // aria-pressed: accent + halo
botao.setBusy(true) // aria-busy: o arco; o clique é segurado
botao.setIcon('pause')
botao.el.addEventListener('nash:toggle', (e) => e.detail) // { pressed }
// a página escreve o ícone; o JS completa a luz e o poço:
<button class="nash-icon-button" aria-label="Tocar"><svg class="nash-icon" viewBox="0 0 24 24">…</svg></button>
// variante congelada, pra documentação — não responde ao ponteiro:
<button class="nash-icon-button" data-state="hover" data-icon="close"></button>
// no ícone da página: <rect class="nash-icon-fill" …> ← a parte que enche ao ligar
// no botão: data-size="small|large" · data-icon-motion="tilt|scale" · data-mode="chat|voz|design|imagem|social" · aria-pressed · aria-busy · aria-expanded
// tokens: --nash-icon-button-accent · --nash-icon-button-size (36 pra densidade de tela) · --nash-icon-button-icon-size · --nash-icon-button-time-scale
// na família: --nash-escala: 4 (câmera lenta) · [data-motion="reduced"]