openvibers/openvibe.shared Commits
Status words read on light themes (4.83.0) (#237)
Changes
@@@ -4,6 +4,17 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semvebreaking change to any exported module, browser global or served file name is a new major.A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).++## 4.83.0 — 2026-11-01++++- **Status words read on light themes.**++ - The kit wrote a status in the theme's fill colours (`--success`, `--warning`, `--danger`). On Daylight, the badges++ "paid" and "expiring" on openvibe.design/kit were green and amber on white at 2.28:1 and 2.15:1.++ - `--ovk-ok-text`, `--ovk-warn-text` and `--ovk-bad-text` follow the theme's readable `--success-text`,++ `--warning-text` and `--danger-text`. They are now 5.4:1 on Daylight, and unchanged on the dark default.++ - The kit uses them for words: badges, the danger button, done and danger icon buttons, the danger fold, the menu's++ danger item, notice icons, and kit-data's rank deltas and pills.++ - Fills and borders keep the fill colours. `room.css` carries the same tokens.++## 4.82.0 — 2026-11-01- **The motion tokens are on every page, and Reduced motion reaches every token-timed transition** (plan L45).@@@ -2,7 +2,7 @@> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.--**Status:** alpha, v4.82.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed++**Status:** alpha, v4.83.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployedconsumer installs a tagged release and none keeps a vendored copy([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` namesthe tag it pins.@@@ -4,7 +4,7 @@"code": true,"wave": "W2","package": "openvibe-shared",-- "version": "4.82.0",++ "version": "4.83.0","plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)","deployed": false,"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",@@@ -132,7 +132,7 @@.ovk-rank-val > small { color: var(--ovk-muted); font-size: .74rem; font-variant-numeric: tabular-nums; white-space: nowrap; }.ovk-rank-delta { grid-column: 5; display: inline-flex; align-items: center; gap: 1px; min-width: 2.6em; justify-content: flex-end; font-size: .78rem; font-weight: 800; font-variant-numeric: tabular-nums; }.ovk-rank-delta .ovk-i { width: 13px; height: 13px; stroke-width: 3; }--.ovk-rank-delta.is-up { color: var(--ovk-ok); } .ovk-rank-delta.is-down { color: var(--ovk-bad); } .ovk-rank-delta.is-new { color: var(--ovk-accent); text-transform: uppercase; font-size: .68rem; letter-spacing: .04em; }++.ovk-rank-delta.is-up { color: var(--ovk-ok-text, var(--ovk-ok)); } .ovk-rank-delta.is-down { color: var(--ovk-bad-text, var(--ovk-bad)); } .ovk-rank-delta.is-new { color: var(--ovk-accent); text-transform: uppercase; font-size: .68rem; letter-spacing: .04em; }.ovk-rank-bar { grid-column: 3 / -1; grid-row: 2; height: 4px; border-radius: 999px; background: color-mix(in srgb, var(--ovk-line) 60%, transparent); overflow: hidden; }.ovk-rank-bar::after { content: ''; display: block; height: 100%; width: calc(var(--v, 0) * 100%); border-radius: inherit; background: color-mix(in srgb, var(--ovk-c1) 55%, transparent); }.ovk-rank-row.is-top .ovk-rank-bar::after { background: var(--ovk-c1); }@@@ -233,9 +233,9 @@ a.ovk-tool-crumb:hover { color: var(--ovk-text); }.ovk-answer-body > :last-child { margin-bottom: 0; }.ovk-answer-next { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 12px; }.ovk-pill { display: inline-block; margin-left: 4px; padding: 1px 8px; border-radius: 999px; font-size: .74rem; font-weight: 700; vertical-align: 1px; white-space: nowrap; color: var(--ovk-accent); background: color-mix(in srgb, var(--ovk-accent) 13%, transparent); }--.ovk-pill--ok { color: var(--ovk-ok); background: color-mix(in srgb, var(--ovk-ok) 14%, transparent); }--.ovk-pill--warn { color: var(--ovk-warn); background: color-mix(in srgb, var(--ovk-warn) 15%, transparent); }--.ovk-pill--bad { color: var(--ovk-bad); background: color-mix(in srgb, var(--ovk-bad) 14%, transparent); }++.ovk-pill--ok { color: var(--ovk-ok-text, var(--ovk-ok)); background: color-mix(in srgb, var(--ovk-ok) 14%, transparent); }++.ovk-pill--warn { color: var(--ovk-warn-text, var(--ovk-warn)); background: color-mix(in srgb, var(--ovk-warn) 15%, transparent); }++.ovk-pill--bad { color: var(--ovk-bad-text, var(--ovk-bad)); background: color-mix(in srgb, var(--ovk-bad) 14%, transparent); }.ovk-family-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 8px; }.ovk-family-head.ovk-family-head h2 { margin: 0; font-family: inherit; font-size: .95rem; font-weight: 750; color: var(--ovk-text); }@@@ -24,6 +24,11 @@--ovk-ok: var(--success, #22c55e);--ovk-warn: var(--warning, #f59e0b);--ovk-bad: var(--danger, var(--error, #ef4444));++ /* Status in words (4.83.0): a theme's -text status colours are its readable ones; the fills above are for shapes. On++ * a light theme the fill green or amber on white was about 2:1 (openvibe.design/kit's "paid" and "expiring"). */++ --ovk-ok-text: var(--success-text, var(--ovk-ok));++ --ovk-warn-text: var(--warning-text, var(--ovk-warn));++ --ovk-bad-text: var(--danger-text, var(--ovk-bad));--ovk-live: var(--live-red, #ef4444);/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */@@@ -95,7 +100,7 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -.ovk-btn--quiet { background: var(--ovk-panel); color: var(--ovk-text); border-color: var(--ovk-line); }.ovk-btn--quiet:hover { border-color: var(--ovk-accent); }.ovk-btn--ghost { background: transparent; color: var(--ovk-accent); padding-left: 8px; padding-right: 8px; }--.ovk-btn--danger { background: transparent; color: var(--ovk-bad); border-color: color-mix(in srgb, var(--ovk-bad) 45%, transparent); }++.ovk-btn--danger { background: transparent; color: var(--ovk-bad-text); border-color: color-mix(in srgb, var(--ovk-bad) 45%, transparent); }.ovk-btn--danger:hover { background: color-mix(in srgb, var(--ovk-bad) 12%, transparent); }.ovk-btn--small { min-height: 36px; padding: 6px 13px; border-radius: 999px; font-size: .88rem; }.ovk-btn:focus-visible, .ovk-icon-btn:focus-visible, .ovk-go:focus-visible { outline: 2px solid var(--ovk-accent); outline-offset: 2px; }@@@ -104,15 +109,15 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -.ovk-icon-btn::-webkit-details-marker { display: none; }.ovk-icon-btn:hover { color: var(--ovk-text); background: var(--ovk-panel-2); border-color: var(--ovk-line); }.ovk-icon-btn[hidden] { display: none; }--.ovk-icon-btn.is-done { color: var(--ovk-ok); }--.ovk-icon-btn--danger:hover { color: var(--ovk-bad); }++.ovk-icon-btn.is-done { color: var(--ovk-ok-text); }++.ovk-icon-btn--danger:hover { color: var(--ovk-bad-text); }.ovk-icon-btn .ovk-i { width: 20px; height: 20px; }@media (pointer: coarse) { .ovk-icon-btn { width: var(--ovk-tap); height: var(--ovk-tap); } }.ovk-badge { display: inline-flex; align-items: center; flex: none; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--ovk-line); font-size: .7rem; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; color: var(--ovk-muted); white-space: nowrap; }.ovk-badge--accent { color: var(--ovk-accent); border-color: color-mix(in srgb, var(--ovk-accent) 50%, transparent); }--.ovk-badge--ok { color: var(--ovk-ok); border-color: color-mix(in srgb, var(--ovk-ok) 50%, transparent); }--.ovk-badge--warn { color: var(--ovk-warn); border-color: color-mix(in srgb, var(--ovk-warn) 50%, transparent); }--.ovk-badge--bad { color: var(--ovk-bad); border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }++.ovk-badge--ok { color: var(--ovk-ok-text); border-color: color-mix(in srgb, var(--ovk-ok) 50%, transparent); }++.ovk-badge--warn { color: var(--ovk-warn-text); border-color: color-mix(in srgb, var(--ovk-warn) 50%, transparent); }++.ovk-badge--bad { color: var(--ovk-bad-text); border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }.ovk-badge--live { color: #fff; background: var(--ovk-live); border-color: transparent; }.ovk-dot { display: inline-grid; place-items: center; flex: none; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box; border-radius: 999px; background: var(--ovk-fill); color: var(--ovk-on-fill); font-size: .72rem; font-weight: 800; }@@@ -181,14 +186,14 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -.ovk-fold-body { padding: 12px 14px 14px; }.ovk-fold-body > :first-child { margin-top: 0; }.ovk-fold-body > :last-child { margin-bottom: 0; }--.ovk-fold--danger > summary { color: var(--ovk-bad); }++.ovk-fold--danger > summary { color: var(--ovk-bad-text); }.ovk-more { position: relative; display: inline-block; }.ovk-more > summary { list-style: none; }.ovk-pop { position: absolute; right: 0; top: calc(100% + 4px); z-index: 60; min-width: 210px; padding: 6px; box-sizing: border-box; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); box-shadow: var(--ov-elev-3); }.ovk-pop form { margin: 0; }.ovk-pop-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; box-sizing: border-box; padding: 8px 10px; border: 0; border-radius: max(0px, calc(var(--ov-radius-1) - 2px)); background: transparent; color: var(--ovk-text); font: inherit; font-weight: 600; text-align: left; text-decoration: none; cursor: pointer; }.ovk-pop-item:hover, .ovk-pop-item:focus-visible { background: var(--ovk-panel-2); outline: none; }--.ovk-pop-item--danger { color: var(--ovk-bad); }++.ovk-pop-item--danger { color: var(--ovk-bad-text); }/* ── Settings rows: what it does on the left, its control on the right ── */.ovk-set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px; box-sizing: border-box; padding: 8px 14px; border-top: 1px solid var(--ovk-line); color: var(--ovk-text); cursor: pointer; }@@@ -272,10 +277,10 @@ div.ovk-set-row { cursor: default; }.ovk-notice { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 12px; padding: 10px 14px; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); color: var(--ovk-text); font-size: .92rem; }.ovk-notice > .ovk-i { margin-top: 2px; }.ovk-notice--ok { border-color: color-mix(in srgb, var(--ovk-ok) 45%, transparent); }--.ovk-notice--ok > .ovk-i { color: var(--ovk-ok); }++.ovk-notice--ok > .ovk-i { color: var(--ovk-ok-text); }.ovk-notice--error { border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }--.ovk-notice--error > .ovk-i { color: var(--ovk-bad); }--.ovk-notice--warn > .ovk-i { color: var(--ovk-warn); }++.ovk-notice--error > .ovk-i { color: var(--ovk-bad-text); }++.ovk-notice--warn > .ovk-i { color: var(--ovk-warn-text); }.ovk-notice--info > .ovk-i { color: var(--ovk-accent); }.ovk-snip { position: relative; }.ovk-snip > .ovk-icon-btn { position: absolute; top: 6px; right: 6px; z-index: 1; width: 34px; height: 34px; background: var(--ovk-panel); border-color: var(--ovk-line); }@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "4.82.0",++ "version": "4.83.0","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","bin": {@@@ -21,6 +21,11 @@--ovk-ok: var(--success, #22c55e);--ovk-warn: var(--warning, #f59e0b);--ovk-bad: var(--danger, var(--error, #ef4444));++ /* Status in words (4.83.0): a theme's -text status colours are its readable ones; the fills above are for shapes. On++ * a light theme the fill green or amber on white was about 2:1 (openvibe.design/kit's "paid" and "expiring"). */++ --ovk-ok-text: var(--success-text, var(--ovk-ok));++ --ovk-warn-text: var(--warning-text, var(--ovk-warn));++ --ovk-bad-text: var(--danger-text, var(--ovk-bad));--ovk-live: var(--live-red, #ef4444);/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */@@@ -33,4 +33,9 @@ assert.match(kit, /@media \(prefers-reduced-motion: reduce\) \{ :root:root \{ --assert.match(kit, /:is\(\[data-theme=paper\][^{]*\) \{\s*--ov-dur-1: \.15s;/, 'Paper is calm');const fx = fs.readFileSync(path.join(__dirname, '..', 'ov-fx.js'), 'utf8');assert.ok(fx.includes('--ov-dur-1:.12s;--ov-dur-2:.2s;--ov-dur-3:.32s;--ov-dur-4:.52s'), 'the same values as ov-fx.js');--console.log('kit-shape: tokens, the kit on them, the personalities and the motion');++// Status in words (4.83.0): the kit writes a status in the theme's readable -text colour, never its fill colour.++assert.ok(kit.includes('--ovk-ok-text: var(--success-text, var(--ovk-ok));') && kit.includes('--ovk-bad-text: var(--danger-text, var(--ovk-bad));'), 'the -text status tokens');++assert.doesNotMatch(kit, /[^-\w]color: var\(--ovk-(ok|warn|bad)\)/, 'no word in a fill colour');++const data = fs.readFileSync(path.join(__dirname, '..', 'kit-data.css'), 'utf8');++assert.doesNotMatch(data, /[^-\w]color: var\(--ovk-(ok|warn|bad)\)/, 'nor in kit-data.css');++console.log('kit-shape: tokens, the kit on them, the personalities, the motion and status words');
@@ -4,6 +4,17 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -4,6 +4,17 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
+## 4.83.0 — 2026-11-01
+
+- **Status words read on light themes.**
+ - The kit wrote a status in the theme's fill colours (`--success`, `--warning`, `--danger`). On Daylight, the badges
+ "paid" and "expiring" on openvibe.design/kit were green and amber on white at 2.28:1 and 2.15:1.
+ - `--ovk-ok-text`, `--ovk-warn-text` and `--ovk-bad-text` follow the theme's readable `--success-text`,
+ `--warning-text` and `--danger-text`. They are now 5.4:1 on Daylight, and unchanged on the dark default.
+ - The kit uses them for words: badges, the danger button, done and danger icon buttons, the danger fold, the menu's
+ danger item, notice icons, and kit-data's rank deltas and pills.
+ - Fills and borders keep the fill colours. `room.css` carries the same tokens.
+
## 4.82.0 — 2026-11-01
## 4.82.0 — 2026-11-01
- **The motion tokens are on every page, and Reduced motion reaches every token-timed transition** (plan L45).
- **The motion tokens are on every page, and Reduced motion reaches every token-timed transition** (plan L45).
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
-**Status:** alpha, v4.82.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
+**Status:** alpha, v4.83.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
consumer installs a tagged release and none keeps a vendored copy
consumer installs a tagged release and none keeps a vendored copy
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
the tag it pins.
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "4.82.0",
+ "version": "4.83.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"deployed": false,
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
@@ -132,7 +132,7 @@
@@ -132,7 +132,7 @@
.ovk-rank-val > small { color: var(--ovk-muted); font-size: .74rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ovk-rank-val > small { color: var(--ovk-muted); font-size: .74rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ovk-rank-delta { grid-column: 5; display: inline-flex; align-items: center; gap: 1px; min-width: 2.6em; justify-content: flex-end; font-size: .78rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.ovk-rank-delta { grid-column: 5; display: inline-flex; align-items: center; gap: 1px; min-width: 2.6em; justify-content: flex-end; font-size: .78rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.ovk-rank-delta .ovk-i { width: 13px; height: 13px; stroke-width: 3; }
.ovk-rank-delta .ovk-i { width: 13px; height: 13px; stroke-width: 3; }
-.ovk-rank-delta.is-up { color: var(--ovk-ok); } .ovk-rank-delta.is-down { color: var(--ovk-bad); } .ovk-rank-delta.is-new { color: var(--ovk-accent); text-transform: uppercase; font-size: .68rem; letter-spacing: .04em; }
+.ovk-rank-delta.is-up { color: var(--ovk-ok-text, var(--ovk-ok)); } .ovk-rank-delta.is-down { color: var(--ovk-bad-text, var(--ovk-bad)); } .ovk-rank-delta.is-new { color: var(--ovk-accent); text-transform: uppercase; font-size: .68rem; letter-spacing: .04em; }
.ovk-rank-bar { grid-column: 3 / -1; grid-row: 2; height: 4px; border-radius: 999px; background: color-mix(in srgb, var(--ovk-line) 60%, transparent); overflow: hidden; }
.ovk-rank-bar { grid-column: 3 / -1; grid-row: 2; height: 4px; border-radius: 999px; background: color-mix(in srgb, var(--ovk-line) 60%, transparent); overflow: hidden; }
.ovk-rank-bar::after { content: ''; display: block; height: 100%; width: calc(var(--v, 0) * 100%); border-radius: inherit; background: color-mix(in srgb, var(--ovk-c1) 55%, transparent); }
.ovk-rank-bar::after { content: ''; display: block; height: 100%; width: calc(var(--v, 0) * 100%); border-radius: inherit; background: color-mix(in srgb, var(--ovk-c1) 55%, transparent); }
.ovk-rank-row.is-top .ovk-rank-bar::after { background: var(--ovk-c1); }
.ovk-rank-row.is-top .ovk-rank-bar::after { background: var(--ovk-c1); }
@@ -233,9 +233,9 @@ a.ovk-tool-crumb:hover { color: var(--ovk-text); }
@@ -233,9 +233,9 @@ a.ovk-tool-crumb:hover { color: var(--ovk-text); }
.ovk-answer-body > :last-child { margin-bottom: 0; }
.ovk-answer-body > :last-child { margin-bottom: 0; }
.ovk-answer-next { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 12px; }
.ovk-answer-next { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 12px; }
.ovk-pill { display: inline-block; margin-left: 4px; padding: 1px 8px; border-radius: 999px; font-size: .74rem; font-weight: 700; vertical-align: 1px; white-space: nowrap; color: var(--ovk-accent); background: color-mix(in srgb, var(--ovk-accent) 13%, transparent); }
.ovk-pill { display: inline-block; margin-left: 4px; padding: 1px 8px; border-radius: 999px; font-size: .74rem; font-weight: 700; vertical-align: 1px; white-space: nowrap; color: var(--ovk-accent); background: color-mix(in srgb, var(--ovk-accent) 13%, transparent); }
-.ovk-pill--ok { color: var(--ovk-ok); background: color-mix(in srgb, var(--ovk-ok) 14%, transparent); }
-.ovk-pill--warn { color: var(--ovk-warn); background: color-mix(in srgb, var(--ovk-warn) 15%, transparent); }
-.ovk-pill--bad { color: var(--ovk-bad); background: color-mix(in srgb, var(--ovk-bad) 14%, transparent); }
+.ovk-pill--ok { color: var(--ovk-ok-text, var(--ovk-ok)); background: color-mix(in srgb, var(--ovk-ok) 14%, transparent); }
+.ovk-pill--warn { color: var(--ovk-warn-text, var(--ovk-warn)); background: color-mix(in srgb, var(--ovk-warn) 15%, transparent); }
+.ovk-pill--bad { color: var(--ovk-bad-text, var(--ovk-bad)); background: color-mix(in srgb, var(--ovk-bad) 14%, transparent); }
.ovk-family-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 8px; }
.ovk-family-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 8px; }
.ovk-family-head.ovk-family-head h2 { margin: 0; font-family: inherit; font-size: .95rem; font-weight: 750; color: var(--ovk-text); }
.ovk-family-head.ovk-family-head h2 { margin: 0; font-family: inherit; font-size: .95rem; font-weight: 750; color: var(--ovk-text); }
@@ -24,6 +24,11 @@
@@ -24,6 +24,11 @@
--ovk-ok: var(--success, #22c55e);
--ovk-ok: var(--success, #22c55e);
--ovk-warn: var(--warning, #f59e0b);
--ovk-warn: var(--warning, #f59e0b);
--ovk-bad: var(--danger, var(--error, #ef4444));
--ovk-bad: var(--danger, var(--error, #ef4444));
+ /* Status in words (4.83.0): a theme's -text status colours are its readable ones; the fills above are for shapes. On
+ * a light theme the fill green or amber on white was about 2:1 (openvibe.design/kit's "paid" and "expiring"). */
+ --ovk-ok-text: var(--success-text, var(--ovk-ok));
+ --ovk-warn-text: var(--warning-text, var(--ovk-warn));
+ --ovk-bad-text: var(--danger-text, var(--ovk-bad));
--ovk-live: var(--live-red, #ef4444);
--ovk-live: var(--live-red, #ef4444);
/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme
/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme
* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */
* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */
@@ -95,7 +100,7 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
@@ -95,7 +100,7 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
.ovk-btn--quiet { background: var(--ovk-panel); color: var(--ovk-text); border-color: var(--ovk-line); }
.ovk-btn--quiet { background: var(--ovk-panel); color: var(--ovk-text); border-color: var(--ovk-line); }
.ovk-btn--quiet:hover { border-color: var(--ovk-accent); }
.ovk-btn--quiet:hover { border-color: var(--ovk-accent); }
.ovk-btn--ghost { background: transparent; color: var(--ovk-accent); padding-left: 8px; padding-right: 8px; }
.ovk-btn--ghost { background: transparent; color: var(--ovk-accent); padding-left: 8px; padding-right: 8px; }
-.ovk-btn--danger { background: transparent; color: var(--ovk-bad); border-color: color-mix(in srgb, var(--ovk-bad) 45%, transparent); }
+.ovk-btn--danger { background: transparent; color: var(--ovk-bad-text); border-color: color-mix(in srgb, var(--ovk-bad) 45%, transparent); }
.ovk-btn--danger:hover { background: color-mix(in srgb, var(--ovk-bad) 12%, transparent); }
.ovk-btn--danger:hover { background: color-mix(in srgb, var(--ovk-bad) 12%, transparent); }
.ovk-btn--small { min-height: 36px; padding: 6px 13px; border-radius: 999px; font-size: .88rem; }
.ovk-btn--small { min-height: 36px; padding: 6px 13px; border-radius: 999px; font-size: .88rem; }
.ovk-btn:focus-visible, .ovk-icon-btn:focus-visible, .ovk-go:focus-visible { outline: 2px solid var(--ovk-accent); outline-offset: 2px; }
.ovk-btn:focus-visible, .ovk-icon-btn:focus-visible, .ovk-go:focus-visible { outline: 2px solid var(--ovk-accent); outline-offset: 2px; }
@@ -104,15 +109,15 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
@@ -104,15 +109,15 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
.ovk-icon-btn::-webkit-details-marker { display: none; }
.ovk-icon-btn::-webkit-details-marker { display: none; }
.ovk-icon-btn:hover { color: var(--ovk-text); background: var(--ovk-panel-2); border-color: var(--ovk-line); }
.ovk-icon-btn:hover { color: var(--ovk-text); background: var(--ovk-panel-2); border-color: var(--ovk-line); }
.ovk-icon-btn[hidden] { display: none; }
.ovk-icon-btn[hidden] { display: none; }
-.ovk-icon-btn.is-done { color: var(--ovk-ok); }
-.ovk-icon-btn--danger:hover { color: var(--ovk-bad); }
+.ovk-icon-btn.is-done { color: var(--ovk-ok-text); }
+.ovk-icon-btn--danger:hover { color: var(--ovk-bad-text); }
.ovk-icon-btn .ovk-i { width: 20px; height: 20px; }
.ovk-icon-btn .ovk-i { width: 20px; height: 20px; }
@media (pointer: coarse) { .ovk-icon-btn { width: var(--ovk-tap); height: var(--ovk-tap); } }
@media (pointer: coarse) { .ovk-icon-btn { width: var(--ovk-tap); height: var(--ovk-tap); } }
.ovk-badge { display: inline-flex; align-items: center; flex: none; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--ovk-line); font-size: .7rem; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; color: var(--ovk-muted); white-space: nowrap; }
.ovk-badge { display: inline-flex; align-items: center; flex: none; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--ovk-line); font-size: .7rem; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; color: var(--ovk-muted); white-space: nowrap; }
.ovk-badge--accent { color: var(--ovk-accent); border-color: color-mix(in srgb, var(--ovk-accent) 50%, transparent); }
.ovk-badge--accent { color: var(--ovk-accent); border-color: color-mix(in srgb, var(--ovk-accent) 50%, transparent); }
-.ovk-badge--ok { color: var(--ovk-ok); border-color: color-mix(in srgb, var(--ovk-ok) 50%, transparent); }
-.ovk-badge--warn { color: var(--ovk-warn); border-color: color-mix(in srgb, var(--ovk-warn) 50%, transparent); }
-.ovk-badge--bad { color: var(--ovk-bad); border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }
+.ovk-badge--ok { color: var(--ovk-ok-text); border-color: color-mix(in srgb, var(--ovk-ok) 50%, transparent); }
+.ovk-badge--warn { color: var(--ovk-warn-text); border-color: color-mix(in srgb, var(--ovk-warn) 50%, transparent); }
+.ovk-badge--bad { color: var(--ovk-bad-text); border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }
.ovk-badge--live { color: #fff; background: var(--ovk-live); border-color: transparent; }
.ovk-badge--live { color: #fff; background: var(--ovk-live); border-color: transparent; }
.ovk-dot { display: inline-grid; place-items: center; flex: none; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box; border-radius: 999px; background: var(--ovk-fill); color: var(--ovk-on-fill); font-size: .72rem; font-weight: 800; }
.ovk-dot { display: inline-grid; place-items: center; flex: none; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box; border-radius: 999px; background: var(--ovk-fill); color: var(--ovk-on-fill); font-size: .72rem; font-weight: 800; }
@@ -181,14 +186,14 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
@@ -181,14 +186,14 @@ html[data-ov-motion=reduced] { --ov-dur-1: 0s; --ov-dur-2: 0s; --ov-dur-3: 0s; -
.ovk-fold-body { padding: 12px 14px 14px; }
.ovk-fold-body { padding: 12px 14px 14px; }
.ovk-fold-body > :first-child { margin-top: 0; }
.ovk-fold-body > :first-child { margin-top: 0; }
.ovk-fold-body > :last-child { margin-bottom: 0; }
.ovk-fold-body > :last-child { margin-bottom: 0; }
-.ovk-fold--danger > summary { color: var(--ovk-bad); }
+.ovk-fold--danger > summary { color: var(--ovk-bad-text); }
.ovk-more { position: relative; display: inline-block; }
.ovk-more { position: relative; display: inline-block; }
.ovk-more > summary { list-style: none; }
.ovk-more > summary { list-style: none; }
.ovk-pop { position: absolute; right: 0; top: calc(100% + 4px); z-index: 60; min-width: 210px; padding: 6px; box-sizing: border-box; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); box-shadow: var(--ov-elev-3); }
.ovk-pop { position: absolute; right: 0; top: calc(100% + 4px); z-index: 60; min-width: 210px; padding: 6px; box-sizing: border-box; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); box-shadow: var(--ov-elev-3); }
.ovk-pop form { margin: 0; }
.ovk-pop form { margin: 0; }
.ovk-pop-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; box-sizing: border-box; padding: 8px 10px; border: 0; border-radius: max(0px, calc(var(--ov-radius-1) - 2px)); background: transparent; color: var(--ovk-text); font: inherit; font-weight: 600; text-align: left; text-decoration: none; cursor: pointer; }
.ovk-pop-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; box-sizing: border-box; padding: 8px 10px; border: 0; border-radius: max(0px, calc(var(--ov-radius-1) - 2px)); background: transparent; color: var(--ovk-text); font: inherit; font-weight: 600; text-align: left; text-decoration: none; cursor: pointer; }
.ovk-pop-item:hover, .ovk-pop-item:focus-visible { background: var(--ovk-panel-2); outline: none; }
.ovk-pop-item:hover, .ovk-pop-item:focus-visible { background: var(--ovk-panel-2); outline: none; }
-.ovk-pop-item--danger { color: var(--ovk-bad); }
+.ovk-pop-item--danger { color: var(--ovk-bad-text); }
/* ── Settings rows: what it does on the left, its control on the right ── */
/* ── Settings rows: what it does on the left, its control on the right ── */
.ovk-set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px; box-sizing: border-box; padding: 8px 14px; border-top: 1px solid var(--ovk-line); color: var(--ovk-text); cursor: pointer; }
.ovk-set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px; box-sizing: border-box; padding: 8px 14px; border-top: 1px solid var(--ovk-line); color: var(--ovk-text); cursor: pointer; }
@@ -272,10 +277,10 @@ div.ovk-set-row { cursor: default; }
@@ -272,10 +277,10 @@ div.ovk-set-row { cursor: default; }
.ovk-notice { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 12px; padding: 10px 14px; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); color: var(--ovk-text); font-size: .92rem; }
.ovk-notice { display: flex; align-items: flex-start; gap: 10px; margin: 0 0 12px; padding: 10px 14px; border-radius: calc(var(--ov-radius-1) + 2px); border: 1px solid var(--ovk-line); background: var(--ovk-panel); color: var(--ovk-text); font-size: .92rem; }
.ovk-notice > .ovk-i { margin-top: 2px; }
.ovk-notice > .ovk-i { margin-top: 2px; }
.ovk-notice--ok { border-color: color-mix(in srgb, var(--ovk-ok) 45%, transparent); }
.ovk-notice--ok { border-color: color-mix(in srgb, var(--ovk-ok) 45%, transparent); }
-.ovk-notice--ok > .ovk-i { color: var(--ovk-ok); }
+.ovk-notice--ok > .ovk-i { color: var(--ovk-ok-text); }
.ovk-notice--error { border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }
.ovk-notice--error { border-color: color-mix(in srgb, var(--ovk-bad) 50%, transparent); }
-.ovk-notice--error > .ovk-i { color: var(--ovk-bad); }
-.ovk-notice--warn > .ovk-i { color: var(--ovk-warn); }
+.ovk-notice--error > .ovk-i { color: var(--ovk-bad-text); }
+.ovk-notice--warn > .ovk-i { color: var(--ovk-warn-text); }
.ovk-notice--info > .ovk-i { color: var(--ovk-accent); }
.ovk-notice--info > .ovk-i { color: var(--ovk-accent); }
.ovk-snip { position: relative; }
.ovk-snip { position: relative; }
.ovk-snip > .ovk-icon-btn { position: absolute; top: 6px; right: 6px; z-index: 1; width: 34px; height: 34px; background: var(--ovk-panel); border-color: var(--ovk-line); }
.ovk-snip > .ovk-icon-btn { position: absolute; top: 6px; right: 6px; z-index: 1; width: 34px; height: 34px; background: var(--ovk-panel); border-color: var(--ovk-line); }
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "4.82.0",
+ "version": "4.83.0",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -21,6 +21,11 @@
@@ -21,6 +21,11 @@
--ovk-ok: var(--success, #22c55e);
--ovk-ok: var(--success, #22c55e);
--ovk-warn: var(--warning, #f59e0b);
--ovk-warn: var(--warning, #f59e0b);
--ovk-bad: var(--danger, var(--error, #ef4444));
--ovk-bad: var(--danger, var(--error, #ef4444));
+ /* Status in words (4.83.0): a theme's -text status colours are its readable ones; the fills above are for shapes. On
+ * a light theme the fill green or amber on white was about 2:1 (openvibe.design/kit's "paid" and "expiring"). */
+ --ovk-ok-text: var(--success-text, var(--ovk-ok));
+ --ovk-warn-text: var(--warning-text, var(--ovk-warn));
+ --ovk-bad-text: var(--danger-text, var(--ovk-bad));
--ovk-live: var(--live-red, #ef4444);
--ovk-live: var(--live-red, #ef4444);
/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme
/* Shape and elevation (4.81.0, plan L45 step 2): the kit's corners and shadows as tokens a look can set, so a theme
* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */
* has a shape as well as colours. The defaults are the kit's own corners and shadows; the personalities follow. */
@@ -33,4 +33,9 @@ assert.match(kit, /@media \(prefers-reduced-motion: reduce\) \{ :root:root \{ --
@@ -33,4 +33,9 @@ assert.match(kit, /@media \(prefers-reduced-motion: reduce\) \{ :root:root \{ --
assert.match(kit, /:is\(\[data-theme=paper\][^{]*\) \{\s*--ov-dur-1: \.15s;/, 'Paper is calm');
assert.match(kit, /:is\(\[data-theme=paper\][^{]*\) \{\s*--ov-dur-1: \.15s;/, 'Paper is calm');
const fx = fs.readFileSync(path.join(__dirname, '..', 'ov-fx.js'), 'utf8');
const fx = fs.readFileSync(path.join(__dirname, '..', 'ov-fx.js'), 'utf8');
assert.ok(fx.includes('--ov-dur-1:.12s;--ov-dur-2:.2s;--ov-dur-3:.32s;--ov-dur-4:.52s'), 'the same values as ov-fx.js');
assert.ok(fx.includes('--ov-dur-1:.12s;--ov-dur-2:.2s;--ov-dur-3:.32s;--ov-dur-4:.52s'), 'the same values as ov-fx.js');
-console.log('kit-shape: tokens, the kit on them, the personalities and the motion');
+// Status in words (4.83.0): the kit writes a status in the theme's readable -text colour, never its fill colour.
+assert.ok(kit.includes('--ovk-ok-text: var(--success-text, var(--ovk-ok));') && kit.includes('--ovk-bad-text: var(--danger-text, var(--ovk-bad));'), 'the -text status tokens');
+assert.doesNotMatch(kit, /[^-\w]color: var\(--ovk-(ok|warn|bad)\)/, 'no word in a fill colour');
+const data = fs.readFileSync(path.join(__dirname, '..', 'kit-data.css'), 'utf8');
+assert.doesNotMatch(data, /[^-\w]color: var\(--ovk-(ok|warn|bad)\)/, 'nor in kit-data.css');
+console.log('kit-shape: tokens, the kit on them, the personalities, the motion and status words');