origam 2.18.1 → 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
|
@@ -177,10 +177,52 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
177
177
|
|
|
178
178
|
Rejoué sur 30 composants × 8 identités × 2 modes × 2 portées (racine ET
|
|
179
179
|
sous-arbre), soit **1 664 instances**, Chromium, contrôles positif et négatif
|
|
180
|
-
verts : **
|
|
181
|
-
**
|
|
182
|
-
palettes de marque de `packages/marketing`, aucune au DS
|
|
183
|
-
`pnpm -F @origam/tests audit:dark-contrast`.
|
|
180
|
+
verts : **185 → 7 violations AA**, dont sombre **182 → 4** et clair
|
|
181
|
+
**3 → 3** (aucune régression). Les 7 restantes appartiennent toutes aux
|
|
182
|
+
palettes de marque de `packages/marketing`, aucune au DS, et aucune ne
|
|
183
|
+
descend sous 2:1. Harnais : `pnpm -F @origam/tests audit:dark-contrast`.
|
|
184
|
+
|
|
185
|
+
⚠️ **Ces bornes ont d'abord été publiées à `189 → 11`, et les deux étaient
|
|
186
|
+
fausses de 4.** Le parseur de couleurs du harnais ne connaissait pas
|
|
187
|
+
`color(srgb …)`, que la palette `apple` émet : le fond translucide du
|
|
188
|
+
tooltip était pris pour « pas de fond », la remontée d'ancêtres sautait
|
|
189
|
+
jusqu'à la surface opaque et rapportait du noir sur noir à 1.00 — quatre
|
|
190
|
+
violations **fabriquées**, présentes des DEUX côtés de la mesure. Le
|
|
191
|
+
**delta de 178 n'a jamais été faux** ; seules les bornes l'étaient. Détail
|
|
192
|
+
et garde de non-récidive plus bas.
|
|
193
|
+
|
|
194
|
+
- **#871 — le harnais de contraste ne savait pas lire `color(srgb …)`, et
|
|
195
|
+
FABRIQUAIT donc des violations.** Son `parse()` ne reconnaissait que
|
|
196
|
+
`rgb()` / `rgba()`. Un fond qu'il ne sait pas lire n'est pas « ignoré » : il
|
|
197
|
+
est traité comme **non peint**, la remontée d'ancêtres saute l'élément et
|
|
198
|
+
composite contre une couche qui n'est pas celle que voit l'œil. Sur le
|
|
199
|
+
tooltip `apple` — `color(srgb 0.898 0.898 0.906 / 0.94)` sur une surface
|
|
200
|
+
noire — cela donnait du noir sur noir à **1.00** là où le rendu réel est
|
|
201
|
+
~`rgb(215,215,217)` sur noir. 4 instances (root + sous-arbre × clair +
|
|
202
|
+
sombre), des deux côtés de la mesure.
|
|
203
|
+
|
|
204
|
+
`parse()` reconnaît désormais `color(srgb r g b [/ a])`, avec **la même
|
|
205
|
+
expression régulière que `srgbToRgb` dans `contrast.directive.ts`** — la
|
|
206
|
+
vraie directive gérait déjà cette forme, et son commentaire le disait :
|
|
207
|
+
*« design tokens resolve to exactly this form in several themes »*.
|
|
208
|
+
|
|
209
|
+
⛔ **Un quatrième contrôle, parce que le correctif ponctuel ne suffit pas.**
|
|
210
|
+
Le harnais échoue maintenant (`$? = 1`) dès qu'une chaîne de couleur non
|
|
211
|
+
vide lui est illisible, en listant les formes rencontrées. Sans lui, la
|
|
212
|
+
prochaine syntaxe (`oklch()`, `lab()`, résidu de `color-mix()`) décalerait
|
|
213
|
+
le chiffre en silence exactement de la même façon. Contrôle positif du
|
|
214
|
+
garde lui-même : branche `srgb` désactivée → `$? = 1`, 8 formes listées, et
|
|
215
|
+
le compte réaffiche l'ancien **11** ; branche active → `$? = 0`, tout lu,
|
|
216
|
+
**7**.
|
|
217
|
+
|
|
218
|
+
⚠️ #871 listait déjà ce risque sous « non vérifié » — *« les couleurs
|
|
219
|
+
`oklch()` / `color(srgb …)` — aucun token actuel n'en emploie, mais un futur
|
|
220
|
+
thème invaliderait l'instrumentation »*. Un thème le faisait **déjà** au
|
|
221
|
+
moment où la phrase a été écrite. Écart signalé en marge de la **PR #882**
|
|
222
|
+
(#869, `v-contrast`) par l'agent qui rejouait ce harnais avec la vraie
|
|
223
|
+
directive au lieu du stub ; reproduit, remesuré et corrigé ici. Aucun ticket
|
|
224
|
+
propre n'a été ouvert : le défaut est dans l'instrumentation de #871, donc
|
|
225
|
+
il se règle sous #871.
|
|
184
226
|
|
|
185
227
|
- **#871 — `system-bar` et `tooltip` illisibles en sombre.** Les deux peignent
|
|
186
228
|
une surface sombre dans les DEUX modes (`neutral---700` / `neutral---800`)
|
|
@@ -220,8 +262,123 @@ conclure qu'elle ne l'est plus par défaut.
|
|
|
220
262
|
chacun. L'affirmation décrivait un état antérieur à #794 que plus rien ne
|
|
221
263
|
remesurait.
|
|
222
264
|
|
|
265
|
+
### Fixed
|
|
266
|
+
|
|
267
|
+
- **#371 (point 3) — le sélecteur « Items per page » du footer
|
|
268
|
+
DataTable n'avait aucun nom accessible**, déjà corrigé par `3f063d727`
|
|
269
|
+
(2026-09-10) — ce commit **citait #371 sans le fermer**, sixième
|
|
270
|
+
occurrence cette semaine du même motif (cf. #570, #682, #537, #371
|
|
271
|
+
point 1, #371 point 2/ADR-005). Re-mesuré ici avant tout travail sur les
|
|
272
|
+
points restants du ticket : le `<span>` visible est désormais associé au
|
|
273
|
+
`<origam-select>` via `aria-labelledby`, et la suite unitaire complète
|
|
274
|
+
reste verte (7120/7120) — aucun code de production retouché sur ce point,
|
|
275
|
+
seule la mesure manquait au tableau.
|
|
276
|
+
|
|
277
|
+
- **`<OrigamDataTableRow>` ne fuit plus `index` / `mobile` en attributs DOM**
|
|
278
|
+
(#371, point 4). `itemSlotProps()` (`OrigamDataTableRows.vue`) construisait
|
|
279
|
+
la ligne d'item avec deux clés qu'`IDataTableRowProps` ne déclare pas —
|
|
280
|
+
`index`, jamais lu par le composant, et `mobile`, un résidu d'avant
|
|
281
|
+
l'ajout du forwarding de `mobileBreakpoint` (chaque ligne calcule déjà son
|
|
282
|
+
propre `mobile` à partir de ce dernier). Une clé non déclarée tombe dans
|
|
283
|
+
`$attrs`, et le `v-bind="$attrs"` du `<tr>` racine la posait en attribut
|
|
284
|
+
DOM littéral sur **chaque** ligne rendue — `index="0" mobile="false"`.
|
|
285
|
+
Même mécanisme que le point 1 de ce ticket (déjà corrigé, `08c30693a`),
|
|
286
|
+
une famille plus loin. `aria-rowindex`, seul attribut voulu de ce canal,
|
|
287
|
+
continue de passer.
|
|
288
|
+
|
|
289
|
+
### Added
|
|
290
|
+
|
|
291
|
+
- **`mobileBreakpoint` a désormais un contrôle de story sur
|
|
292
|
+
`<OrigamDataTable>`** (#371, point 5). La prop — dont le défaut `'xs'`
|
|
293
|
+
corrige un bug de production documenté en commentaire
|
|
294
|
+
(`OrigamDataTable.vue:190-199` : sans lui, `useDisplay` retombait sur le
|
|
295
|
+
seuil global `'lg'` et forçait le rendu mobile empilé sur tout viewport
|
|
296
|
+
sous 1280px) — n'apparaissait dans aucune des 30 Variants de
|
|
297
|
+
`OrigamDataTable.story.vue`. Ajout de la Variant « Prop —
|
|
298
|
+
mobileBreakpoint » (même jeu d'options que celui déjà utilisé par
|
|
299
|
+
`OrigamDataTableRow.story.vue`) et d'une section « Responsive » +
|
|
300
|
+
ligne de table dans `OrigamDataTable.md`.
|
|
301
|
+
|
|
223
302
|
---
|
|
224
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
|
+
|
|
225
382
|
## [2.18.0] - 2026-09-19
|
|
226
383
|
|
|
227
384
|
80 commits depuis `v2.17.1`. L'essentiel est du **correctif** : des tokens qui
|
|
@@ -136,7 +136,6 @@
|
|
|
136
136
|
import OrigamDataTableGroupHeaderRow from "./OrigamDataTableGroupHeaderRow.vue";
|
|
137
137
|
import OrigamDataTableRow from "./OrigamDataTableRow.vue";
|
|
138
138
|
import OrigamSkeleton from "../Skeleton/OrigamSkeleton.vue";
|
|
139
|
-
import { useDisplay } from "../../composables/Commons/display.composable";
|
|
140
139
|
import { useExpanded } from "../../composables/DataTable/expand.composable";
|
|
141
140
|
import { useGroupBy } from "../../composables/DataTable/group.composable";
|
|
142
141
|
import { useHeaders } from "../../composables/DataTable/headers.composable";
|
|
@@ -186,7 +185,6 @@ const { columns } = useHeaders();
|
|
|
186
185
|
const { expandOnClick, toggleExpand, isExpanded } = useExpanded();
|
|
187
186
|
const { isSelected, toggleSelect } = useSelection();
|
|
188
187
|
const { toggleGroup, isGroupOpen } = useGroupBy();
|
|
189
|
-
const { mobile } = useDisplay(props);
|
|
190
188
|
const { startIndex } = usePagination();
|
|
191
189
|
const slotProps = (item, index) => {
|
|
192
190
|
return {
|
|
@@ -235,20 +233,32 @@ const itemSlotProps = (item, index) => {
|
|
|
235
233
|
onClick: expandOnClick.value ? () => {
|
|
236
234
|
toggleExpand(item);
|
|
237
235
|
} : void 0,
|
|
238
|
-
index,
|
|
239
236
|
item,
|
|
240
237
|
cellProps: props.cellProps,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
238
|
+
/*********************************************************
|
|
239
|
+
* Forward `mobileBreakpoint` — never `mobile` (#371)
|
|
240
|
+
*
|
|
241
|
+
* @description
|
|
242
|
+
* `IDataTableRowProps` (extends `IDisplayProps`)
|
|
243
|
+
* declares `mobileBreakpoint`, not `index` or
|
|
244
|
+
* `mobile`. Each row resolves its OWN `mobile` from
|
|
245
|
+
* `mobileBreakpoint` via its own `useDisplay(props)`
|
|
246
|
+
* call — passing a precomputed `mobile` here was
|
|
247
|
+
* always dead weight, and passing `index` was never
|
|
248
|
+
* read by the row at all. Both fell through
|
|
249
|
+
* `<OrigamDataTableRow>`'s undeclared-key path
|
|
250
|
+
* straight into `$attrs`, which its
|
|
251
|
+
* `v-bind="$attrs"` root then serialised as literal
|
|
252
|
+
* DOM attributes on every rendered `<tr>`:
|
|
253
|
+
* `index="0" mobile="false"`.
|
|
254
|
+
*
|
|
255
|
+
* @description
|
|
256
|
+
* A prior fix (mobileBreakpoint forwarding) added
|
|
257
|
+
* the correct key without removing the stale one it
|
|
258
|
+
* replaced — same shape as the point-1 closure leak
|
|
259
|
+
* in this same file's `groupHeaderRowProps()`.
|
|
260
|
+
********************************************************/
|
|
250
261
|
mobileBreakpoint: props.mobileBreakpoint,
|
|
251
|
-
mobile: mobile.value,
|
|
252
262
|
"aria-rowindex": startIndex.value + index + 2
|
|
253
263
|
},
|
|
254
264
|
getPrefixedEventHandlers(attrs, ":row", () => slotPropsLocal),
|
|
@@ -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;
|