---
fonte: 01-canonicos/seed-componentes.md
versao_da_fonte: v1.43
secao: 05
titulo: "Número — `estável` · validado pelo Rafael em 2026-08-02 (v0.9)"
sequencia: 7 de 98
bytes_do_corpo: 10323
md5_do_corpo: 62b548f497d787d94a97fda0fcf3e669
gerado_por: 06-validacao/geradores/gen-camada-ia.py
nota: fatia GERADA — o corpo abaixo é byte a byte o trecho do canônico; edite o canônico, nunca esta fatia. Canônico inteiro em https://ds.seed.eng.br/01-canonicos/seed-componentes.md
---
## 5. Número — `estável` · validado pelo Rafael em 2026-08-02 (v0.9)

> **Consome form-field (§2) e input (§3).** Documenta o que é do número: a proibição do `type="number"`, as duas variantes (livre e stepper), a localização de decimais na base, o dinheiro em centavos inteiros e a acessibilidade de spinbutton.
>
> **Base de evidência:** 3 rodadas (2026-08-02): dossiê GOV.UK contra type=number (+ MDN/espec HTML), NN/g steppers, Setproduct, Carbon NumberInput (issue #7819) e SAP UI5 (#1637) — os dois gigantes quebrados em separador decimal, React Aria NumberField, Semrush Intergalactic · HMRC currency, uxpatterns.dev currency, Société Générale, Wise money input, Workday (84 locales), SAP Fiori Step Input · ISO 4217 minor units, Modern Treasury, dinero.js, W3C ARIA APG spinbutton (+issues #704/#797), apps de pagamento (padrão observado e descartado). Decisões E1–E9 aprovadas pelo Rafael em 2026-08-02.

### 5.1 Papel, variantes e fronteiras

| | `número-livre` | `número-stepper` |
|---|---|---|
| Emprego | Medições e valores digitados: kWh, kWp, kVA, demanda, R$, % | Quantidades pequenas de ajuste fino: módulos, parcelas, itens, licenças |
| Régua (Fiori) | valor que raramente muda ou não segue passos | ajuste em incrementos definidos é o uso dominante |
| Stepper | NÃO tem | +/− nas laterais (horizontal — NN/g: precisão do dedo no toque) |

**Nunca número para o que não é quantidade** (Fiori + espec HTML): CEP, telefone, UC, IDs → input mascarado (§2.7). Datas/horas → date picker (item futuro).

### 5.2 As 9 decisões (E1–E9, aprovadas 2026-08-02)

| # | Decisão | Porquê | Descartado |
|---|---|---|---|
| E1 | **`type="number"` PROIBIDO no DS** — sempre `type="text"` + `inputmode` numeric/decimal | Dossiê GOV.UK: não ditável (Dragon), sem rótulo no NVDA, spin button confuso; scroll acidental MUDA O VALOR; 16+ dígitos arredondam silenciosamente (corrupção de dado); grandes viram exponencial irreversível; `pattern` não funciona; espec HTML: number só p/ incrementável | type=number "porque é número" (o erro nº 1 da web em campos numéricos) |
| E2 | Duas variantes; stepper com regras Fiori/Setproduct: min/max DESABILITA botão e seta correspondente (nunca esconde), sem layout shift 9→10, precision restringe na digitação, Shift+seta = passo 2×, valor inválido PERMANECE no campo com erro | Fiori Step Input (spec mais completa achada) + NN/g (horizontal no toque, +/− como rótulos, Fitts) + Baymard (nunca apagar entrada) | Stepper universal (ruído em medição digitada); vertical minúsculo nativo; apagar valor inválido |
| E3 | **Localização na base:** aceita vírgula E ponto na digitação; exibe no locale (1.234,56); canônico com ponto decimal | Carbon #7819 (rejeita vírgula) e SAP UI5 #1637 (ponto ESVAZIA o campo): IBM e SAP quebrados em sentidos opostos — quem não resolve na base sofre em issue pública; HMRC normatiza a tolerância (símbolo, espaços, separadores; máx 2 decimais em moeda) | Separador único rígido; resolver por produto (patch infinito) |
| E4 | Motor de implementação: **React Aria NumberField** com visual SEED | Parsing por locale em 30+ idiomas e sistemas de numeração, valida a digitação no locale, clamp/step, teclado mobile automático — headless, casa com shadcn; não reinventar parser | Parser próprio (reinventar o que a Adobe mantém); Intl.NumberFormat só na exibição (não cobre parsing de entrada) |
| E5 | Herdadas explícitas: JetBrains Mono + alinhado à direita em formulário de edição/tabela (§3/Fiori/SocGen); unidade no sufixo E no rótulo (C1); larguras xs/sm (C4) | Consistência com decisões já estáveis | — |
| E6 | Moeda: **formatar SÓ no blur; valor cru no focus**; atalhos opcionais k/m expandem milhares/milhões (ERP power-user: "2k" → 2.000) | uxpatterns: reformatar durante a digitação move o cursor; SocGen: velocidade p/ quem digita valores o dia todo | Live-format a cada tecla (guerra de cursor — a armadilha BR do §2.7 em dobro) |
| E7 | **Dinheiro NUNCA em float: canônico = centavos INTEIROS + código da moeda** (ISO 4217 minor units; BRL expoente 2: R$ 12,34 → `{centavos: 1234, moeda: 'BRL'}`) | Modern Treasury (infra de pagamentos) armazena tudo em minor units; dinero.js LANÇA EXCEÇÃO em float por princípio; aritmética binária corrói centavos | Float/decimal no front (corrupção silenciosa em soma de parcelas de proposta) |
| E8 | Stepper acessível padrão APG: input `role="spinbutton"` + `aria-valuemin/max/now`; botões +/− com `tabindex="-1"` (fora do tab — redundantes com as setas — mas operáveis por mouse e TalkBack/swipe); entrada inválida: valor permanece + `aria-invalid` (nunca aria-valuenow inventado) | W3C APG spinbutton + issues #704/#797; TalkBack incrementa por swipe vertical | Botões no tab order (2 paradas mortas por campo em formulário denso); zerar no inválido |
| E9 | Caixa-registradora (centavos-first dos apps de pagamento) **DESCARTADA como padrão** — registrada como candidata futura SE algum produto SEED tiver fluxo de pagamento | Prática observável (Nubank/Venmo/Cash App) SEM guideline primária publicada; contexto é transferência rápida em teclado próprio de fintech — o nosso é valor de proposta/fatura em formulário e ERP | Adotar por moda (padrão sem função no nosso contexto) |

### 5.3 Contrato HTML e parsing

`type="text"` + `inputmode="decimal"` (com decimais) ou `"numeric"` (inteiros) + `autocapitalize/autocorrect/spellcheck` off + `enterkeyhint` conforme posição (§2.6). Parsing tolerante: `1.234,56` · `1234,56` · `1234.56` · `R$ 1.234,56` · `2k` (com atalho ativo) → todos viram canônico `1234.56` (ou `{centavos:123456, moeda:'BRL'}` em dinheiro). **Regra de desambiguação de separador único (v0.9, bug pego em auto-teste antes da entrega):** com vírgula E ponto presentes, o ÚLTIMO é o decimal; com um separador só, o padrão `1.250` / `12.345.678` (grupos exatos de 3) lê como MILHAR e `1.2` / `1.25` como decimal — sem a regra, "1.250" de kWh viraria 1,25 (erro de 1000×). Ambiguidade residual honesta: `1.250` que o usuário QUIS como decimal de 3 casas é raro em nossos domínios (kWh/R$ usam 2 casas) e o eco formatado no blur dá a chance de correção. Exibição: `Intl.NumberFormat('pt-BR')` (ou locale do produto — Workday sustenta 84; nós nascemos com pt-BR e en-US pelos clientes EUA).

### 5.4 Microcopy

Rótulo carrega a unidade: "Consumo médio mensal (kWh)" · texto auxiliar ensina o formato SÓ quando ambíguo: "Use vírgula para decimais — ex.: 1.250,75" (prevenção Animalia) · erro de faixa diz os limites: "Informe entre 1 e 120 parcelas" (regra W3C: min/max invisíveis são jogo de adivinhação).

### 5.5 Código — essência (HTML/CSS delta + React)

```html
<!-- número-livre (medição) -->
<input class="seed-field__control seed-input__control--num" type="text" inputmode="decimal"
       autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="next">

<!-- número-stepper -->
<div class="seed-stepper" role="group">
  <button type="button" tabindex="-1" aria-label="Diminuir" class="seed-stepper__btn">−</button>
  <input class="seed-field__control seed-stepper__value" type="text" inputmode="numeric"
         role="spinbutton" aria-valuemin="1" aria-valuemax="120" aria-valuenow="12">
  <button type="button" tabindex="-1" aria-label="Aumentar" class="seed-stepper__btn">+</button>
</div>
```

```css
.seed-stepper{display:flex;align-items:stretch;border:1px solid var(--seed-field-border);
  border-radius:var(--seed-field-radius);overflow:hidden;width:max-content}
.seed-stepper:focus-within{box-shadow:var(--seed-focus-ring)}
.seed-stepper__btn{width:44px;border:0;background:var(--seed-surface-sunken);
  color:var(--seed-text-primary);font-size:18px;cursor:pointer}
.seed-stepper__btn:disabled{color:var(--seed-text-disabled);cursor:not-allowed}
.seed-stepper__value{border:0;width:72px;text-align:center;font-family:var(--seed-font-mono)}
```

```tsx
/* Motor: React Aria (E4). Esqueleto de consumo com visual SEED: */
import { NumberField, Group, Input, Button, Label } from "react-aria-components";
export function SeedNumberStepper(props: { label: string; min?: number; max?: number;
  step?: number; value?: number; onChange?: (v: number) => void }) {
  return (
    <NumberField minValue={props.min} maxValue={props.max} step={props.step ?? 1}
      value={props.value} onChange={props.onChange}
      formatOptions={{ maximumFractionDigits: 0 }}> {/* locale-aware: pt-BR/en-US */}
      <Label className="text-sm font-semibold text-[var(--seed-field-label)]">{props.label}</Label>
      <Group className="flex w-max items-stretch overflow-hidden rounded-md border border-[var(--seed-field-border)] focus-within:ring-2 focus-within:ring-[var(--seed-border-focus)]">
        <Button slot="decrement" excludeFromTabOrder aria-label="Diminuir"
          className="w-11 bg-[var(--seed-surface-sunken)] disabled:text-[var(--seed-text-disabled)]">−</Button>
        <Input className="w-20 border-0 bg-transparent text-center font-mono text-sm outline-none" />
        <Button slot="increment" excludeFromTabOrder aria-label="Aumentar"
          className="w-11 bg-[var(--seed-surface-sunken)] disabled:text-[var(--seed-text-disabled)]">+</Button>
      </Group>
    </NumberField>
  );
}
/* Moeda (E6+E7): <NumberField formatOptions={{style:'currency',currency:'BRL'}}> — React Aria
   formata no blur e edita cru; canônico emitido: Math.round(value*100) centavos + 'BRL'.
   Somas/operações com dinero.js — nunca float. */
```

### 5.6 Aplicação dos 7 testes (§1.12)

1. Container 3:1: herda; stepper com borda 4.74/4.50. ✅ 2. Mecanismos: variantes diferem por ESTRUTURA (botões), não por tom; min/max desabilita (fundo+cor+cursor). ✅ 3. Grayscale: +/− e desabilitado legíveis sem matiz. ✅ 4. Par: livre × stepper inconfundíveis. ✅ 5. Regra do um: um passo por clique; um valor por campo. ✅ 6. Estado atual: valor sempre visível; limites comunicados no erro/ajuda. ✅ 7. Polegar 360px: botões 44px nas LATERAIS (horizontal NN/g), fonte 16px, teclado decimal correto. ✅

---

