@ksmv/ui-checks 0.21.1 → 0.22.1

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.22.1 - 2026-10-17
4
+
5
+ - **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.
6
+ - **A regra `tokens/contrast` mede um par na ponta de um degradê ao longo dele:** um token `<início>-end`, quando o início também é um token, é a ponta de um degradê, e o par é medido em 17 pontos, em sRGB e em Oklab (os espaços em que o navegador interpola um degradê que não declara espaço), com o alfa pré-multiplicado, ficando com o pior. Um texto que passa nas duas pontas pode reprovar no meio.
7
+ - **Quem usa `computedEvidence` precisa capturar a evidência de novo:** os dois contratos mudaram.
8
+
9
+ ## 0.22.0 - 2026-10-17
10
+
11
+ - **Contratos de tema 3.3.0 e de contraste 11.0.0** empacotados. O de tema ganha 89 derivados opcionais e 5 tokens de forma; o de contraste ganha 104 pares. Nenhum par sai. Os pares do pressionado contra as superfícies e os da linha com tom são obrigações novas, e um tema com o primário claro demais, ou com um fundo de tom escuro, reprova. Atualize o `@ksmv/ui-react` junto (ver o CHANGELOG dele).
12
+ - **A regra `css/no-literal-color` aceita o padrão literal do contrato**, só como fallback do próprio token: `var(--ui-x, <cor>)` passa quando a cor é, texto por texto, o padrão que o contrato dá à `--ui-x`. Qualquer outra cor literal continua recusada.
13
+ - **Quem usa `computedEvidence` precisa capturar a evidência de novo:** os dois contratos mudaram.
14
+
3
15
  ## 0.21.1 - 2026-10-15
4
16
 
5
17
  - Sem mudança no verificador: a versão acompanha a do `@ksmv/ui-react` 0.21.1. Os contratos continuam tema `3.2.0` e contraste `10.0.0`, e a evidência calculada da 0.21.0 continua valendo.
package/README.md CHANGED
@@ -1,6 +1,8 @@
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), 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ê), prefixo dos tokens, cores literais fora dos arquivos de tema e evidência calculada vinculada às fontes.
4
+
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.
4
6
 
5
7
  ## Antes de depender deste pacote
6
8
 
package/dist/color.d.ts CHANGED
@@ -14,9 +14,10 @@ export declare function parsedColor(value: string): {
14
14
  mode: "rgb";
15
15
  } | undefined;
16
16
  export declare function containsUnsupportedColorFunction(value: string): boolean;
17
- export declare function containsLiteralColor(value: string): boolean;
17
+ export declare function containsLiteralColor(value: string, contractDefaults?: Readonly<Record<string, string>>): boolean;
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
21
  export declare function composite(foreground: RgbColor, background: RgbColor): RgbColor;
22
+ export declare function mix(start: RgbColor, end: RgbColor, at: number): RgbColor[];
22
23
  export declare function contrastRatio(foreground: RgbColor, background: RgbColor): number | undefined;
package/dist/color.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { converter, parse } from "culori";
2
2
  import valueParser from "postcss-value-parser";
3
3
  const toRgb = converter("rgb");
4
+ const toOklab = converter("oklab");
4
5
  const unsupportedFunctions = new Set(["color-mix", "light-dark"]);
5
6
  const literalColorFunctions = new Set([
6
7
  "color",
@@ -87,9 +88,21 @@ export function containsUnsupportedColorFunction(value) {
87
88
  });
88
89
  return unsupported;
89
90
  }
