# Armadilhas de tema e token

> Quatro defeitos que só aparecem quando o token sai da tabela e entra no CSS de quem consome. Cada armadilha traz o que os gêmeos (`02-tokens/seed-tokens.css`, v1.40) medem hoje, contado na geração, e a cláusula do canon que a resolve, byte a byte, com o endereço de origem. Nada aqui é regra nova. Gerada por `06-validacao/geradores/gen-vitrine.py`.

## No escuro, o primitivo não troca de valor

Uma cor primitiva (`--seed-turquesa-700`) tem um valor só. O componente que a usa direto fica com a cor do claro também no escuro, e o par fundo × texto se separa quando só um dos dois inverte.

Medido nos gêmeos: 60 primitivos de cor no `:root`, nenhum redeclarado no bloco `[data-theme="dark"]` — que redeclara 151 variáveis, nenhuma delas primitiva de cor.

Resolve:

> **Componentes §0** — seed-componentes.md v2.00 · §0 · MD5 da fatia b307543f · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/00-fundamentos-de-componente-valem-para-todos-os/

1. **Toda cor via token semântico** (`--seed-action-primary`), nunca primitivo, nunca hex. Dark mode vem de graça.

> **Tokens §1** — seed-tokens.md v1.40 · §1 · MD5 da fatia 56715422 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-tokens/01-arquitetura-em-3-camadas/

- **Componentes consomem semânticos**, nunca primitivos, nunca hex.

> **Componentes §64** — seed-componentes.md v2.00 · §64 · MD5 da fatia c7911e59 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/64-bancada-e-tema-regras-transversais-bt1-bt6-f7-5/#sec-64-1-os-contratos

| # | Contrato | Por quê |
|---|---|---|
| **BT3** | **O par de estado tem de sobreviver à inversão.** Fundo e texto de um mesmo estado medem **≥ 3,00** de contraste nos DOIS temas | É a formulação MEDÍVEL do GI2. O defeito real nunca é "usou primitiva": é primitiva de um lado e semântico do outro — o par se separa quando um dos dois inverte e o outro não |

## A especificidade decide qual bloco de tema vence

Os gêmeos declaram o mesmo nome em mais de um bloco: o claro, o escuro, o espelho do tema do sistema e os modos de acessibilidade. A sobrescrita feita no produto com seletor mais fraco, ou que chega antes, perde para o bloco de tema — e o defeito só aparece no tema que aquele bloco cobre.

Medido nos gêmeos: os blocos que declaram variáveis, na ordem do arquivo — com especificidade igual, vence o que vem depois.

| seletor | condição | especificidade (a, b, c) | variáveis | linha |
|---|---|---|---|---|
| :root | — | :root → (0, 1, 0) | 378 | 181 |
| [data-theme="dark"] | — | [data-theme="dark"] → (0, 1, 0) | 151 | 969 |
| :root:not([data-theme="light"]) | @media (prefers-color-scheme: dark) | :root:not([data-theme="light"]) → (0, 2, 0) | 151 | 1180 |
| :root | @media (prefers-reduced-motion: reduce) | :root → (0, 1, 0) | 6 | 1383 |
| :root, :root[data-theme] | @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) | :root → (0, 1, 0) · :root[data-theme] → (0, 2, 0) | 2 | 1408 |
| :root, :root[data-theme] | @media (prefers-reduced-transparency: reduce) | :root → (0, 1, 0) · :root[data-theme] → (0, 2, 0) | 2 | 1414 |
| :root, :root[data-theme] | @media (prefers-contrast: more) | :root → (0, 1, 0) · :root[data-theme] → (0, 2, 0) | 7 | 1420 |

Resolve:

> **Tokens §3d** — seed-tokens.md v1.40 · §3d · MD5 da fatia 9a593032 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-tokens/03d-material-vidro-e-metal-f10-1-v1-24-v1-25-v1-26/#sec-os-fallbacks-regra-escrita-e-executavel

O seletor dos blocos de fallback é `:root, :root[data-theme]`: cobre os três estados de tema (sem atributo, claro
forçado, escuro forçado) com especificidade igual ou maior que a dos blocos de tema, e vence por vir depois no arquivo.

## Sombra tem tinta, e no escuro a borda assume

A elevação é o par superfície + sombra. A sombra escrita à mão em preto, fora do token, não acompanha o tema.

Medido nos gêmeos: 3 tokens de sombra; no claro, 3 tingem com `rgba(36,46,52,…)` (o cinza-900 `#242E34`); no escuro, 3 usam `rgba(0,0,0,…)`.

