@ksmv/ui-checks 0.22.1 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.23.2 - 2026-10-19
4
+
5
+ - **Nem a 0.23.0 nem a 0.23.1 foram publicadas.** A release do `@ksmv/ui-react` parou nas duas, na suíte de navegadores, e as do checks, que saem junto com ela, ficaram retidas no npm sem aprovação. Nada mudou no pacote desde então: de 0.22.1, atualize direto para a 0.23.2. As tags `checks-v0.23.0` e `checks-v0.23.1` existem no repositório, mas não têm versão no registro.
6
+ - **Contratos de tema 3.5.0 e de contraste 13.0.0** empacotados (#397 a #401). O de tema ganha 70 derivados opcionais (338 no total); o de contraste ganha 115 pares e altera um (451 no total). Nenhum par sai. Atualize o `@ksmv/ui-react` junto (ver o CHANGELOG dele).
7
+ - **O contrato de contraste passa ao `schemaVersion` 3:** um par pode medir a opacidade de um grupo. Com `opacity`, o par compõe o texto e o fundo esmaecidos sobre a superfície de fora (`over`), e `group` dá o fundo próprio do grupo; uma opacidade sem `over` é recusada. A versão anterior do `@ksmv/ui-checks` recusa o schema 3.
8
+ - **Quem usa `computedEvidence` precisa capturar a evidência de novo:** os dois contratos mudaram, e um par com opacidade exige o `group` e o `over` na evidência.
9
+
3
10
  ## 0.22.1 - 2026-10-17
4
11
 
5
12
  - **Contratos de tema 3.4.0 e de contraste 12.0.0** empacotados (#394). O de tema ganha o derivado opcional `--ui-surface-inverse-end`, a ponta do degradê da superfície inversa; o de contraste ganha 20 pares: o texto apagado sobre a superfície inversa (`inverse-muted-text`) e cada par de um derivado `-on-inverse` sobre a superfície medido também sobre a ponta (contexto `-end`). Nenhum par sai. Um tema que não declara nenhum dos dois tokens passa como antes.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ksmv/ui-checks
2
2
 
3
- Verificações de qualidade para o contrato semântico `--ui-*` de `@ksmv/ui-react`: completude dos temas, contraste dos pares do contrato (ações, estados, campos, controles e, desde o contrato de contraste `3.0.0`, o texto base sobre as superfícies e, desde o `4.0.0`, a cor de link e, desde o `5.0.0`, a borda das ações e, desde o `6.0.0`, as variantes de ação da 0.17.0 e o botão de texto e, desde o `7.0.0`, os botões da casa da 0.18.1 e o hover por tom do botão de texto e, desde o `7.1.0`, o texto de hover dos botões suaves e do contorno de aviso e, desde o `9.0.0`, o texto, o limite de controle, o link, o botão de texto e o fantasma sobre a superfície inversa, a paleta categórica, a descrição do diálogo, o giro contra a superfície, o aviso e o rótulo de seção, o item de menu e o cartão de rádio, o cabeçalho da tabela e o grupo de alternância, e o selo cheio, a bolinha sobre a superfície inversa e a barra e, desde o `10.0.0`, o fundo do botão de ícone contornado, a cor da ajuda e a caixa de seleção e, desde o `11.0.0`, o botão pressionado contra as superfícies, a linha da lista com tom, o selo com marca à frente, o cabeçalho de página e a métrica, os passos e a navegação lateral, a conversa e a mídia, a escolha de horário e de dia, a grade de calendário e o quadro e, desde o `12.0.0`, o texto apagado sobre a superfície inversa e cada par da superfície inversa medido também sobre a ponta do degradê), prefixo dos tokens, cores literais fora dos arquivos de tema e evidência calculada vinculada às fontes.
3
+ Verificações de qualidade para o contrato semântico `--ui-*` de `@ksmv/ui-react`: completude dos temas, contraste dos pares do contrato (ações, estados, campos, controles e, desde o contrato de contraste `3.0.0`, o texto base sobre as superfícies e, desde o `4.0.0`, a cor de link e, desde o `5.0.0`, a borda das ações e, desde o `6.0.0`, as variantes de ação da 0.17.0 e o botão de texto e, desde o `7.0.0`, os botões da casa da 0.18.1 e o hover por tom do botão de texto e, desde o `7.1.0`, o texto de hover dos botões suaves e do contorno de aviso e, desde o `9.0.0`, o texto, o limite de controle, o link, o botão de texto e o fantasma sobre a superfície inversa, a paleta categórica, a descrição do diálogo, o giro contra a superfície, o aviso e o rótulo de seção, o item de menu e o cartão de rádio, o cabeçalho da tabela e o grupo de alternância, e o selo cheio, a bolinha sobre a superfície inversa e a barra e, desde o `10.0.0`, o fundo do botão de ícone contornado, a cor da ajuda e a caixa de seleção e, desde o `11.0.0`, o botão pressionado contra as superfícies, a linha da lista com tom, o selo com marca à frente, o cabeçalho de página e a métrica, os passos e a navegação lateral, a conversa e a mídia, a escolha de horário e de dia, a grade de calendário e o quadro e, desde o `12.0.0`, o texto apagado sobre a superfície inversa e cada par da superfície inversa medido também sobre a ponta do degradê e, desde o `13.0.0`, a caixa redonda, a linha de lista com o hover, a feita e o título como link, e os passos por fazer, com a opacidade de grupo, e a mídia na bolha, o documento por tom e o áudio), prefixo dos tokens, cores literais fora dos arquivos de tema e evidência calculada vinculada às fontes.
4
4
 
5
5
  A regra `css/no-literal-color` recusa cor literal fora dos temas configurados. Desde a 0.22.0, ela aceita uma exceção: o fallback de `var(--ui-x, <cor>)`, quando a cor é, texto por texto, o padrão literal que o contrato de tema dá à própria `--ui-x` (como `var(--ui-media-overlay-bg, rgb(0 0 0 / 70%))`). Quem escreve esse fallback implementa o contrato. Outra cor, ainda que equivalente, uma cor fora do `var()` ou o fallback de um token sem padrão literal continuam reprovados.
6
6
 
@@ -97,6 +97,8 @@ config Invalid computed theme evidence in artifacts/theme-evidence.json: the the
97
97
 
98
98
  Depois de atualizar o pacote React, a mensagem traz a versão do contrato da captura e a do contrato instalado; quando faltam tokens ou resta `var(...)`, traz os nomes dos tokens, em ordem, até dez, e a contagem dos demais. A mensagem não repete valores do produto: aparecem só o caminho da evidência e o nome do tema como estão na configuração, nomes de tokens e versões de contrato. Falha ao resolver os contratos também é relatada com a própria causa (`Unable to resolve the configured contract source.`, `Unsupported theme contract.` ou `Unsupported contrast manifest.`).
99
99
 
100
+ Desde o contrato de contraste `13.0.0` (`schemaVersion` 3), um par pode medir a opacidade de um grupo, como a linha feita a 0,75: `opacity` dá a opacidade, `over` a superfície opaca fora do grupo e, quando o texto está numa camada dentro dele, `group` o fundo do elemento que tem a opacidade. A regra pinta o grupo guardando o alfa e compõe o resultado, com a opacidade, sobre `over`; um degradê (um token `-end`) em qualquer camada é medido ao longo dele, e vale o pior ponto; um par sem `over`, com uma camada que não resolve ou com a superfície de fora translúcida fica sem cobertura, nunca aprovado. A evidência calculada precisa trazer todas as camadas de cada par (o texto, o fundo, `over` e `group`).
101
+
100
102
  Tokens opcionais, os derivados e os de marca, podem ficar vazios na captura: é assim que o navegador informa um token que o tema não declarou. Um derivado vazio assume o valor do token de origem, que é o padrão documentado no contrato. Quando a origem também é um derivado, como no texto do hover do contorno de aviso (`--ui-action-warning-outline-text-hover` deriva de `--ui-action-warning-outline-text`, que deriva do contorno de perigo), a cadeia é seguida até um token declarado. Um derivado que é ponta de um par de contraste, como `--ui-control-checked-bg`, também pode ficar vazio; a captura precisa então do token em que a cadeia dele termina. Sem evidência, `tokens/contrast` segue a mesma cadeia: um derivado que o tema não declara é avaliado pelo valor da origem, ou pelo padrão literal do contrato (`transparent`, uma cor). Um par com `over` mede um fundo translúcido composto sobre aquela superfície; sem `over`, um fundo translúcido continua cobertura pendente.
101
103
 
102
104
  Desde o contrato de tema `3.0.0`, a forma é da biblioteca: a regra `themes/no-shape` aponta, como violação, cada token da lista `shape` do contrato que um seletor de tema declarar. Quem migra de um tema que declarava forma pode carregar essas violações na baseline enquanto tira as linhas.
package/dist/color.d.ts CHANGED
@@ -18,6 +18,8 @@ export declare function containsLiteralColor(value: string, contractDefaults?: R
18
18
  export declare function referencedTokens(value: string): string[];
19
19
  export declare function unresolvedTokenReferences(value: string, tokens: ReadonlyMap<string, string>, stack?: ReadonlySet<string>): string[];
20
20
  export declare function resolveColor(value: string | undefined, tokens: ReadonlyMap<string, string>, stack?: ReadonlySet<string>): ColorResolution;
21
+ export declare function paintOver(top: RgbColor, bottom: RgbColor): RgbColor;
22
+ export declare function faded(color: RgbColor, opacity: number): RgbColor;
21
23
  export declare function composite(foreground: RgbColor, background: RgbColor): RgbColor;
22
24
  export declare function mix(start: RgbColor, end: RgbColor, at: number): RgbColor[];
23
25
  export declare function contrastRatio(foreground: RgbColor, background: RgbColor): number | undefined;
package/dist/color.js CHANGED
@@ -187,6 +187,19 @@ export function resolveColor(value, tokens, stack = new Set()) {
187
187
  const color = parsedColor(value.trim());
188
188
  return color ? { color, status: "resolved" } : { status: "uncovered" };
189
189
  }
190
+ // One color painted over another, keeping the alpha of the result (Porter-Duff
191
+ // source-over), for a layer that is itself translucent: a group's paint
192
+ // before its opacity. composite() below assumes an opaque background.
193
+ export function paintOver(top, bottom) {
194
+ const [topAlpha, bottomAlpha] = [top.alpha ?? 1, bottom.alpha ?? 1];
195
+ const alpha = topAlpha + bottomAlpha * (1 - topAlpha);
196
+ const channel = (upper, lower) => alpha === 0 ? 0 : (upper * topAlpha + lower * bottomAlpha * (1 - topAlpha)) / alpha;
197
+ return { alpha, b: channel(top.b, bottom.b), g: channel(top.g, bottom.g), mode: "rgb", r: channel(top.r, bottom.r) };
198
+ }
199
+ // A color under a group's opacity: its alpha scaled by it.
200
+ export function faded(color, opacity) {
201
+ return { ...color, alpha: (color.alpha ?? 1) * opacity };
202
+ }
190
203
  export function composite(foreground, background) {
191
204
  const alpha = foreground.alpha ?? 1;
192
205
  return {
@@ -29,13 +29,21 @@ export interface ContrastPair {
29
29
  context: string;
30
30
  foreground: string;
31
31
  minimum: number;
32
- /** The surface under a background that may be translucent (schema 2). */
32
+ /** The background of the element that has the opacity, when the text sits on
33
+ * a layer inside it, as a marker inside a card (schema 3). Without it, the
34
+ * background is the element's own and is painted once. */
35
+ group?: string;
36
+ /** The opacity of a group around the pair, as a done row's 0.75 (schema 3).
37
+ * It needs the opaque surface outside the group in over. */
38
+ opacity?: number;
39
+ /** The surface under a background that may be translucent (schema 2), or
40
+ * the surface outside a group with an opacity (schema 3). */
33
41
  over?: string;
34
42
  }
35
43
  export interface ContrastManifest {
36
44
  contractVersion: string;
37
45
  pairs: ContrastPair[];
38
- schemaVersion: 2;
46
+ schemaVersion: 2 | 3;
39
47
  }
40
48
  export interface ContractIdentity {
41
49
  contractVersion: string;
package/dist/contracts.js CHANGED
@@ -96,18 +96,20 @@ export function parseContrastManifest(content) {
96
96
  }
97
97
  if (!isRecord(value) ||
98
98
  !exactKeys(value, ["contractVersion", "pairs", "schemaVersion"]) ||
99
- value.schemaVersion !== 2 ||
99
+ (value.schemaVersion !== 2 && value.schemaVersion !== 3) ||
100
100
  typeof value.contractVersion !== "string" ||
101
101
  !semverPattern.test(value.contractVersion) ||
102
102
  !Array.isArray(value.pairs)) {
103
103
  throw new CheckSetupError("Unsupported contrast manifest.");
104
104
  }
105
+ const schema = value.schemaVersion;
105
106
  const identities = [];
106
107
  for (const pair of value.pairs) {
107
108
  if (!isRecord(pair) ||
108
109
  !(exactKeys(pair, ["background", "context", "foreground", "minimum"]) ||
109
110
  (exactKeys(pair, ["background", "context", "foreground", "minimum", "over"]) &&
110
- typeof pair.over === "string" && tokenNamePattern.test(pair.over))) ||
111
+ typeof pair.over === "string" && tokenNamePattern.test(pair.over)) ||
112
+ (schema === 3 && isGroupPair(pair))) ||
111
113
  typeof pair.background !== "string" ||
112
114
  !tokenNamePattern.test(pair.background) ||
113
115
  typeof pair.foreground !== "string" ||
@@ -119,13 +121,26 @@ export function parseContrastManifest(content) {
119
121
  pair.minimum <= 0) {
120
122
  throw new CheckSetupError("Unsupported contrast manifest.");
121
123
  }
122
- identities.push(`${pair.context}\u0000${pair.foreground}\u0000${pair.background}\u0000${pair.over ?? ""}`);
124
+ identities.push(`${pair.context}\u0000${pair.foreground}\u0000${pair.background}\u0000${pair.over ?? ""}\u0000${pair.group ?? ""}\u0000${pair.opacity ?? ""}`);
123
125
  }
124
126
  if (new Set(identities).size !== identities.length) {
125
127
  throw new CheckSetupError("Unsupported contrast manifest.");
126
128
  }
127
129
  return value;
128
130
  }
131
+ // A pair under a group's opacity (schema 3): the opacity in (0, 1], the
132
+ // opaque surface outside the group in over, and the group's own background
133
+ // when the text sits on a layer inside it.
134
+ function isGroupPair(pair) {
135
+ const keys = ["background", "context", "foreground", "minimum", "opacity", "over"];
136
+ return ((exactKeys(pair, keys) || (exactKeys(pair, [...keys, "group"]) && typeof pair.group === "string" && tokenNamePattern.test(pair.group))) &&
137
+ typeof pair.opacity === "number" &&
138
+ Number.isFinite(pair.opacity) &&
139
+ pair.opacity > 0 &&
140
+ pair.opacity <= 1 &&
141
+ typeof pair.over === "string" &&
142
+ tokenNamePattern.test(pair.over));
143
+ }
129
144
  function identity(content, contractVersion) {
130
145
  return {
131
146
  contractVersion,
@@ -1,6 +1,6 @@
1
1
  {
2
- "contractVersion": "12.0.0",
3
- "schemaVersion": 2,
2
+ "contractVersion": "13.0.0",
3
+ "schemaVersion": 3,
4
4
  "pairs": [
5
5
  { "foreground": "--ui-action-primary-text", "background": "--ui-action-primary-bg", "minimum": 4.5, "context": "normal-text" },
6
6
  { "foreground": "--ui-action-primary-text", "background": "--ui-action-primary-bg-hover", "minimum": 4.5, "context": "normal-text-hover" },
@@ -259,7 +259,7 @@
259
259
  { "foreground": "--ui-side-nav-count-text-on-inverse", "background": "--ui-side-nav-count-bg-on-inverse", "over": "--ui-surface-inverse", "minimum": 4.5, "context": "side-nav-count-text-on-inverse" },
260
260
  { "foreground": "--ui-steps-current", "background": "--ui-steps-current-bg", "over": "--ui-surface", "minimum": 3, "context": "steps-current-boundary" },
261
261
  { "foreground": "--ui-steps-current", "background": "--ui-surface", "minimum": 3, "context": "steps-current-border" },
262
- { "foreground": "--ui-steps-current", "background": "--ui-surface", "minimum": 3, "context": "steps-complete-mark" },
262
+ { "foreground": "--ui-steps-current", "background": "--ui-steps-complete-marker-bg", "minimum": 3, "context": "steps-complete-mark" },
263
263
  { "foreground": "--ui-steps-track-filled", "background": "--ui-surface", "minimum": 3, "context": "steps-track-filled" },
264
264
  { "foreground": "--ui-side-nav-count-dot", "background": "--ui-surface", "minimum": 3, "context": "side-nav-count-dot" },
265
265
  { "foreground": "--ui-message-incoming-text", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-incoming-text" },
@@ -337,6 +337,121 @@
337
337
  { "foreground": "--ui-button-pressed-bg-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "button-pressed-boundary-on-inverse-end" },
338
338
  { "foreground": "--ui-button-pressed-bg-hover-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "button-pressed-boundary-hover-on-inverse-end" },
339
339
  { "foreground": "--ui-side-nav-count-text-on-inverse", "background": "--ui-side-nav-count-bg-on-inverse", "over": "--ui-surface-inverse-end", "minimum": 4.5, "context": "side-nav-count-text-on-inverse-end" },
340
- { "foreground": "--ui-text-muted-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-muted-text-end" }
340
+ { "foreground": "--ui-text-muted-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-muted-text-end" },
341
+ { "foreground": "--ui-checkbox-border", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface", "minimum": 3, "context": "checkbox-circle-ring" },
342
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface", "minimum": 3, "context": "checkbox-circle-ring-hover" },
343
+ { "foreground": "--ui-checkbox-border", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface-raised", "minimum": 3, "context": "checkbox-circle-ring-raised" },
344
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface-raised", "minimum": 3, "context": "checkbox-circle-ring-hover-raised" },
345
+ { "foreground": "--ui-checkbox-border", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface-subtle", "minimum": 3, "context": "checkbox-circle-ring-subtle" },
346
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "over": "--ui-surface-subtle", "minimum": 3, "context": "checkbox-circle-ring-hover-subtle" },
347
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-success-hover" },
348
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-success-hover" },
349
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-warning-hover" },
350
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-warning-hover" },
351
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-danger-hover" },
352
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-danger-hover" },
353
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-info-hover" },
354
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-info-hover" },
355
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-success-hover-raised" },
356
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-success-hover-raised" },
357
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-warning-hover-raised" },
358
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-warning-hover-raised" },
359
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-danger-hover-raised" },
360
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-danger-hover-raised" },
361
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-info-hover-raised" },
362
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-info-hover-raised" },
363
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover" },
364
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-success-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-success" },
365
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-success-hover" },
366
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-warning-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-warning" },
367
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-warning-hover" },
368
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-danger" },
369
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-danger-hover" },
370
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-info-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-info" },
371
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-link-hover-info-hover" },
372
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-success-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-success-raised" },
373
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-success-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-success-hover-raised" },
374
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-warning-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-warning-raised" },
375
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-warning-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-warning-hover-raised" },
376
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-danger-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-danger-raised" },
377
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-danger-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-danger-hover-raised" },
378
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-info-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-info-raised" },
379
+ { "foreground": "--ui-list-item-title-hover", "background": "--ui-list-item-info-bg-hover", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-link-hover-info-hover-raised" },
380
+ { "foreground": "--ui-list-item-done-title", "background": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-title" },
381
+ { "foreground": "--ui-text-secondary", "background": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-description" },
382
+ { "foreground": "--ui-control-checked-bg", "background": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-disc" },
383
+ { "foreground": "--ui-checkbox-mark", "background": "--ui-control-checked-bg", "group": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-mark" },
384
+ { "foreground": "--ui-list-item-done-title", "background": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-title-raised" },
385
+ { "foreground": "--ui-text-secondary", "background": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-description-raised" },
386
+ { "foreground": "--ui-control-checked-bg", "background": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-disc-raised" },
387
+ { "foreground": "--ui-checkbox-mark", "background": "--ui-control-checked-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-mark-raised" },
388
+ { "foreground": "--ui-list-item-done-title", "background": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-title-subtle" },
389
+ { "foreground": "--ui-text-secondary", "background": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 4.5, "context": "list-item-done-description-subtle" },
390
+ { "foreground": "--ui-control-checked-bg", "background": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-disc-subtle" },
391
+ { "foreground": "--ui-checkbox-mark", "background": "--ui-control-checked-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-mark-subtle" },
392
+ { "foreground": "--ui-list-item-done-ring", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring" },
393
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring-hover" },
394
+ { "foreground": "--ui-list-item-done-ring", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring-raised" },
395
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-raised", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring-hover-raised" },
396
+ { "foreground": "--ui-list-item-done-ring", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring-subtle" },
397
+ { "foreground": "--ui-checkbox-circle-hover", "background": "--ui-checkbox-circle-bg", "group": "--ui-surface-subtle", "over": "--ui-surface-subtle", "opacity": 0.75, "minimum": 3, "context": "list-item-done-circle-ring-hover-subtle" },
398
+ { "foreground": "--ui-steps-upcoming-title", "background": "--ui-surface", "over": "--ui-surface", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-title" },
399
+ { "foreground": "--ui-steps-upcoming-description", "background": "--ui-surface", "over": "--ui-surface", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-description" },
400
+ { "foreground": "--ui-steps-upcoming-marker-text", "background": "--ui-steps-upcoming-marker-bg", "group": "--ui-surface", "over": "--ui-surface", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-marker" },
401
+ { "foreground": "--ui-steps-upcoming-title", "background": "--ui-surface", "over": "--ui-surface-raised", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-title-raised" },
402
+ { "foreground": "--ui-steps-upcoming-description", "background": "--ui-surface", "over": "--ui-surface-raised", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-description-raised" },
403
+ { "foreground": "--ui-steps-upcoming-marker-text", "background": "--ui-steps-upcoming-marker-bg", "group": "--ui-surface", "over": "--ui-surface-raised", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-marker-raised" },
404
+ { "foreground": "--ui-steps-upcoming-title", "background": "--ui-surface", "over": "--ui-surface-subtle", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-title-subtle" },
405
+ { "foreground": "--ui-steps-upcoming-description", "background": "--ui-surface", "over": "--ui-surface-subtle", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-description-subtle" },
406
+ { "foreground": "--ui-steps-upcoming-marker-text", "background": "--ui-steps-upcoming-marker-bg", "group": "--ui-surface", "over": "--ui-surface-subtle", "opacity": 0.7, "minimum": 4.5, "context": "steps-upcoming-marker-subtle" },
407
+ { "foreground": "--ui-message-incoming-document-text", "background": "--ui-message-incoming-document-bg", "over": "--ui-message-incoming-bg", "minimum": 4.5, "context": "message-incoming-document-text" },
408
+ { "foreground": "--ui-message-incoming-document-meta", "background": "--ui-message-incoming-document-bg", "over": "--ui-message-incoming-bg", "minimum": 4.5, "context": "message-incoming-document-meta" },
409
+ { "foreground": "--ui-message-incoming-document-download-icon", "background": "--ui-message-incoming-document-download-bg", "group": "--ui-message-incoming-document-bg", "over": "--ui-message-incoming-bg", "opacity": 1, "minimum": 3, "context": "message-incoming-document-download" },
410
+ { "foreground": "--ui-message-incoming-document-download-icon-hover", "background": "--ui-message-incoming-document-download-bg-hover", "group": "--ui-message-incoming-document-bg", "over": "--ui-message-incoming-bg", "opacity": 1, "minimum": 3, "context": "message-incoming-document-download-hover" },
411
+ { "foreground": "--ui-message-assistant-document-text", "background": "--ui-message-assistant-document-bg", "over": "--ui-message-assistant-bg", "minimum": 4.5, "context": "message-assistant-document-text" },
412
+ { "foreground": "--ui-message-assistant-document-meta", "background": "--ui-message-assistant-document-bg", "over": "--ui-message-assistant-bg", "minimum": 4.5, "context": "message-assistant-document-meta" },
413
+ { "foreground": "--ui-message-assistant-document-download-icon", "background": "--ui-message-assistant-document-download-bg", "group": "--ui-message-assistant-document-bg", "over": "--ui-message-assistant-bg", "opacity": 1, "minimum": 3, "context": "message-assistant-document-download" },
414
+ { "foreground": "--ui-message-assistant-document-download-icon-hover", "background": "--ui-message-assistant-document-download-bg-hover", "group": "--ui-message-assistant-document-bg", "over": "--ui-message-assistant-bg", "opacity": 1, "minimum": 3, "context": "message-assistant-document-download-hover" },
415
+ { "foreground": "--ui-message-outgoing-document-text", "background": "--ui-message-outgoing-document-bg", "over": "--ui-message-outgoing-bg", "minimum": 4.5, "context": "message-outgoing-document-text" },
416
+ { "foreground": "--ui-message-outgoing-document-meta", "background": "--ui-message-outgoing-document-bg", "over": "--ui-message-outgoing-bg", "minimum": 4.5, "context": "message-outgoing-document-meta" },
417
+ { "foreground": "--ui-message-outgoing-document-download-icon", "background": "--ui-message-outgoing-document-download-bg", "group": "--ui-message-outgoing-document-bg", "over": "--ui-message-outgoing-bg", "opacity": 1, "minimum": 3, "context": "message-outgoing-document-download" },
418
+ { "foreground": "--ui-message-outgoing-document-download-icon-hover", "background": "--ui-message-outgoing-document-download-bg-hover", "group": "--ui-message-outgoing-document-bg", "over": "--ui-message-outgoing-bg", "opacity": 1, "minimum": 3, "context": "message-outgoing-document-download-hover" },
419
+ { "foreground": "--ui-media-enlarge-icon", "background": "--ui-media-enlarge-bg", "group": "--ui-media-thumbnail-veil", "over": "--ui-surface", "opacity": 1, "minimum": 3, "context": "media-enlarge-icon" },
420
+ { "foreground": "--ui-message-incoming-audio-play-icon", "background": "--ui-message-incoming-audio-play-bg", "over": "--ui-message-incoming-bg", "minimum": 3, "context": "message-incoming-audio-play" },
421
+ { "foreground": "--ui-message-incoming-audio-wave-played", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 3, "context": "message-incoming-audio-wave-played" },
422
+ { "foreground": "--ui-message-incoming-audio-wave-unplayed", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 3, "context": "message-incoming-audio-wave-unplayed" },
423
+ { "foreground": "--ui-message-incoming-audio-playhead", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 3, "context": "message-incoming-audio-playhead" },
424
+ { "foreground": "--ui-message-incoming-audio-time", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-incoming-audio-time" },
425
+ { "foreground": "--ui-message-incoming-audio-speed-text", "background": "--ui-message-incoming-audio-speed-bg", "over": "--ui-message-incoming-bg", "minimum": 4.5, "context": "message-incoming-audio-speed" },
426
+ { "foreground": "--ui-message-assistant-audio-play-icon", "background": "--ui-message-assistant-audio-play-bg", "over": "--ui-message-assistant-bg", "minimum": 3, "context": "message-assistant-audio-play" },
427
+ { "foreground": "--ui-message-assistant-audio-wave-played", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 3, "context": "message-assistant-audio-wave-played" },
428
+ { "foreground": "--ui-message-assistant-audio-wave-unplayed", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 3, "context": "message-assistant-audio-wave-unplayed" },
429
+ { "foreground": "--ui-message-assistant-audio-playhead", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 3, "context": "message-assistant-audio-playhead" },
430
+ { "foreground": "--ui-message-assistant-audio-time", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-assistant-audio-time" },
431
+ { "foreground": "--ui-message-assistant-audio-speed-text", "background": "--ui-message-assistant-audio-speed-bg", "over": "--ui-message-assistant-bg", "minimum": 4.5, "context": "message-assistant-audio-speed" },
432
+ { "foreground": "--ui-message-outgoing-audio-play-icon", "background": "--ui-message-outgoing-audio-play-bg", "over": "--ui-message-outgoing-bg", "minimum": 3, "context": "message-outgoing-audio-play" },
433
+ { "foreground": "--ui-message-outgoing-audio-wave-played", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 3, "context": "message-outgoing-audio-wave-played" },
434
+ { "foreground": "--ui-message-outgoing-audio-wave-unplayed", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 3, "context": "message-outgoing-audio-wave-unplayed" },
435
+ { "foreground": "--ui-message-outgoing-audio-playhead", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 3, "context": "message-outgoing-audio-playhead" },
436
+ { "foreground": "--ui-message-outgoing-audio-time", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-outgoing-audio-time" },
437
+ { "foreground": "--ui-message-outgoing-audio-speed-text", "background": "--ui-message-outgoing-audio-speed-bg", "over": "--ui-message-outgoing-bg", "minimum": 4.5, "context": "message-outgoing-audio-speed" },
438
+ { "foreground": "--ui-message-incoming-audio-play-icon", "background": "--ui-message-incoming-audio-play-bg", "over": "--ui-surface", "minimum": 3, "context": "audio-play" },
439
+ { "foreground": "--ui-message-incoming-audio-wave-played", "background": "--ui-surface", "minimum": 3, "context": "audio-wave-played" },
440
+ { "foreground": "--ui-message-incoming-audio-wave-unplayed", "background": "--ui-surface", "minimum": 3, "context": "audio-wave-unplayed" },
441
+ { "foreground": "--ui-message-incoming-audio-playhead", "background": "--ui-surface", "minimum": 3, "context": "audio-playhead" },
442
+ { "foreground": "--ui-message-incoming-audio-time", "background": "--ui-surface", "minimum": 4.5, "context": "audio-time" },
443
+ { "foreground": "--ui-message-incoming-audio-speed-text", "background": "--ui-message-incoming-audio-speed-bg", "over": "--ui-surface", "minimum": 4.5, "context": "audio-speed" },
444
+ { "foreground": "--ui-message-incoming-audio-play-icon", "background": "--ui-message-incoming-audio-play-bg", "over": "--ui-surface-raised", "minimum": 3, "context": "audio-play-raised" },
445
+ { "foreground": "--ui-message-incoming-audio-wave-played", "background": "--ui-surface-raised", "minimum": 3, "context": "audio-wave-played-raised" },
446
+ { "foreground": "--ui-message-incoming-audio-wave-unplayed", "background": "--ui-surface-raised", "minimum": 3, "context": "audio-wave-unplayed-raised" },
447
+ { "foreground": "--ui-message-incoming-audio-playhead", "background": "--ui-surface-raised", "minimum": 3, "context": "audio-playhead-raised" },
448
+ { "foreground": "--ui-message-incoming-audio-time", "background": "--ui-surface-raised", "minimum": 4.5, "context": "audio-time-raised" },
449
+ { "foreground": "--ui-message-incoming-audio-speed-text", "background": "--ui-message-incoming-audio-speed-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "audio-speed-raised" },
450
+ { "foreground": "--ui-message-incoming-audio-play-icon", "background": "--ui-message-incoming-audio-play-bg", "over": "--ui-surface-subtle", "minimum": 3, "context": "audio-play-subtle" },
451
+ { "foreground": "--ui-message-incoming-audio-wave-played", "background": "--ui-surface-subtle", "minimum": 3, "context": "audio-wave-played-subtle" },
452
+ { "foreground": "--ui-message-incoming-audio-wave-unplayed", "background": "--ui-surface-subtle", "minimum": 3, "context": "audio-wave-unplayed-subtle" },
453
+ { "foreground": "--ui-message-incoming-audio-playhead", "background": "--ui-surface-subtle", "minimum": 3, "context": "audio-playhead-subtle" },
454
+ { "foreground": "--ui-message-incoming-audio-time", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "audio-time-subtle" },
455
+ { "foreground": "--ui-message-incoming-audio-speed-text", "background": "--ui-message-incoming-audio-speed-bg", "over": "--ui-surface-subtle", "minimum": 4.5, "context": "audio-speed-subtle" }
341
456
  ]
342
457
  }
@@ -1,5 +1,5 @@
1
1
  import { isComputedPropertyName, isIdentifier, isJsxAttribute, isJsxExpression, isObjectLiteralExpression, isPropertyAssignment, isJsxSpreadAttribute, isStringLiteralLikeNode, } from "typescript/unstable/ast";
2
- import { composite, contrastRatio, isColorBearingProperty, mix, resolveColor, } from "../color.js";
2
+ import { composite, contrastRatio, faded, isColorBearingProperty, mix, paintOver, resolveColor, } from "../color.js";
3
3
  import { parseContrastManifest, } from "../contracts.js";
4
4
  import { createThemeCollector } from "./theme.js";
5
5
  // The end of a gradient (#394): a token named <start>-end, when the start is
@@ -21,6 +21,53 @@ function resolveAlong(token, tokens) {
21
21
  .flat()
22
22
  .map((color) => ({ color, status: "resolved" }));
23
23
  }
24
+ function groupRatio(pair, tokens) {
25
+ const opacity = pair.opacity;
26
+ const foreground = resolveColor(tokens.get(pair.foreground), tokens);
27
+ const backgrounds = resolveAlong(pair.background, tokens);
28
+ const groups = pair.group === undefined ? [undefined] : resolveAlong(pair.group, tokens);
29
+ const surfaces = pair.over === undefined ? [] : resolveAlong(pair.over, tokens);
30
+ if (foreground.status !== "resolved" ||
31
+ [...backgrounds, ...groups, ...surfaces].some((layer) => layer !== undefined && layer.status !== "resolved") ||
32
+ surfaces.length === 0) {
33
+ return "uncovered";
34
+ }
35
+ let worst = Infinity;
36
+ for (const background of backgrounds) {
37
+ for (const group of groups) {
38
+ if (background.status !== "resolved" || group?.status === "uncovered")
39
+ return "uncovered";
40
+ const inner = group?.status === "resolved" ? paintOver(background.color, group.color) : background.color;
41
+ const text = paintOver(foreground.color, inner);
42
+ for (const surface of surfaces) {
43
+ if (surface.status !== "resolved" || (surface.color.alpha ?? 1) < 1)
44
+ return "translucent";
45
+ const seenBackground = composite(faded(inner, opacity), surface.color);
46
+ const seenText = composite(faded(text, opacity), surface.color);
47
+ const ratio = contrastRatio(seenText, seenBackground);
48
+ if (ratio === undefined)
49
+ return "translucent";
50
+ worst = Math.min(worst, ratio);
51
+ }
52
+ }
53
+ }
54
+ return worst;
55
+ }
56
+ function reportPair(result, location, themeName, pair, ratio) {
57
+ // A group pair (schema 3) may differ from another only by its group, the
58
+ // surface outside or the opacity: they are in its fingerprint. The pairs
59
+ // before schema 3 keep theirs, so a baseline stays valid.
60
+ const nodeKind = `contrast-pair:${pair.context}:${pair.foreground}:${pair.background}:${pair.group ?? ""}:${pair.over}:${pair.opacity}`;
61
+ if (ratio === "uncovered") {
62
+ result.uncovered.push({ ...location, message: `Contrast pair ${pair.context} could not be resolved.`, nodeKind, rule: "tokens/contrast", required: true });
63
+ }
64
+ else if (ratio === "translucent") {
65
+ result.uncovered.push({ ...location, message: `Contrast pair ${pair.context} requires a known opaque background.`, nodeKind, rule: "tokens/contrast", required: true });
66
+ }
67
+ else if (ratio < pair.minimum) {
68
+ result.violations.push({ ...location, anchor: themeName, message: `Contrast pair ${pair.context} is below ${pair.minimum}:1.`, nodeKind, rule: "tokens/contrast" });
69
+ }
70
+ }
24
71
  export async function loadContrastManifest(url) {
25
72
  const { readFile } = await import("node:fs/promises");
26
73
  const packaged = url ?? new URL("../contrast-contract.json", import.meta.url);
@@ -117,8 +164,9 @@ export function createTokenContrastRule({ computed = {}, defaults = {}, derived
117
164
  for (const theme of collector.themes) {
118
165
  const location = findingLocation(theme);
119
166
  const computedTheme = computed[theme.name] ?? {};
120
- const hasCompleteComputedEvidence = manifest.pairs.every(({ background, foreground }) => computedTheme[background] !== undefined &&
121
- computedTheme[foreground] !== undefined);
167
+ // Every layer a pair reads: the text, its background, the surface
168
+ // under a translucent one and a group's background.
169
+ const hasCompleteComputedEvidence = manifest.pairs.every((pair) => [pair.foreground, pair.background, pair.over, pair.group].every((token) => token === undefined || computedTheme[token] !== undefined));
122
170
  if (theme.overlapping && !hasCompleteComputedEvidence) {
123
171
  result.uncovered.push({
124
172
  ...location,
@@ -130,7 +178,7 @@ export function createTokenContrastRule({ computed = {}, defaults = {}, derived
130
178
  continue;
131
179
  }
132
180
  const missingComputedEndpoints = theme.unresolved
133
- ? manifest.pairs.flatMap(({ background, foreground }) => [background, foreground].filter((token) => computedTheme[token] === undefined))
181
+ ? manifest.pairs.flatMap(({ background, foreground, group, over }) => [background, foreground, over, group].filter((token) => token !== undefined && computedTheme[token] === undefined))
134
182
  : [];
135
183
  if (missingComputedEndpoints.length > 0) {
136
184
  result.uncovered.push({
@@ -163,6 +211,11 @@ export function createTokenContrastRule({ computed = {}, defaults = {}, derived
163
211
  tokens.set(token, value);
164
212
  }
165
213
  for (const pair of manifest.pairs) {
214
+ if (pair.opacity !== undefined) {
215
+ const ratio = groupRatio(pair, tokens);
216
+ reportPair(result, location, theme.name, pair, ratio);
217
+ continue;
218
+ }
166
219
  const foreground = resolveColor(tokens.get(pair.foreground), tokens);
167
220
  // Each point of the background, a gradient's included, and of the
168
221
  // surface under it; the pair is as good as its worst point.
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "3.4.0",
2
+ "contractVersion": "3.5.0",
3
3
  "schemaVersion": 2,
4
4
  "prefix": "--ui-",
5
5
  "required": [
@@ -321,7 +321,77 @@
321
321
  "--ui-board-card-border-hover": { "kind": "token", "token": "--ui-action-primary-bg" },
322
322
  "--ui-board-drop-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
323
323
  "--ui-board-drop-border": { "kind": "token", "token": "--ui-action-primary-bg" },
324
- "--ui-surface-inverse-end": { "kind": "token", "token": "--ui-surface-inverse" }
324
+ "--ui-surface-inverse-end": { "kind": "token", "token": "--ui-surface-inverse" },
325
+ "--ui-checkbox-circle-bg": { "kind": "literal", "value": "transparent" },
326
+ "--ui-checkbox-circle-hover": { "kind": "token", "token": "--ui-control-checked-bg" },
327
+ "--ui-list-item-border-hover": { "kind": "token", "token": "--ui-border-strong" },
328
+ "--ui-list-item-success-bg-hover": { "kind": "token", "token": "--ui-list-item-success-bg" },
329
+ "--ui-list-item-warning-bg-hover": { "kind": "token", "token": "--ui-list-item-warning-bg" },
330
+ "--ui-list-item-danger-bg-hover": { "kind": "token", "token": "--ui-list-item-danger-bg" },
331
+ "--ui-list-item-info-bg-hover": { "kind": "token", "token": "--ui-list-item-info-bg" },
332
+ "--ui-list-item-shadow-color": { "kind": "literal", "value": "transparent" },
333
+ "--ui-list-item-done-border": { "kind": "token", "token": "--ui-border" },
334
+ "--ui-list-item-done-title": { "kind": "token", "token": "--ui-text-secondary" },
335
+ "--ui-list-item-done-ring": { "kind": "token", "token": "--ui-text-secondary" },
336
+ "--ui-list-item-title-hover": { "kind": "token", "token": "--ui-link-text" },
337
+ "--ui-steps-complete-marker-bg": { "kind": "token", "token": "--ui-surface" },
338
+ "--ui-steps-upcoming-title": { "kind": "token", "token": "--ui-text-primary" },
339
+ "--ui-steps-upcoming-description": { "kind": "token", "token": "--ui-text-primary" },
340
+ "--ui-steps-upcoming-marker-bg": { "kind": "token", "token": "--ui-surface-subtle" },
341
+ "--ui-steps-upcoming-marker-text": { "kind": "token", "token": "--ui-text-primary" },
342
+ "--ui-media-thumbnail-veil": { "kind": "token", "token": "--ui-media-overlay-bg" },
343
+ "--ui-media-enlarge-bg": { "kind": "literal", "value": "transparent" },
344
+ "--ui-media-enlarge-icon": { "kind": "token", "token": "--ui-media-overlay-text" },
345
+ "--ui-media-enlarge-shadow-color": { "kind": "literal", "value": "transparent" },
346
+ "--ui-message-incoming-document-bg": { "kind": "token", "token": "--ui-surface" },
347
+ "--ui-message-incoming-document-border": { "kind": "token", "token": "--ui-border" },
348
+ "--ui-message-incoming-document-text": { "kind": "token", "token": "--ui-text-primary" },
349
+ "--ui-message-incoming-document-meta": { "kind": "token", "token": "--ui-text-secondary" },
350
+ "--ui-message-incoming-document-download-bg": { "kind": "literal", "value": "transparent" },
351
+ "--ui-message-incoming-document-download-bg-hover": { "kind": "token", "token": "--ui-action-ghost-bg-hover" },
352
+ "--ui-message-incoming-document-download-icon": { "kind": "token", "token": "--ui-action-ghost-text" },
353
+ "--ui-message-incoming-document-download-icon-hover": { "kind": "token", "token": "--ui-action-ghost-text-hover" },
354
+ "--ui-message-assistant-document-bg": { "kind": "token", "token": "--ui-surface" },
355
+ "--ui-message-assistant-document-border": { "kind": "token", "token": "--ui-border" },
356
+ "--ui-message-assistant-document-text": { "kind": "token", "token": "--ui-text-primary" },
357
+ "--ui-message-assistant-document-meta": { "kind": "token", "token": "--ui-text-secondary" },
358
+ "--ui-message-assistant-document-download-bg": { "kind": "literal", "value": "transparent" },
359
+ "--ui-message-assistant-document-download-bg-hover": { "kind": "token", "token": "--ui-action-ghost-bg-hover" },
360
+ "--ui-message-assistant-document-download-icon": { "kind": "token", "token": "--ui-action-ghost-text" },
361
+ "--ui-message-assistant-document-download-icon-hover": { "kind": "token", "token": "--ui-action-ghost-text-hover" },
362
+ "--ui-message-outgoing-document-bg": { "kind": "token", "token": "--ui-surface" },
363
+ "--ui-message-outgoing-document-border": { "kind": "token", "token": "--ui-border" },
364
+ "--ui-message-outgoing-document-text": { "kind": "token", "token": "--ui-text-primary" },
365
+ "--ui-message-outgoing-document-meta": { "kind": "token", "token": "--ui-text-secondary" },
366
+ "--ui-message-outgoing-document-download-bg": { "kind": "literal", "value": "transparent" },
367
+ "--ui-message-outgoing-document-download-bg-hover": { "kind": "token", "token": "--ui-action-ghost-bg-hover" },
368
+ "--ui-message-outgoing-document-download-icon": { "kind": "token", "token": "--ui-action-ghost-text" },
369
+ "--ui-message-outgoing-document-download-icon-hover": { "kind": "token", "token": "--ui-action-ghost-text-hover" },
370
+ "--ui-message-incoming-audio-play-bg": { "kind": "token", "token": "--ui-action-primary-bg" },
371
+ "--ui-message-incoming-audio-play-icon": { "kind": "token", "token": "--ui-action-primary-text" },
372
+ "--ui-message-incoming-audio-wave-played": { "kind": "token", "token": "--ui-action-primary-bg" },
373
+ "--ui-message-incoming-audio-wave-unplayed": { "kind": "token", "token": "--ui-border-strong" },
374
+ "--ui-message-incoming-audio-playhead": { "kind": "token", "token": "--ui-message-incoming-text" },
375
+ "--ui-message-incoming-audio-time": { "kind": "token", "token": "--ui-message-incoming-meta" },
376
+ "--ui-message-incoming-audio-speed-bg": { "kind": "token", "token": "--ui-surface-subtle" },
377
+ "--ui-message-incoming-audio-speed-text": { "kind": "token", "token": "--ui-text-primary" },
378
+ "--ui-message-assistant-audio-play-bg": { "kind": "token", "token": "--ui-action-primary-bg" },
379
+ "--ui-message-assistant-audio-play-icon": { "kind": "token", "token": "--ui-action-primary-text" },
380
+ "--ui-message-assistant-audio-wave-played": { "kind": "token", "token": "--ui-action-primary-bg" },
381
+ "--ui-message-assistant-audio-wave-unplayed": { "kind": "token", "token": "--ui-border-strong" },
382
+ "--ui-message-assistant-audio-playhead": { "kind": "token", "token": "--ui-message-assistant-text" },
383
+ "--ui-message-assistant-audio-time": { "kind": "token", "token": "--ui-message-assistant-meta" },
384
+ "--ui-message-assistant-audio-speed-bg": { "kind": "token", "token": "--ui-surface-subtle" },
385
+ "--ui-message-assistant-audio-speed-text": { "kind": "token", "token": "--ui-text-primary" },
386
+ "--ui-message-outgoing-audio-play-bg": { "kind": "token", "token": "--ui-action-primary-text" },
387
+ "--ui-message-outgoing-audio-play-icon": { "kind": "token", "token": "--ui-action-primary-bg" },
388
+ "--ui-message-outgoing-audio-wave-played": { "kind": "token", "token": "--ui-action-primary-text" },
389
+ "--ui-message-outgoing-audio-wave-unplayed": { "kind": "literal", "value": "rgb(255 255 255 / 60%)" },
390
+ "--ui-message-outgoing-audio-playhead": { "kind": "token", "token": "--ui-message-outgoing-text" },
391
+ "--ui-message-outgoing-audio-time": { "kind": "token", "token": "--ui-message-outgoing-meta" },
392
+ "--ui-message-outgoing-audio-speed-bg": { "kind": "literal", "value": "rgb(0 0 0 / 20%)" },
393
+ "--ui-message-outgoing-audio-speed-text": { "kind": "token", "token": "--ui-action-primary-text" },
394
+ "--ui-audio-play-shadow-color": { "kind": "literal", "value": "transparent" }
325
395
  },
326
396
  "shape": [
327
397
  "--ui-font-body",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksmv/ui-checks",
3
- "version": "0.22.1",
3
+ "version": "0.23.2",
4
4
  "description": "Static and computed quality checks for semantic UI token contracts.",
5
5
  "keywords": ["accessibility", "design-tokens", "quality", "static-analysis"],
6
6
  "license": "Apache-2.0",