90
- export function containsLiteralColor(value) {
91
+ // A literal color in a value. The fallback of `var(--ui-x, <literal>)` is not
92
+ // one when the theme contract gives --ui-x exactly that literal as its
93
+ // default: the stylesheet implements the contract (#276). Each var is judged
94
+ // on its own, and the text must be the contract's, not an equal color.
95
+ export function containsLiteralColor(value, contractDefaults = {}) {
91
96
  let literal = false;
92
97
  valueParser(value).walk((node) => {
98
+ if (node.type === "function" && node.value.toLowerCase() === "var") {
99
+ const { fallback, name } = splitVarArguments(node.nodes);
100
+ const variable = name.trim();
101
+ if (fallback !== undefined && Object.hasOwn(contractDefaults, variable) && fallback.trim() === contractDefaults[variable]) {
102
+ return false;
103
+ }
104
+ return undefined;
105
+ }
93
106
  if (node.type === "function" && node.value.toLowerCase() !== "var") {
94
107
  if (literalColorFunctions.has(node.value.toLowerCase()) ||
95
108
  parsedColor(valueParser.stringify(node))) {
@@ -183,6 +196,21 @@ export function composite(foreground, background) {
183
196
  r: foreground.r * alpha + background.r * (1 - alpha),
184
197
  };
185
198
  }
199
+ // A point of a gradient between two colors. The browser interpolates legacy
200
+ // colors (hex, rgb(), hsl()) in sRGB and the others, as oklch(), in Oklab
201
+ // (CSS Color 4, §12.1); the space is chosen by the syntax, so the check takes
202
+ // both points. Both interpolate with premultiplied alpha (§12.3): a channel
203
+ // is weighted by its color's alpha, so a transparent end lends no hue.
204
+ export function mix(start, end, at) {
205
+ const [startAlpha, endAlpha] = [start.alpha ?? 1, end.alpha ?? 1];
206
+ const alpha = startAlpha + (endAlpha - startAlpha) * at;
207
+ const between = (from, to) => alpha === 0 ? 0 : (from * startAlpha + (to * endAlpha - from * startAlpha) * at) / alpha;
208
+ const srgb = { alpha, b: between(start.b, end.b), g: between(start.g, end.g), mode: "rgb", r: between(start.r, end.r) };
209
+ const [from, to] = [toOklab(start), toOklab(end)];
210
+ const clamp = (value) => Math.min(1, Math.max(0, value));
211
+ const oklab = toRgb({ a: between(from.a, to.a), alpha, b: between(from.b, to.b), l: between(from.l, to.l), mode: "oklab" });
212
+ return [srgb, { alpha, b: clamp(oklab.b), g: clamp(oklab.g), mode: "rgb", r: clamp(oklab.r) }];
213
+ }
186
214
  function luminance(color) {
187
215
  const channel = (value) => value <= 0.04045
188
216
  ? value / 12.92
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "10.0.0",
2
+ "contractVersion": "12.0.0",
3
3
  "schemaVersion": 2,
4
4
  "pairs": [
5
5
  { "foreground": "--ui-action-primary-text", "background": "--ui-action-primary-bg", "minimum": 4.5, "context": "normal-text" },
@@ -213,6 +213,130 @@
213
213
  { "foreground": "--ui-field-description", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "field-description-subtle" },
214
214
  { "foreground": "--ui-checkbox-border", "background": "--ui-checkbox-bg", "over": "--ui-surface", "minimum": 3, "context": "checkbox-boundary" },
215
215
  { "foreground": "--ui-checkbox-border", "background": "--ui-surface", "minimum": 3, "context": "checkbox-boundary-surface" },
216
- { "foreground": "--ui-checkbox-mark", "background": "--ui-control-checked-bg", "minimum": 3, "context": "checkbox-mark" }
216
+ { "foreground": "--ui-checkbox-mark", "background": "--ui-control-checked-bg", "minimum": 3, "context": "checkbox-mark" },
217
+ { "foreground": "--ui-button-pressed-text", "background": "--ui-button-pressed-bg", "minimum": 4.5, "context": "button-pressed-text" },
218
+ { "foreground": "--ui-button-pressed-text", "background": "--ui-button-pressed-bg-hover", "minimum": 4.5, "context": "button-pressed-text-hover" },
219
+ { "foreground": "--ui-button-pressed-bg", "background": "--ui-surface", "minimum": 3, "context": "button-pressed-boundary" },
220
+ { "foreground": "--ui-button-pressed-bg-hover", "background": "--ui-surface", "minimum": 3, "context": "button-pressed-boundary-hover" },
221
+ { "foreground": "--ui-button-pressed-text-on-inverse", "background": "--ui-button-pressed-bg-on-inverse", "minimum": 4.5, "context": "button-pressed-text-on-inverse" },
222
+ { "foreground": "--ui-button-pressed-text-on-inverse", "background": "--ui-button-pressed-bg-hover-on-inverse", "minimum": 4.5, "context": "button-pressed-text-hover-on-inverse" },
223
+ { "foreground": "--ui-button-pressed-bg-on-inverse", "background": "--ui-surface-inverse", "minimum": 3, "context": "button-pressed-boundary-on-inverse" },
224
+ { "foreground": "--ui-button-pressed-bg-hover-on-inverse", "background": "--ui-surface-inverse", "minimum": 3, "context": "button-pressed-boundary-hover-on-inverse" },
225
+ { "foreground": "--ui-button-pressed-bg", "background": "--ui-surface-raised", "minimum": 3, "context": "button-pressed-boundary-raised" },
226
+ { "foreground": "--ui-button-pressed-bg-hover", "background": "--ui-surface-raised", "minimum": 3, "context": "button-pressed-boundary-hover-raised" },
227
+ { "foreground": "--ui-button-pressed-bg", "background": "--ui-surface-subtle", "minimum": 3, "context": "button-pressed-boundary-subtle" },
228
+ { "foreground": "--ui-button-pressed-bg-hover", "background": "--ui-surface-subtle", "minimum": 3, "context": "button-pressed-boundary-hover-subtle" },
229
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-success-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-success" },
230
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-success-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-success" },
231
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-warning-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-warning" },
232
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-warning-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-warning" },
233
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-danger" },
234
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-danger" },
235
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-info-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-title-info" },
236
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-info-bg", "over": "--ui-surface", "minimum": 4.5, "context": "list-item-description-info" },
237
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-success-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-success-raised" },
238
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-success-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-success-raised" },
239
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-warning-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-warning-raised" },
240
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-warning-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-warning-raised" },
241
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-danger-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-danger-raised" },
242
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-danger-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-danger-raised" },
243
+ { "foreground": "--ui-text-primary", "background": "--ui-list-item-info-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-title-info-raised" },
244
+ { "foreground": "--ui-text-secondary", "background": "--ui-list-item-info-bg", "over": "--ui-surface-raised", "minimum": 4.5, "context": "list-item-description-info-raised" },
245
+ { "foreground": "--ui-badge-leading-text", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "badge-leading-text" },
246
+ { "foreground": "--ui-page-header-eyebrow", "background": "--ui-surface", "minimum": 4.5, "context": "page-header-eyebrow" },
247
+ { "foreground": "--ui-page-header-eyebrow", "background": "--ui-surface-raised", "minimum": 4.5, "context": "page-header-eyebrow-raised" },
248
+ { "foreground": "--ui-page-header-eyebrow", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "page-header-eyebrow-subtle" },
249
+ { "foreground": "--ui-stat-label", "background": "--ui-surface", "minimum": 4.5, "context": "stat-label" },
250
+ { "foreground": "--ui-stat-label", "background": "--ui-surface-raised", "minimum": 4.5, "context": "stat-label-raised" },
251
+ { "foreground": "--ui-stat-label", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "stat-label-subtle" },
252
+ { "foreground": "--ui-steps-current-text", "background": "--ui-steps-current", "minimum": 4.5, "context": "steps-current-text" },
253
+ { "foreground": "--ui-text-primary", "background": "--ui-steps-current-bg", "over": "--ui-surface", "minimum": 4.5, "context": "steps-current-title" },
254
+ { "foreground": "--ui-text-secondary", "background": "--ui-steps-current-bg", "over": "--ui-surface", "minimum": 4.5, "context": "steps-current-description" },
255
+ { "foreground": "--ui-text-primary", "background": "--ui-steps-complete-bg", "over": "--ui-surface", "minimum": 4.5, "context": "steps-complete-title" },
256
+ { "foreground": "--ui-text-secondary", "background": "--ui-steps-complete-bg", "over": "--ui-surface", "minimum": 4.5, "context": "steps-complete-description" },
257
+ { "foreground": "--ui-steps-track-empty", "background": "--ui-surface", "minimum": 3, "context": "steps-track-empty" },
258
+ { "foreground": "--ui-side-nav-count-text", "background": "--ui-side-nav-count-bg", "over": "--ui-surface", "minimum": 4.5, "context": "side-nav-count-text" },
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
+ { "foreground": "--ui-steps-current", "background": "--ui-steps-current-bg", "over": "--ui-surface", "minimum": 3, "context": "steps-current-boundary" },
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" },
263
+ { "foreground": "--ui-steps-track-filled", "background": "--ui-surface", "minimum": 3, "context": "steps-track-filled" },
264
+ { "foreground": "--ui-side-nav-count-dot", "background": "--ui-surface", "minimum": 3, "context": "side-nav-count-dot" },
265
+ { "foreground": "--ui-message-incoming-text", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-incoming-text" },
266
+ { "foreground": "--ui-message-incoming-meta", "background": "--ui-message-incoming-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-incoming-meta" },
267
+ { "foreground": "--ui-message-incoming-quote-text", "background": "--ui-message-incoming-quote-bg", "over": "--ui-message-incoming-bg", "minimum": 4.5, "context": "message-incoming-quote" },
268
+ { "foreground": "--ui-message-assistant-text", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-assistant-text" },
269
+ { "foreground": "--ui-message-assistant-meta", "background": "--ui-message-assistant-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-assistant-meta" },
270
+ { "foreground": "--ui-message-assistant-quote-text", "background": "--ui-message-assistant-quote-bg", "over": "--ui-message-assistant-bg", "minimum": 4.5, "context": "message-assistant-quote" },
271
+ { "foreground": "--ui-message-outgoing-text", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-outgoing-text" },
272
+ { "foreground": "--ui-message-outgoing-meta", "background": "--ui-message-outgoing-bg", "over": "--ui-surface", "minimum": 4.5, "context": "message-outgoing-meta" },
273
+ { "foreground": "--ui-message-outgoing-quote-text", "background": "--ui-message-outgoing-quote-bg", "over": "--ui-message-outgoing-bg", "minimum": 4.5, "context": "message-outgoing-quote" },
274
+ { "foreground": "--ui-system-message-accent-text", "background": "--ui-system-message-accent-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-accent-title" },
275
+ { "foreground": "--ui-text-primary", "background": "--ui-system-message-accent-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-accent-body" },
276
+ { "foreground": "--ui-text-secondary", "background": "--ui-system-message-accent-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-accent-time" },
277
+ { "foreground": "--ui-system-message-danger-text", "background": "--ui-system-message-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-danger-title" },
278
+ { "foreground": "--ui-text-primary", "background": "--ui-system-message-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-danger-body" },
279
+ { "foreground": "--ui-text-secondary", "background": "--ui-system-message-danger-bg", "over": "--ui-surface", "minimum": 4.5, "context": "system-message-danger-time" },
280
+ { "foreground": "--ui-media-overlay-text", "background": "--ui-media-overlay-bg", "over": "--ui-surface", "minimum": 4.5, "context": "media-overlay-text" },
281
+ { "foreground": "--ui-media-overlay-text", "background": "--ui-media-control-bg", "over": "--ui-media-viewer-base", "minimum": 3, "context": "media-control-icon" },
282
+ { "foreground": "--ui-media-overlay-text", "background": "--ui-media-control-bg-hover", "over": "--ui-media-viewer-base", "minimum": 3, "context": "media-control-icon-hover" },
283
+ { "foreground": "--ui-media-overlay-text", "background": "--ui-media-caption-bg", "over": "--ui-media-viewer-base", "minimum": 4.5, "context": "media-caption" },
284
+ { "foreground": "--ui-media-strip-current", "background": "--ui-surface", "minimum": 3, "context": "media-strip-current" },
285
+ { "foreground": "--ui-time-slot-available-text", "background": "--ui-time-slot-available-bg", "over": "--ui-surface", "minimum": 4.5, "context": "time-slot-available-text" },
286
+ { "foreground": "--ui-time-slot-available-text-hover", "background": "--ui-time-slot-available-bg-hover", "over": "--ui-surface", "minimum": 4.5, "context": "time-slot-available-text-hover" },
287
+ { "foreground": "--ui-time-slot-available-border", "background": "--ui-surface", "minimum": 3, "context": "time-slot-available-boundary" },
288
+ { "foreground": "--ui-time-slot-busy-text", "background": "--ui-time-slot-busy-bg", "over": "--ui-surface", "minimum": 4.5, "context": "time-slot-busy-text" },
289
+ { "foreground": "--ui-time-slot-busy-border", "background": "--ui-surface", "minimum": 3, "context": "time-slot-busy-boundary" },
290
+ { "foreground": "--ui-time-slot-busy-selected-text", "background": "--ui-time-slot-busy-selected-bg", "over": "--ui-surface", "minimum": 4.5, "context": "time-slot-busy-selected-text" },
291
+ { "foreground": "--ui-time-slot-busy-selected-bg", "background": "--ui-surface", "minimum": 3, "context": "time-slot-busy-selected-boundary" },
292
+ { "foreground": "--ui-text-secondary", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "time-slot-outside-text" },
293
+ { "foreground": "--ui-day-today-text", "background": "--ui-surface", "minimum": 4.5, "context": "day-today-text" },
294
+ { "foreground": "--ui-day-today-text", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "day-today-text-hover" },
295
+ { "foreground": "--ui-text-secondary", "background": "--ui-day-selected-bg", "over": "--ui-surface", "minimum": 4.5, "context": "day-selected-label" },
296
+ { "foreground": "--ui-day-selected-border", "background": "--ui-day-selected-bg", "over": "--ui-surface", "minimum": 3, "context": "day-selected-boundary" },
297
+ { "foreground": "--ui-day-selected-border", "background": "--ui-surface", "minimum": 3, "context": "day-selected-border" },
298
+ { "foreground": "--ui-button-pressed-bg", "background": "--ui-day-selected-bg", "over": "--ui-surface", "minimum": 3, "context": "day-selected-circle" },
299
+ { "foreground": "--ui-day-detail-available", "background": "--ui-surface", "minimum": 4.5, "context": "day-detail-available" },
300
+ { "foreground": "--ui-day-detail-busy", "background": "--ui-surface", "minimum": 4.5, "context": "day-detail-busy" },
301
+ { "foreground": "--ui-day-detail-available", "background": "--ui-day-selected-bg", "over": "--ui-surface", "minimum": 4.5, "context": "day-detail-available-selected" },
302
+ { "foreground": "--ui-day-detail-busy", "background": "--ui-day-selected-bg", "over": "--ui-surface", "minimum": 4.5, "context": "day-detail-busy-selected" },
303
+ { "foreground": "--ui-day-detail-available", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "day-detail-available-hover" },
304
+ { "foreground": "--ui-day-detail-busy", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "day-detail-busy-hover" },
305
+ { "foreground": "--ui-text-primary", "background": "--ui-category-1-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-1" },
306
+ { "foreground": "--ui-text-primary", "background": "--ui-category-2-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-2" },
307
+ { "foreground": "--ui-text-primary", "background": "--ui-category-3-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-3" },
308
+ { "foreground": "--ui-text-primary", "background": "--ui-category-4-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-4" },
309
+ { "foreground": "--ui-text-primary", "background": "--ui-category-5-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-5" },
310
+ { "foreground": "--ui-text-primary", "background": "--ui-category-6-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-6" },
311
+ { "foreground": "--ui-text-primary", "background": "--ui-category-7-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-7" },
312
+ { "foreground": "--ui-text-primary", "background": "--ui-category-8-bg", "over": "--ui-surface", "minimum": 4.5, "context": "calendar-event-title-8" },
313
+ { "foreground": "--ui-calendar-selected-text", "background": "--ui-surface-subtle", "minimum": 4.5, "context": "calendar-selected-text" },
314
+ { "foreground": "--ui-calendar-free-text", "background": "--ui-surface", "minimum": 4.5, "context": "calendar-free-text" },
315
+ { "foreground": "--ui-calendar-free-border", "background": "--ui-surface", "minimum": 3, "context": "calendar-free-boundary" },
316
+ { "foreground": "--ui-board-count-text", "background": "--ui-board-count-bg", "over": "--ui-surface", "minimum": 4.5, "context": "board-count-text" },
317
+ { "foreground": "--ui-board-card-border-hover", "background": "--ui-surface-subtle", "minimum": 3, "context": "board-card-border-hover" },
318
+ { "foreground": "--ui-board-drop-border", "background": "--ui-surface-subtle", "minimum": 3, "context": "board-drop-border" },
319
+ { "foreground": "--ui-board-drop-border", "background": "--ui-board-drop-bg", "over": "--ui-surface-subtle", "minimum": 3, "context": "board-drop-line" },
320
+ { "foreground": "--ui-border-control", "background": "--ui-surface-subtle", "minimum": 3, "context": "board-placeholder-boundary" },
321
+ { "foreground": "--ui-text-muted-on-inverse", "background": "--ui-surface-inverse", "minimum": 4.5, "context": "inverse-muted-text" },
322
+ { "foreground": "--ui-text-primary-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-body-text-end" },
323
+ { "foreground": "--ui-text-secondary-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-secondary-text-end" },
324
+ { "foreground": "--ui-border-control-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "inverse-control-boundary-end" },
325
+ { "foreground": "--ui-link-text-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-link-text-end" },
326
+ { "foreground": "--ui-text-button-text-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-text-button-text-end" },
327
+ { "foreground": "--ui-action-ghost-text-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-ghost-text-end" },
328
+ { "foreground": "--ui-action-ghost-text-hover-on-inverse", "background": "--ui-action-ghost-bg-hover-on-inverse", "over": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-ghost-text-hover-end" },
329
+ { "foreground": "--ui-action-ghost-text-hover-on-inverse", "background": "--ui-action-ghost-bg-active-on-inverse", "over": "--ui-surface-inverse-end", "minimum": 4.5, "context": "inverse-ghost-text-active-end" },
330
+ { "foreground": "--ui-icon-button-ghost-text-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "inverse-icon-button-ghost-icon-end" },
331
+ { "foreground": "--ui-icon-button-ghost-text-on-inverse", "background": "--ui-action-ghost-bg-hover-on-inverse", "over": "--ui-surface-inverse-end", "minimum": 3, "context": "inverse-icon-button-ghost-icon-hover-end" },
332
+ { "foreground": "--ui-icon-button-ghost-text-on-inverse", "background": "--ui-action-ghost-bg-active-on-inverse", "over": "--ui-surface-inverse-end", "minimum": 3, "context": "inverse-icon-button-ghost-icon-active-end" },
333
+ { "foreground": "--ui-status-success-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "status-indicator-success-on-inverse-end" },
334
+ { "foreground": "--ui-status-warning-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "status-indicator-warning-on-inverse-end" },
335
+ { "foreground": "--ui-status-danger-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "status-indicator-danger-on-inverse-end" },
336
+ { "foreground": "--ui-status-info-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "status-indicator-info-on-inverse-end" },
337
+ { "foreground": "--ui-button-pressed-bg-on-inverse", "background": "--ui-surface-inverse-end", "minimum": 3, "context": "button-pressed-boundary-on-inverse-end" },
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
+ { "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" }
217
341
  ]
218
342
  }
@@ -10,7 +10,7 @@ export async function createDefaultRules({ components, computed, contract, manif
10
10
  manifest ?? loadContrastManifest(),
11
11
  ]);
12
12
  return [
13
- createNoLiteralColorRule({ themes }),
13
+ createNoLiteralColorRule({ contractDefaults: resolvedContract.defaults, themes }),
14
14
  createTokenPrefixRule(),
15
15
  createTokenCompleteRule({ computed, contract: resolvedContract, themes }),
16
16
  createThemeShapeRule({ contract: resolvedContract, themes }),
@@ -1,6 +1,8 @@
1
1
  import type { ThemeConfig } from "../config.js";
2
2
  import type { CheckRule } from "../scan.js";
3
3
  export interface NoLiteralColorOptions {
4
+ /** The theme contract's literal defaults: `var(--ui-x, <its default>)` is the contract, not a literal. */
5
+ contractDefaults?: Readonly<Record<string, string>>;
4
6
  themes: ThemeConfig[];
5
7
  }
6
- export declare function createNoLiteralColorRule({ themes, }: NoLiteralColorOptions): CheckRule;
8
+ export declare function createNoLiteralColorRule({ contractDefaults, themes, }: NoLiteralColorOptions): CheckRule;
@@ -26,12 +26,12 @@ function jsxStringValue(node) {
26
26
  ? node.expression.text
27
27
  : undefined;
28
28
  }
29
- function classContainsLiteralColor(value) {
29
+ function classContainsLiteralColor(value, contractDefaults) {
30
30
  return [...value.matchAll(/\[([^\]]+)\]/g)].some((match) => {
31
31
  const candidate = match[1] ?? "";
32
32
  const separator = candidate.indexOf(":");
33
33
  if (separator <= 0) {
34
- return containsLiteralColor(candidate);
34
+ return containsLiteralColor(candidate, contractDefaults);
35
35
  }
36
36
  const property = candidate.slice(0, separator);
37
37
  const propertyValue = candidate.slice(separator + 1);
@@ -40,27 +40,27 @@ function classContainsLiteralColor(value) {
40
40
  const utility = currentClass.split(":").at(-1) ?? "";
41
41
  if (utility.endsWith("-")) {
42
42
  if (property === "color" || property === "image") {
43
- return containsLiteralColor(propertyValue);
43
+ return containsLiteralColor(propertyValue, contractDefaults);
44
44
  }
45
- return (/^(?:accent|bg|border|caret|decoration|divide|fill|from|outline|placeholder|ring|shadow|stroke|text|to|via)-$/.test(utility) && containsLiteralColor(propertyValue));
45
+ return (/^(?:accent|bg|border|caret|decoration|divide|fill|from|outline|placeholder|ring|shadow|stroke|text|to|via)-$/.test(utility) && containsLiteralColor(propertyValue, contractDefaults));
46
46
  }
47
- return (isColorBearingProperty(property) && containsLiteralColor(propertyValue));
47
+ return (isColorBearingProperty(property) && containsLiteralColor(propertyValue, contractDefaults));
48
48
  });
49
49
  }
50
- export function createNoLiteralColorRule({ themes, }) {
50
+ export function createNoLiteralColorRule({ contractDefaults = {}, themes, }) {
51
51
  return {
52
52
  name: "css/no-literal-color",
53
53
  check(file) {
54
54
  const result = emptyResult();
55
55
  file.cssRoot?.walkDecls((declaration) => {
56
56
  if (isColorBearingProperty(declaration.prop) &&
57
- containsLiteralColor(declaration.value) &&
57
+ containsLiteralColor(declaration.value, contractDefaults) &&
58
58
  !isAllowedThemeDeclaration(file, declaration, themes)) {
59
59
  result.violations.push({
60
60
  anchor: cssDeclarationAnchor(declaration),
61
61
  column: declaration.source?.start?.column ?? 1,
62
62
  line: declaration.source?.start?.line ?? 1,
63
- message: "Literal colors are allowed only in configured themes.",
63
+ message: "Literal colors are allowed only in configured themes, or as the theme contract's own default of the token they fall back from.",
64
64
  nodeKind: "css-declaration",
65
65
  path: file.path,
66
66
  rule: "css/no-literal-color",
@@ -77,20 +77,20 @@ export function createNoLiteralColorRule({ themes, }) {
77
77
  const isDirectColor = isColorBearingProperty(node.name.text);
78
78
  const isLiteralClass = node.name.text === "className" &&
79
79
  value !== undefined &&
80
- classContainsLiteralColor(value);
80
+ classContainsLiteralColor(value, contractDefaults);
81
81
  if (value !== undefined &&
82
- ((isDirectColor && containsLiteralColor(value)) || isLiteralClass)) {
82
+ ((isDirectColor && containsLiteralColor(value, contractDefaults)) || isLiteralClass)) {
83
83
  const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
84
84
  // For className, only the classes that carry the literal color
85
85
  // identify the finding; the other classes may change freely.
86
86
  const anchor = isLiteralClass && !isDirectColor
87
- ? `className: ${value.split(/\s+/).filter(classContainsLiteralColor).join(" ")}`
87
+ ? `className: ${value.split(/\s+/).filter((name) => classContainsLiteralColor(name, contractDefaults)).join(" ")}`
88
88
  : tsxNodeAnchor(node, sourceFile);
89
89
  result.violations.push({
90
90
  anchor,
91
91
  column: position.character + 1,
92
92
  line: position.line + 1,
93
- message: "Literal colors are allowed only in configured themes.",
93
+ message: "Literal colors are allowed only in configured themes, or as the theme contract's own default of the token they fall back from.",
94
94
  nodeKind: "tsx-color-attribute",
95
95
  path: file.path,
96
96
  rule: "css/no-literal-color",
@@ -112,13 +112,13 @@ export function createNoLiteralColorRule({ themes, }) {
112
112
  const name = propertyName(property.name);
113
113
  if (name !== undefined &&
114
114
  isColorBearingProperty(name) &&
115
- containsLiteralColor(property.initializer.text)) {
115
+ containsLiteralColor(property.initializer.text, contractDefaults)) {
116
116
  const position = sourceFile.getLineAndCharacterOfPosition(property.initializer.getStart(sourceFile));
117
117
  result.violations.push({
118
118
  anchor: tsxNodeAnchor(property, sourceFile),
119
119
  column: position.character + 1,
120
120
  line: position.line + 1,
121
- message: "Literal colors are allowed only in configured themes.",
121
+ message: "Literal colors are allowed only in configured themes, or as the theme contract's own default of the token they fall back from.",
122
122
  nodeKind: "tsx-style-property",
123
123
  path: file.path,
124
124
  rule: "css/no-literal-color",
@@ -1,7 +1,26 @@
1
1
  import { isComputedPropertyName, isIdentifier, isJsxAttribute, isJsxExpression, isObjectLiteralExpression, isPropertyAssignment, isJsxSpreadAttribute, isStringLiteralLikeNode, } from "typescript/unstable/ast";
2
- import { composite, contrastRatio, isColorBearingProperty, resolveColor, } from "../color.js";
2
+ import { composite, contrastRatio, isColorBearingProperty, mix, resolveColor, } from "../color.js";
3
3
  import { parseContrastManifest, } from "../contracts.js";
4
4
  import { createThemeCollector } from "./theme.js";
5
+ // The end of a gradient (#394): a token named <start>-end, when the start is
6
+ // a token too, paints a gradient from the start to itself. A pair on it is
7
+ // measured along the whole gradient, and not only at the end: between two
8
+ // colors that pass, the mix can fall below the minimum, as when the text's
9
+ // luminance lies between the two ends. Each point is taken in sRGB and in
10
+ // Oklab, the two spaces a browser may interpolate in.
11
+ const gradientSteps = 16;
12
+ function resolveAlong(token, tokens) {
13
+ const end = resolveColor(tokens.get(token), tokens);
14
+ const startName = token.endsWith("-end") ? token.slice(0, -"-end".length) : undefined;
15
+ if (!startName || !tokens.has(startName))
16
+ return [end];
17
+ const start = resolveColor(tokens.get(startName), tokens);
18
+ if (start.status !== "resolved" || end.status !== "resolved")
19
+ return [{ status: "uncovered" }];
20
+ return Array.from({ length: gradientSteps + 1 }, (_, step) => mix(start.color, end.color, step / gradientSteps))
21
+ .flat()
22
+ .map((color) => ({ color, status: "resolved" }));
23
+ }
5
24
  export async function loadContrastManifest(url) {
6
25
  const { readFile } = await import("node:fs/promises");
7
26
  const packaged = url ?? new URL("../contrast-contract.json", import.meta.url);
@@ -144,17 +163,25 @@ export function createTokenContrastRule({ computed = {}, defaults = {}, derived
144
163
  tokens.set(token, value);
145
164
  }
146
165
  for (const pair of manifest.pairs) {
147
- let background = resolveColor(tokens.get(pair.background), tokens);
148
166
  const foreground = resolveColor(tokens.get(pair.foreground), tokens);
149
- // A translucent background is measured as painted over the surface
150
- // the pair names (schema 2); without one it stays uncovered below.
151
- if (pair.over && background.status === "resolved" && (background.color.alpha ?? 1) < 1) {
152
- const over = resolveColor(tokens.get(pair.over), tokens);
153
- background = over.status === "resolved" && (over.color.alpha ?? 1) === 1
154
- ? { color: composite(background.color, over.color), status: "resolved" }
155
- : { status: "uncovered" };
167
+ // Each point of the background, a gradient's included, and of the
168
+ // surface under it; the pair is as good as its worst point.
169
+ const backgrounds = [];
170
+ for (const painted of resolveAlong(pair.background, tokens)) {
171
+ // A translucent background is measured as painted over the surface
172
+ // the pair names (schema 2); without one it stays uncovered below.
173
+ if (pair.over && painted.status === "resolved" && (painted.color.alpha ?? 1) < 1) {
174
+ for (const over of resolveAlong(pair.over, tokens)) {
175
+ backgrounds.push(over.status === "resolved" && (over.color.alpha ?? 1) === 1
176
+ ? { color: composite(painted.color, over.color), status: "resolved" }
177
+ : { status: "uncovered" });
178
+ }
179
+ }
180
+ else {
181
+ backgrounds.push(painted);
182
+ }
156
183
  }
157
- if (background.status === "uncovered" ||
184
+ if (backgrounds.some((background) => background.status === "uncovered") ||
158
185
  foreground.status === "uncovered") {
159
186
  result.uncovered.push({
160
187
  ...location,
@@ -165,7 +192,8 @@ export function createTokenContrastRule({ computed = {}, defaults = {}, derived
165
192
  });
166
193
  continue;
167
194
  }
168
- const ratio = contrastRatio(foreground.color, background.color);
195
+ const ratios = backgrounds.map((background) => background.status === "resolved" ? contrastRatio(foreground.color, background.color) : undefined);
196
+ const ratio = ratios.includes(undefined) ? undefined : Math.min(...ratios);
169
197
  if (ratio === undefined) {
170
198
  result.uncovered.push({
171
199
  ...location,
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "3.2.0",
2
+ "contractVersion": "3.4.0",
3
3
  "schemaVersion": 2,
4
4
  "prefix": "--ui-",
5
5
  "required": [
@@ -231,7 +231,97 @@
231
231
  "--ui-field-description": { "kind": "token", "token": "--ui-text-secondary" },
232
232
  "--ui-checkbox-border": { "kind": "token", "token": "--ui-border-control" },
233
233
  "--ui-checkbox-bg": { "kind": "token", "token": "--ui-surface" },
234
- "--ui-checkbox-mark": { "kind": "token", "token": "--ui-control-checked-fg" }
234
+ "--ui-checkbox-mark": { "kind": "token", "token": "--ui-control-checked-fg" },
235
+ "--ui-button-pressed-bg": { "kind": "token", "token": "--ui-action-primary-bg" },
236
+ "--ui-button-pressed-bg-hover": { "kind": "token", "token": "--ui-action-primary-bg-hover" },
237
+ "--ui-button-pressed-text": { "kind": "token", "token": "--ui-action-primary-text" },
238
+ "--ui-button-pressed-bg-on-inverse": { "kind": "token", "token": "--ui-surface" },
239
+ "--ui-button-pressed-bg-hover-on-inverse": { "kind": "token", "token": "--ui-surface-subtle" },
240
+ "--ui-button-pressed-text-on-inverse": { "kind": "token", "token": "--ui-text-primary" },
241
+ "--ui-list-item-success-bg": { "kind": "token", "token": "--ui-status-success-bg" },
242
+ "--ui-list-item-success-border": { "kind": "token", "token": "--ui-status-success" },
243
+ "--ui-list-item-warning-bg": { "kind": "token", "token": "--ui-status-warning-bg" },
244
+ "--ui-list-item-warning-border": { "kind": "token", "token": "--ui-status-warning" },
245
+ "--ui-list-item-danger-bg": { "kind": "token", "token": "--ui-status-danger-bg" },
246
+ "--ui-list-item-danger-border": { "kind": "token", "token": "--ui-status-danger" },
247
+ "--ui-list-item-info-bg": { "kind": "token", "token": "--ui-status-info-bg" },
248
+ "--ui-list-item-info-border": { "kind": "token", "token": "--ui-status-info" },
249
+ "--ui-badge-leading-text": { "kind": "token", "token": "--ui-text-secondary" },
250
+ "--ui-page-header-eyebrow": { "kind": "token", "token": "--ui-text-secondary" },
251
+ "--ui-stat-label": { "kind": "token", "token": "--ui-text-secondary" },
252
+ "--ui-steps-current": { "kind": "token", "token": "--ui-action-primary-bg" },
253
+ "--ui-steps-current-text": { "kind": "token", "token": "--ui-action-primary-text" },
254
+ "--ui-steps-current-bg": { "kind": "token", "token": "--ui-surface" },
255
+ "--ui-steps-current-ring": { "kind": "token", "token": "--ui-border" },
256
+ "--ui-steps-complete-bg": { "kind": "token", "token": "--ui-surface-subtle" },
257
+ "--ui-steps-track-filled": { "kind": "token", "token": "--ui-action-primary-bg" },
258
+ "--ui-steps-track-empty": { "kind": "token", "token": "--ui-border-control" },
259
+ "--ui-side-nav-count-bg": { "kind": "token", "token": "--ui-surface-subtle" },
260
+ "--ui-side-nav-count-text": { "kind": "token", "token": "--ui-text-secondary" },
261
+ "--ui-side-nav-count-bg-on-inverse": { "kind": "literal", "value": "transparent" },
262
+ "--ui-side-nav-count-text-on-inverse": { "kind": "token", "token": "--ui-text-secondary-on-inverse" },
263
+ "--ui-side-nav-count-dot": { "kind": "token", "token": "--ui-status-info" },
264
+ "--ui-message-incoming-bg": { "kind": "token", "token": "--ui-surface" },
265
+ "--ui-message-incoming-border": { "kind": "token", "token": "--ui-border" },
266
+ "--ui-message-incoming-text": { "kind": "token", "token": "--ui-text-primary" },
267
+ "--ui-message-incoming-meta": { "kind": "token", "token": "--ui-text-secondary" },
268
+ "--ui-message-incoming-quote-bg": { "kind": "token", "token": "--ui-surface-subtle" },
269
+ "--ui-message-incoming-quote-bar": { "kind": "token", "token": "--ui-action-primary-bg" },
270
+ "--ui-message-incoming-quote-text": { "kind": "token", "token": "--ui-text-secondary" },
271
+ "--ui-message-assistant-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
272
+ "--ui-message-assistant-border": { "kind": "token", "token": "--ui-border" },
273
+ "--ui-message-assistant-text": { "kind": "token", "token": "--ui-text-primary" },
274
+ "--ui-message-assistant-meta": { "kind": "token", "token": "--ui-text-secondary" },
275
+ "--ui-message-assistant-quote-bg": { "kind": "token", "token": "--ui-surface-subtle" },
276
+ "--ui-message-assistant-quote-bar": { "kind": "token", "token": "--ui-action-primary-bg" },
277
+ "--ui-message-assistant-quote-text": { "kind": "token", "token": "--ui-text-secondary" },
278
+ "--ui-message-outgoing-bg": { "kind": "token", "token": "--ui-action-primary-bg" },
279
+ "--ui-message-outgoing-border": { "kind": "token", "token": "--ui-action-primary-bg" },
280
+ "--ui-message-outgoing-text": { "kind": "token", "token": "--ui-action-primary-text" },
281
+ "--ui-message-outgoing-meta": { "kind": "token", "token": "--ui-action-primary-text" },
282
+ "--ui-message-outgoing-quote-bg": { "kind": "literal", "value": "rgb(0 0 0 / 20%)" },
283
+ "--ui-message-outgoing-quote-bar": { "kind": "token", "token": "--ui-action-primary-text" },
284
+ "--ui-message-outgoing-quote-text": { "kind": "token", "token": "--ui-action-primary-text" },
285
+ "--ui-system-message-accent-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
286
+ "--ui-system-message-accent-border": { "kind": "token", "token": "--ui-border" },
287
+ "--ui-system-message-accent-text": { "kind": "token", "token": "--ui-text-primary" },
288
+ "--ui-system-message-danger-bg": { "kind": "token", "token": "--ui-status-danger-bg" },
289
+ "--ui-system-message-danger-border": { "kind": "token", "token": "--ui-status-danger" },
290
+ "--ui-system-message-danger-text": { "kind": "token", "token": "--ui-status-danger-text" },
291
+ "--ui-media-overlay-bg": { "kind": "literal", "value": "rgb(0 0 0 / 70%)" },
292
+ "--ui-media-overlay-text": { "kind": "literal", "value": "#ffffff" },
293
+ "--ui-media-control-bg": { "kind": "literal", "value": "rgb(255 255 255 / 20%)" },
294
+ "--ui-media-control-bg-hover": { "kind": "literal", "value": "rgb(255 255 255 / 30%)" },
295
+ "--ui-media-caption-bg": { "kind": "literal", "value": "rgb(0 0 0 / 50%)" },
296
+ "--ui-media-viewer-backdrop": { "kind": "literal", "value": "rgb(0 0 0 / 85%)" },
297
+ "--ui-media-viewer-base": { "kind": "literal", "value": "#262626" },
298
+ "--ui-media-strip-current": { "kind": "token", "token": "--ui-action-primary-bg" },
299
+ "--ui-time-slot-available-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
300
+ "--ui-time-slot-available-border": { "kind": "token", "token": "--ui-action-soft-accent-border" },
301
+ "--ui-time-slot-available-text": { "kind": "token", "token": "--ui-action-soft-accent-text" },
302
+ "--ui-time-slot-available-bg-hover": { "kind": "token", "token": "--ui-action-soft-accent-bg-hover" },
303
+ "--ui-time-slot-available-text-hover": { "kind": "token", "token": "--ui-action-soft-accent-text-hover" },
304
+ "--ui-time-slot-busy-bg": { "kind": "token", "token": "--ui-status-warning-bg" },
305
+ "--ui-time-slot-busy-border": { "kind": "token", "token": "--ui-status-warning" },
306
+ "--ui-time-slot-busy-text": { "kind": "token", "token": "--ui-status-warning-text" },
307
+ "--ui-time-slot-busy-selected-bg": { "kind": "token", "token": "--ui-action-warning-bg" },
308
+ "--ui-time-slot-busy-selected-text": { "kind": "token", "token": "--ui-action-warning-text" },
309
+ "--ui-time-slot-ring": { "kind": "token", "token": "--ui-border" },
310
+ "--ui-day-today-text": { "kind": "token", "token": "--ui-action-soft-accent-text" },
311
+ "--ui-day-selected-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
312
+ "--ui-day-selected-border": { "kind": "token", "token": "--ui-action-primary-bg" },
313
+ "--ui-day-detail-available": { "kind": "token", "token": "--ui-text-secondary" },
314
+ "--ui-day-detail-busy": { "kind": "token", "token": "--ui-text-secondary" },
315
+ "--ui-calendar-selected-text": { "kind": "token", "token": "--ui-text-primary" },
316
+ "--ui-calendar-conflict": { "kind": "token", "token": "--ui-status-danger" },
317
+ "--ui-calendar-free-border": { "kind": "token", "token": "--ui-action-soft-accent-border" },
318
+ "--ui-calendar-free-text": { "kind": "token", "token": "--ui-action-soft-accent-text" },
319
+ "--ui-board-count-bg": { "kind": "token", "token": "--ui-surface-subtle" },
320
+ "--ui-board-count-text": { "kind": "token", "token": "--ui-text-primary" },
321
+ "--ui-board-card-border-hover": { "kind": "token", "token": "--ui-action-primary-bg" },
322
+ "--ui-board-drop-bg": { "kind": "token", "token": "--ui-action-soft-accent-bg" },
323
+ "--ui-board-drop-border": { "kind": "token", "token": "--ui-action-primary-bg" },
324
+ "--ui-surface-inverse-end": { "kind": "token", "token": "--ui-surface-inverse" }
235
325
  },
236
326
  "shape": [
237
327
  "--ui-font-body",
@@ -276,7 +366,12 @@
276
366
  "--ui-font-weight-medium",
277
367
  "--ui-font-weight-bold",
278
368
  "--ui-alert-radius",
279
- "--ui-alert-padding"
369
+ "--ui-alert-padding",
370
+ "--ui-control-height-xs",
371
+ "--ui-radius-xs",
372
+ "--ui-list-item-radius",
373
+ "--ui-font-size-2xl",
374
+ "--ui-font-size-3xl"
280
375
  ],
281
376
  "brandComposition": [
282
377
  "--ui-brand-primary",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ksmv/ui-checks",
3
- "version": "0.21.1",
3
+ "version": "0.22.1",
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",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "devDependencies": { "@types/node": "26.6.2", "vitest": "5.0.1" },
29
29
  "engines": { "node": ">=22.14" },
30
- "repository": { "type": "git", "url": "https://github.com/AxisGov/ui.git", "directory": "packages/checks" },
30
+ "repository": { "type": "git", "url": "https://github.com/katosmv/ui.git", "directory": "packages/checks" },
31
31
  "homepage": "https://www.npmjs.com/package/@ksmv/ui-checks",
32
32
  "bugs": {},
33
33
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" }