origam 2.18.2 → 2.18.3
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
|
@@ -301,6 +301,84 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
301
301
|
|
|
302
302
|
---
|
|
303
303
|
|
|
304
|
+
#### `v-contrast` était silencieusement inerte sur fond opaque (#869)
|
|
305
|
+
|
|
306
|
+
La directive `v-contrast`, câblée sur 30 composants, n'émettait **jamais**
|
|
307
|
+
rien — ni classe, ni `color: !important`, ni `console.warn` — dès que fond
|
|
308
|
+
**et** texte étaient tous deux OPAQUES, c'est-à-dire la configuration par
|
|
309
|
+
défaut du DS. Deux ruptures indépendantes dans le code partagé, corrigeant
|
|
310
|
+
l'une ne suffisait pas sans l'autre :
|
|
311
|
+
|
|
312
|
+
1. **`toRgb()`** faisait **toujours** passer la couleur par un aller-retour
|
|
313
|
+
`canvas.fillStyle`, même quand `getComputedStyle` avait déjà rendu une
|
|
314
|
+
chaîne `rgb()/rgba()` exploitable. Le getter `fillStyle` de Chromium
|
|
315
|
+
sérialise toute couleur OPAQUE en hexadécimal `#rrggbb` (seul l'alpha < 1
|
|
316
|
+
ressort en `rgba(…)`), ce qui transformait une valeur déjà utilisable en
|
|
317
|
+
une valeur morte pour `rgbaParts`/`channelsOf`.
|
|
318
|
+
2. **`channelsOf()`** extrayait les canaux via `match(/[\d.]+/g)`, taillé
|
|
319
|
+
pour `rgb(r, g, b)` — une suite de chiffres hexadécimaux SANS séparateur
|
|
320
|
+
(ex. `#777777`, six chiffres) s'agrège en UN seul grand nombre au lieu de
|
|
321
|
+
trois, donc `channelsOf` rend `null` sur exactement la forme hex que la
|
|
322
|
+
rupture 1 pouvait encore produire (couleur nommée, `hsl()`, …
|
|
323
|
+
nécessitant légitimement le détour par le canvas).
|
|
324
|
+
|
|
325
|
+
**Correctif** : `toRgb()` court-circuite désormais le canvas quand l'entrée
|
|
326
|
+
est déjà `rgb()/rgba()` (le cas de la quasi-totalité des appels, puisqu'ils
|
|
327
|
+
lisent `getComputedStyle`), et le chemin canvas restant convertit son
|
|
328
|
+
résultat hexadécimal en `rgb()/rgba()` via un nouveau `hexToRgb()` avant de
|
|
329
|
+
le rendre — `toRgb()` ne rend plus jamais de hex. La mécanique de correction
|
|
330
|
+
elle-même (forcer noir/blanc, poser `data-origam-contrast-fixed`, journaliser
|
|
331
|
+
le ratio) n'est pas modifiée : elle fonctionnait déjà, elle n'était
|
|
332
|
+
simplement jamais atteinte.
|
|
333
|
+
|
|
334
|
+
**Preuve** — `packages/tests/e2e/contrast-directive.spec.ts` (Playwright,
|
|
335
|
+
Chromium réel — jsdom n'a pas de `canvas.getContext('2d')` fonctionnel dans
|
|
336
|
+
ce dépôt, donc ne peut ni reproduire ni vérifier ce bug) : contrôle positif
|
|
337
|
+
(`#777777` sur blanc, 4.48:1, opaque des deux côtés) déclenche désormais la
|
|
338
|
+
correction + le `console.warn` ; contrôle négatif (noir sur blanc, 21:1) ne
|
|
339
|
+
déclenche rien ; non-régression du chemin translucide (fond composé via
|
|
340
|
+
`resolvePaintedBackground`) inchangée. **A/B contre le commit parent** : 4
|
|
341
|
+
des 5 tests rougissent sur le code d'avant (le contrôle négatif reste vert
|
|
342
|
+
des deux côtés, comme attendu).
|
|
343
|
+
|
|
344
|
+
⚠️ **Rayon mesuré séparément, pas hérité de #871 — et le chiffre publié par
|
|
345
|
+
#871 contenait un artefact, corrigé depuis par #883.** Le chiffrage initial
|
|
346
|
+
du ticket (346 violations en mode sombre) datait d'avant #876. #876 avait
|
|
347
|
+
d'abord publié **189 → 11** ; en rejouant `audit/dark-contrast.audit.mjs`
|
|
348
|
+
avec la VRAIE directive (redirect du stub désactivé) au lieu de sa
|
|
349
|
+
reformulation mathématique, seuls **7 `console.warn` réels** se
|
|
350
|
+
déclenchaient sur les 32 configurations (30 composants × 8 identités × 2
|
|
351
|
+
modes, portées racine + sous-arbre) — écart signalé à l'auteur de #876, qui
|
|
352
|
+
l'a reproduit, creusé et corrigé sous #883 : **les bornes AVANT et APRÈS
|
|
353
|
+
étaient toutes les deux fausses (185 → 7, pas 189 → 11)**, la même erreur de
|
|
354
|
+
mesure existant déjà dans le comptage "avant". Le delta réel (178) n'a
|
|
355
|
+
jamais changé.
|
|
356
|
+
|
|
357
|
+
L'écart venait d'`origam-tooltip__content` (identité `apple`, root + subtree,
|
|
358
|
+
light + dark) et, découvert par #883, également d'`origam-bottom-nav` sur la
|
|
359
|
+
même identité. Fond réel : `color(srgb 0.898039 0.898039 0.905882 / 0.94)`
|
|
360
|
+
(translucide) ; `toRgb()` le résout correctement en `rgb(215, 215, 217)` une
|
|
361
|
+
fois composé sur son ancêtre opaque, contre du texte noir : **ratio 14.61:1,
|
|
362
|
+
conforme** — vérifié en appelant directement `toRgb()`/
|
|
363
|
+
`resolvePaintedBackground()` de la vraie directive sur cette page. La
|
|
364
|
+
fonction `parse()` de `dark-contrast.audit.mjs` (une réimplémentation
|
|
365
|
+
distincte, pas la directive) ne reconnaissait que `rgba?\(…\)` — elle
|
|
366
|
+
ignorait silencieusement cette couche `color(srgb …)`, sautait jusqu'à
|
|
367
|
+
l'ancêtre opaque suivant et rapportait à tort un faux "noir sur noir"
|
|
368
|
+
(ratio 1.00). **Ce n'était pas un défaut du DS ni de `v-contrast`** — la
|
|
369
|
+
même classe de lacune que ce ticket corrige, mais dans un autre fichier. Les
|
|
370
|
+
7 restantes (4 `origam-badge__badge`, 3 `origam-breadcrumb-item`) sont
|
|
371
|
+
confirmées identiques entre les deux mesures, et `sous 2:1` passe à **0** une
|
|
372
|
+
fois l'artefact retiré : plus rien de ce qui reste n'est proche d'invisible.
|
|
373
|
+
|
|
374
|
+
Non corrigé ici : `dark-contrast.audit.mjs` appartenait à #871/#876, pas à ce
|
|
375
|
+
ticket — signalé, reproduit et corrigé séparément sous #883 (ne pas citer
|
|
376
|
+
189 → 11, c'était faux des deux côtés).
|
|
377
|
+
|
|
378
|
+
**Rupture d'API** : aucune — `toRgb`/`channelsOf`/`hexToRgb` restent des
|
|
379
|
+
fonctions privées du module, non exportées ; la surface publique
|
|
380
|
+
(`setContrastConfig`, `v-contrast` par défaut) est inchangée.
|
|
381
|
+
|
|
304
382
|
## [2.18.0] - 2026-09-19
|
|
305
383
|
|
|
306
384
|
80 commits depuis `v2.17.1`. L'essentiel est du **correctif** : des tokens qui
|
|
@@ -34,10 +34,24 @@ function srgbToRgb(color) {
|
|
|
34
34
|
const a = m[4] !== void 0 ? parseFloat(m[4]) : 1;
|
|
35
35
|
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
36
36
|
}
|
|
37
|
+
function hexToRgb(hex) {
|
|
38
|
+
const m = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
|
|
39
|
+
if (!m) return null;
|
|
40
|
+
const h = m[1].length <= 4 ? [...m[1]].map(c => c + c).join("") : m[1];
|
|
41
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
42
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
43
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
44
|
+
if (h.length === 8) {
|
|
45
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
46
|
+
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
47
|
+
}
|
|
48
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
49
|
+
}
|
|
37
50
|
function toRgb(color) {
|
|
38
51
|
if (!color) return null;
|
|
39
52
|
const direct = srgbToRgb(color);
|
|
40
53
|
if (direct) return direct;
|
|
54
|
+
if (/^rgba?\(/i.test(color)) return color;
|
|
41
55
|
if (typeof document === "undefined") return color;
|
|
42
56
|
if (normCtx === void 0) normCtx = document.createElement("canvas").getContext("2d");
|
|
43
57
|
if (!normCtx) return color;
|
|
@@ -48,7 +62,7 @@ function toRgb(color) {
|
|
|
48
62
|
normCtx.fillStyle = color;
|
|
49
63
|
const onWhite = normCtx.fillStyle;
|
|
50
64
|
if (onBlack !== onWhite) return null;
|
|
51
|
-
return srgbToRgb(onBlack) ?? onBlack;
|
|
65
|
+
return srgbToRgb(onBlack) ?? hexToRgb(onBlack) ?? onBlack;
|
|
52
66
|
}
|
|
53
67
|
function rgbaParts(rgb) {
|
|
54
68
|
if (!rgb) return null;
|
|
@@ -22,10 +22,24 @@ function srgbToRgb(color) {
|
|
|
22
22
|
const a = m[4] !== void 0 ? parseFloat(m[4]) : 1;
|
|
23
23
|
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
24
24
|
}
|
|
25
|
+
function hexToRgb(hex) {
|
|
26
|
+
const m = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
|
|
27
|
+
if (!m) return null;
|
|
28
|
+
const h = m[1].length <= 4 ? [...m[1]].map((c) => c + c).join("") : m[1];
|
|
29
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
30
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
31
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
32
|
+
if (h.length === 8) {
|
|
33
|
+
const a = parseInt(h.slice(6, 8), 16) / 255;
|
|
34
|
+
return a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
35
|
+
}
|
|
36
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
37
|
+
}
|
|
25
38
|
function toRgb(color) {
|
|
26
39
|
if (!color) return null;
|
|
27
40
|
const direct = srgbToRgb(color);
|
|
28
41
|
if (direct) return direct;
|
|
42
|
+
if (/^rgba?\(/i.test(color)) return color;
|
|
29
43
|
if (typeof document === "undefined") return color;
|
|
30
44
|
if (normCtx === void 0) normCtx = document.createElement("canvas").getContext("2d");
|
|
31
45
|
if (!normCtx) return color;
|
|
@@ -36,7 +50,7 @@ function toRgb(color) {
|
|
|
36
50
|
normCtx.fillStyle = color;
|
|
37
51
|
const onWhite = normCtx.fillStyle;
|
|
38
52
|
if (onBlack !== onWhite) return null;
|
|
39
|
-
return srgbToRgb(onBlack) ?? onBlack;
|
|
53
|
+
return srgbToRgb(onBlack) ?? hexToRgb(onBlack) ?? onBlack;
|
|
40
54
|
}
|
|
41
55
|
function rgbaParts(rgb) {
|
|
42
56
|
if (!rgb) return null;
|