Ir ao conteúdo
SEED engenharia

Tokens · v1.40

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.

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 §0seed-componentes.md v2.00 · §0 · MD5 da fatia b307543fler no lugar ↗
  1. Toda cor via token semântico (--seed-action-primary), nunca primitivo, nunca hex. Dark mode vem de graça.
Tokens §1seed-tokens.md v1.40 · §1 · MD5 da fatia 56715422ler no lugar ↗
  • Componentes consomem semânticos, nunca primitivos, nunca hex.
Componentes §64seed-componentes.md v2.00 · §64 · MD5 da fatia c7911e59ler no lugar ↗
# 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.

seletorcondiçãoespecificidade (a, b, c)variáveislinha
:root—:root → (0, 1, 0)378181
[data-theme="dark"]—[data-theme="dark"] → (0, 1, 0)151969
:root:not([data-theme="light"])@media (prefers-color-scheme: dark):root:not([data-theme="light"]) → (0, 2, 0)1511180
:root@media (prefers-reduced-motion: reduce):root → (0, 1, 0)61383
: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)21408
:root, :root[data-theme]@media (prefers-reduced-transparency: reduce):root → (0, 1, 0) · :root[data-theme] → (0, 2, 0)21414
:root, :root[data-theme]@media (prefers-contrast: more):root → (0, 1, 0) · :root[data-theme] → (0, 2, 0)71420

Resolve:

Tokens §3dseed-tokens.md v1.40 · §3d · MD5 da fatia 9a593032ler no lugar ↗

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,…).

tokenclaro (:root)escuro ([data-theme="dark"])
--seed-shadow-modal0 16px 48px rgba(36,46,52,0.22)0 16px 48px rgba(0,0,0,0.60)
--seed-shadow-overlay0 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-raised0 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 §2seed-tokens.md v1.40 · §2 · MD5 da fatia 2b940971ler no lugar ↗

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.

tokenforma do valor (o maior dos temas)propriedade no canon (código)propriedade no registry (r/*.css)
--seed-focus-ringlista de 2, separada por vírgulabox-shadow (10×)—
--seed-font-expressivelista de 3, separada por vírgula——
--seed-font-monolista de 5, separada por vírgulafont-family (4×)—
--seed-font-sanslista de 6, separada por vírgulafont-family (4×)—
--seed-mat-canvaslista de 5, separada por vírgulabackground (1×)background (2×)
--seed-mat-glass-filter2 partes separadas por espaçobackdrop-filter (2×)backdrop-filter (2×)
--seed-mat-glass-shadowlista de 4, separada por vírgulabox-shadow (1×)box-shadow (2×)
--seed-mat-glass-shadow-hoverlista de 4, separada por vírgula—box-shadow (1×)
--seed-mat-metal-action-edgelista de 4, separada por vírgulabox-shadow (1×)box-shadow (1×)
--seed-mat-metal-edgelista de 5, separada por vírgulabox-shadow (1×)box-shadow (1×)
--seed-shadow-modal4 partes separadas por espaço——
--seed-shadow-overlaylista de 2, separada por vírgula——
--seed-shadow-raisedlista de 2, separada por vírgula——

Resolve:

Componentes §0seed-componentes.md v2.00 · §0 · MD5 da fatia b307543fler no lugar ↗
  1. 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).
Esc