@orianatech/pire 0.10.0 → 0.12.0

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.
@@ -17,9 +17,31 @@
17
17
  --red-10:#fdeaea;--red-30:#eda3a7;--red-50:#c0242e;--red-60:#991c25;
18
18
  --purple-10:#f0edfb;--purple-50:#5a3fc0;
19
19
 
20
- /* --- Text ------------------------------------------------------------- */
21
- --text-primary:var(--gray-90);--text-secondary:var(--gray-60);--text-tertiary:var(--gray-50);
20
+ /* --- Text ---------------------------------------------------------------
21
+ The scale sits one step darker than it used to. `--text-tertiary` was
22
+ gray-50, which held only 2.95:1 on the page surface and 3.22:1 on a card —
23
+ under the 4.5:1 that body text needs, in 21 rules of components.css.
24
+
25
+ Moving tertiary alone was not possible: the first value that clears 4.5:1 on
26
+ the page surface lands within 0.3 contrast points of secondary, so the two
27
+ would have read as one weight and the third level would have been decorative.
28
+ Both moved instead, which keeps three steps that are actually distinguishable.
29
+
30
+ `--text-secondary` and `--text-label` now resolve to the same gray-70. They
31
+ stay separate tokens because they mean different things — secondary is body
32
+ text, label names a field — and either may move again on its own. Do not
33
+ "simplify" one into the other.
34
+
35
+ `--text-disabled` deliberately stays at gray-40 and below 4.5:1: WCAG 1.4.3
36
+ exempts inactive components, and darkening it removes the signal that the
37
+ control is disabled. */
38
+ --text-primary:var(--gray-90);--text-secondary:var(--gray-70);--text-tertiary:var(--gray-60);
22
39
  --text-disabled:var(--gray-40);--text-inverse:var(--gray-0);--text-on-accent:var(--gray-0);
40
+ /* Text on a solid status fill — today only the IconButton count badge. Separate from
41
+ `--text-inverse` because the two flip in opposite directions: the shell and toasts are dark in
42
+ both themes and always want light text, while the status fills invert (red-60 in light,
43
+ red-30 in dark) and so want the opposite. Sharing one token put white on light pink at 1.94:1. */
44
+ --text-on-status:var(--gray-0);
23
45
  --text-link:var(--blue-60);--text-link-hover:var(--blue-70);--text-label:var(--gray-70);
24
46
  --text-numeric:var(--gray-90);
25
47
 
@@ -47,7 +69,40 @@
47
69
  --status-informative-fg:var(--blue-70);--status-informative-bg:var(--blue-10);--status-informative-border:var(--blue-30);
48
70
  --status-neutral-fg:var(--gray-70);--status-neutral-bg:var(--gray-10);--status-neutral-border:var(--gray-30);
49
71
 