| token | claro (:root) | escuro ([data-theme="dark"]) |
|---|---|---|
| --seed-shadow-modal | 0 16px 48px rgba(36,46,52,0.22) | 0 16px 48px rgba(0,0,0,0.60) |
| --seed-shadow-overlay | 0 8px 24px rgba(36,46,52,0.14), 0 2px 6px rgba(36,46,52,0.08) | 0 8px 24px rgba(0,0,0,0.50) |
| --seed-shadow-raised | 0 1px 2px rgba(36,46,52,0.10), 0 1px 1px rgba(36,46,52,0.06) | 0 1px 2px rgba(0,0,0,0.40) |

Resolve:

> **Tokens §2** — seed-tokens.md v1.40 · §2 · MD5 da fatia 2b940971 · byte a byte do canon · https://ds.seed.eng.br/canon/seed-tokens/02-camada-1-primitivos/#sec-2-8-elevacao-par-superficie-sombra-modelo-atlassian

Elevação nunca é só sombra: é o par. `surface-raised` + `shadow-raised` (cards) · `surface-overlay` + `shadow-overlay` (menus, popovers) · modal usa `shadow-modal`. Sombras: no tema claro, **tintadas no cinza-900** (#242E34, `rgba(36,46,52,…)`); no tema escuro, **preto com opacidade** (`rgba(0,0,0,0.40)` no `shadow-raised`, `0.50` no `shadow-overlay`, `0.60` no `shadow-modal`), porque uma sombra tintada some sobre superfície escura [inferência: não medida]. No dark, a **borda sutil assume a separação** (sombra sozinha some em fundo escuro). *Errata de redação (v1.38, `PR-P177`): a frase anterior dizia "sombras tintadas no cinza-900, nunca preto", mas as 3 sombras do tema escuro são pretas com opacidade; o texto passa a descrever o que os tokens emitem, e nenhum valor mudou.* Existe também `surface-sunken` para áreas rebaixadas (wells, blocos de código).

## O atalho de CSS que anula um token composto

Um token cujo valor tem várias partes (lista de sombras, pilha de fontes, cadeia de filtros) só vale na propriedade que aceita aquele formato. Dentro de outra propriedade — `outline: 3px solid var(--seed-focus-ring)` — a declaração inteira fica inválida no momento do cálculo e a propriedade cai no valor herdado ou no inicial, sem aviso no console.

A regra do cálculo é a da norma: W3C, *CSS Custom Properties for Cascading Variables Module Level 1*, Candidate Recommendation Snapshot de 16 jun. 2022, §3.1 Invalid Variables (lido em 2026-10-18). Conferido nos gêmeos, na geração: 13 tokens com valor de mais de uma parte, listados abaixo com a propriedade em que o canon e o registry os consomem. Dos 5 tokens de borda (`--seed-border-*`), 5 têm uma parte só (uma cor); das 4 durações (`--seed-dur-*`), 4; das 5 curvas (`--seed-ease-*`), 5 — o atalho `border` ou `transition` escrito com esses tokens não se anula hoje.

| token | forma do valor (o maior dos temas) | propriedade no canon (código) | propriedade no registry (r/*.css) |
|---|---|---|---|
| --seed-focus-ring | lista de 2, separada por vírgula | box-shadow (10×) | — |
| --seed-font-expressive | lista de 3, separada por vírgula | — | — |
| --seed-font-mono | lista de 5, separada por vírgula | font-family (4×) | — |
| --seed-font-sans | lista de 6, separada por vírgula | font-family (4×) | — |
| --seed-mat-canvas | lista de 5, separada por vírgula | background (1×) | background (2×) |
| --seed-mat-glass-filter | 2 partes separadas por espaço | backdrop-filter (2×) | backdrop-filter (2×) |
| --seed-mat-glass-shadow | lista de 4, separada por vírgula | box-shadow (1×) | box-shadow (2×) |
| --seed-mat-glass-shadow-hover | lista de 4, separada por vírgula | — | box-shadow (1×) |
| --seed-mat-metal-action-edge | lista de 4, separada por vírgula | box-shadow (1×) | box-shadow (1×) |
| --seed-mat-metal-edge | lista de 5, separada por vírgula | box-shadow (1×) | box-shadow (1×) |
| --seed-shadow-modal | 4 partes separadas por espaço | — | — |
| --seed-shadow-overlay | lista de 2, separada por vírgula | — | — |
| --seed-shadow-raised | lista de 2, separada por vírgula | — | — |

Resolve:

> **Componentes §0** — seed-componentes.md v2.00 · §0 · MD5 da fatia b307543f · byte a byte do canon · https://ds.seed.eng.br/canon/seed-componentes/00-fundamentos-de-componente-valem-para-todos-os/

3. **Foco sempre visível:** `box-shadow: var(--seed-focus-ring)` — nunca `outline: none` sem substituto. Nos campos, o nome acessível (`<label for>`) e a ordem em que erro e ajuda são anunciados vêm do form-field (§2.9).