50
- /* --- Data visualisation: ordered, colour-blind safe sequence ----------- */
51
- --viz-1:var(--blue-60);--viz-2:var(--teal-50);--viz-3:var(--purple-50);--viz-4:var(--amber-50);
52
- --viz-5:var(--blue-30);--viz-6:var(--gray-60);
72
+ /* --- Data visualisation ------------------------------------------------
73
+ A ramp of its own, kept apart from the semantic ramps on purpose: a series
74
+ colour must never be mistakable for a status colour, and the status steps are
75
+ already spoken for by positive / critical / negative.
76
+
77
+ Two steps per hue. -50 is the light-surface step, -40 the lighter dark-surface
78
+ one, because the two surfaces do not accept the same lightness — light takes
79
+ L 0.43-0.77, dark takes L 0.48-0.67 — so no single value can serve both. That
80
+ is why --viz-1..6 are re-pointed per theme in themes.css rather than fixed
81
+ here, and it is the reason this ramp exists at all.
82
+
83
+ Every step is validated against the real surfaces (#ffffff and #19222b) on
84
+ lightness, chroma, colour-blind separation, normal-vision separation and
85
+ contrast. The previous sequence — blue-60, teal-50, purple-50, amber-50,
86
+ blue-30, gray-60 — failed both: teal-50, blue-30 and gray-60 sat below the
87
+ chroma floor and read as grey, blue-30 held only 1.99:1 against white, and
88
+ blue-60 and purple-50 dropped under 2.5:1 on the dark surface. */
89
+ --viz-blue-50:#436cc8;--viz-blue-40:#5d83d4;
90
+ --viz-rust-50:#b94739;--viz-rust-40:#c86556;
91
+ --viz-cyan-50:#0083ad;--viz-cyan-40:#0097bc;
92
+ --viz-gold-50:#936b00;--viz-gold-40:#a58100;
93
+ --viz-violet-50:#9750a7;--viz-violet-40:#a86ab6;
94
+ --viz-jade-50:#008c64;--viz-jade-40:#009f7a;
95
+
96
+ /* Slot order is part of the result, not presentation: colour-blind separation is
97
+ measured between *adjacent* slots, and these six in a different order fall to
98
+ ΔE 1.6. Assign series in this order, and never cycle past --viz-6. */
99
+ --viz-1:var(--viz-blue-50);--viz-2:var(--viz-rust-50);--viz-3:var(--viz-cyan-50);
100
+ --viz-4:var(--viz-gold-50);--viz-5:var(--viz-violet-50);--viz-6:var(--viz-jade-50);
101
+
102
+ /* Everything past the sixth series. Deliberately a neutral: a seventh hue would
103
+ imply an identity the eye cannot reliably recover, so the tail folds into one
104
+ "other" bucket instead. */
105
+ --viz-other:var(--gray-40);
106
+ /* Grid and axis rules — recessive by design, so the data sits in front of them. */
107
+ --viz-grid:var(--gray-20);--viz-axis:var(--gray-30);
53
108
  }
@@ -1,8 +1,14 @@
1
1
  /* Theming contract: structure is hardcoded, colour is swappable.
2
2
  Consumers re-point the alias layer only — never the ramps, never geometry. */
3
3
  [data-theme="dark"]{
4
- --text-primary:var(--gray-05);--text-secondary:var(--gray-40);--text-tertiary:var(--gray-50);
5
- --text-disabled:var(--gray-60);--text-inverse:var(--gray-100);--text-label:var(--gray-30);
4
+ /* Shifted one step lighter, mirroring the light scale's move a step darker and for the same
5
+ reason: tertiary at gray-50 held only 3.77:1 on `--surface-raised`, the darkest surface text
6
+ ever lands on. Menus and popovers use that surface, so it is not an edge case. */
7
+ --text-primary:var(--gray-05);--text-secondary:var(--gray-30);--text-tertiary:var(--gray-40);
8
+ /* --text-inverse stays light here. It names text on a permanently dark ground — the shell bar,
9
+ toasts, tooltips, avatars — not "the opposite of the current theme". Pointing it at gray-100
10
+ put near-black text on the near-black shell: 1.02:1, invisible, and shipped. */
11
+ --text-disabled:var(--gray-60);--text-inverse:var(--gray-05);--text-on-status:var(--gray-100);--text-label:var(--gray-30);
6
12
  --text-link:var(--blue-30);--text-link-hover:var(--blue-20);--text-numeric:var(--gray-05);
7
13
  --surface-page:var(--gray-100);--surface-card:var(--gray-90);--surface-sunken:#111922;
8
14
  --surface-raised:var(--gray-80);--surface-hover:var(--gray-80);--surface-active:var(--gray-70);
@@ -11,13 +17,32 @@
11
17
  --surface-nav:var(--gray-90);--surface-toolbar:var(--gray-90);--surface-overlay:rgba(4,16,31,.66);
12
18
  --border-subtle:#28333d;--border-default:var(--gray-70);--border-strong:var(--gray-50);
13
19
  --border-field:var(--gray-50);--border-focus:var(--blue-30);--border-accent:var(--blue-30);
14
- --accent-bg:var(--blue-50);--accent-bg-hover:var(--blue-40);--accent-bg-active:var(--blue-30);
20
+ /* Hover and press deepen here rather than lighten, the same direction as the light theme. Going
21
+ lighter is the usual dark-theme reflex and it broke the contrast: the fill lightened while
22
+ `--accent-fg` stayed white, so a hovered primary button was 3.19:1 and a pressed one 1.99:1.
23
+ Only CI caught it, because a local run leaves the pointer off the button. */
24
+ --accent-bg:var(--blue-50);--accent-bg-hover:var(--blue-60);--accent-bg-active:var(--blue-70);
15
25
  --accent-subtle-bg:var(--blue-90);--accent-subtle-fg:var(--blue-20);
16
26
  --status-positive-fg:var(--green-30);--status-positive-bg:#0e2b1c;--status-positive-border:var(--green-50);
17
27
  --status-critical-fg:var(--amber-30);--status-critical-bg:#31210a;--status-critical-border:var(--amber-50);
18
28
  --status-negative-fg:var(--red-30);--status-negative-bg:#331416;--status-negative-border:var(--red-50);
19
29
  --status-informative-fg:var(--blue-30);--status-informative-bg:var(--blue-90);--status-informative-border:var(--blue-50);
20
30
  --status-neutral-fg:var(--gray-30);--status-neutral-bg:var(--gray-80);--status-neutral-border:var(--gray-70);
31
+ /* Series colours are re-pointed, not inverted: the -40 steps are their own
32
+ validated values against #19222b. Keep the slots in the same hue order as
33
+ light, so a series that is rust in one theme is rust in the other. */
34
+ --viz-1:var(--viz-blue-40);--viz-2:var(--viz-rust-40);--viz-3:var(--viz-cyan-40);
35
+ --viz-4:var(--viz-gold-40);--viz-5:var(--viz-violet-40);--viz-6:var(--viz-jade-40);
36
+ --viz-other:var(--gray-50);--viz-grid:#28333d;--viz-axis:var(--gray-70);
21
37
  }
22
- /* Accessibility scope: 3:1 non-text contrast on every control edge */
23
- [data-contrast="high"]{--border-subtle:var(--gray-50);--border-default:var(--gray-70);--border-field:var(--gray-80);--text-secondary:var(--gray-80)}
38
+ /* Accessibility scope: 3:1 non-text contrast on every control edge.
39
+ Tertiary darkens alongside secondary here — it did not before, which left the
40
+ scope's lightest text at the same value as the default theme's. */
41
+ [data-contrast="high"]{--border-subtle:var(--gray-50);--border-default:var(--gray-70);--border-field:var(--gray-80);--text-secondary:var(--gray-80);--text-tertiary:var(--gray-70)}
42
+
43
+ /* The high-contrast scope above darkens text, which is only correct on a light ground. Combined
44
+ with the dark theme it inverted the intent: `--text-secondary` resolved to gray-80, the same
45
+ value as `--surface-raised`, giving 1.00:1 — text the exact colour of what it sits on. Neither
46
+ scope is gated alone, so nothing caught it; both are gated now.
47
+ Order matters — this must follow both blocks it corrects. */
48
+ [data-theme="dark"][data-contrast="high"]{--text-secondary:var(--gray-05);--text-tertiary:var(--gray-20);--border-subtle:var(--gray-50);--border-default:var(--gray-40);--border-field:var(--gray-30)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orianatech/pire",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Oriana Pire Design System — React components for ERP interfaces, built on React Aria.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -97,6 +97,9 @@
97
97
  "test": "vitest run",
98
98
  "test:watch": "vitest",
99
99
  "test:storybook": "vitest run --project=storybook",
100
+ "test:a11y:dark": "STORYBOOK_THEME=dark vitest run --project=storybook",
101
+ "test:a11y:contrast": "STORYBOOK_CONTRAST=high vitest run --project=storybook",
102
+ "test:a11y:dark-contrast": "STORYBOOK_THEME=dark STORYBOOK_CONTRAST=high vitest run --project=storybook",
100
103
  "test:unit": "vitest run --project=unit",
101
104
  "pack:check": "publint --strict && attw --pack . --exclude-entrypoints ./styles.css ./tokens.css ./components.css",
102
105
  "fonts:vendor": "node scripts/vendor-fonts.mjs"