openvibers/openvibe.shared Commits

Status words read on light themes (4.83.0) (#237)

OP OpenVibers committed 12 days ago · 06d46ef3a8 · 8 files, +45 −19

Badges, the danger button, done/danger icon buttons, the danger fold and menu item, notice icons and kit-data's
deltas and pills wrote a status in the fill colours; on Daylight "paid" and "expiring" were 2.28:1 and 2.15:1. New
--ovk-ok-text/-warn-text/-bad-text follow --success-text/--warning-text/--danger-text: 5.4:1 on Daylight, the dark
default unchanged. Fills and borders keep the fill colours.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 6d2929a..3b0cde1 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -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.
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).
ddiff --git a/README.md b/README.md
iindex 382c241..2416a77 100644
---- a/README.md
++++ b/README.md
@@@ -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 deployed
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
the tag it pins.
ddiff --git a/STATUS.json b/STATUS.json
iindex 3df93b3..f499aaf 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -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)",
ddiff --git a/kit-data.css b/kit-data.css
iindex 91ee2eb..8a3aafa 100644
---- a/kit-data.css
++++ b/kit-data.css
@@@ -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); }
ddiff --git a/kit.css b/kit.css
iindex c1cda01..fcd5f4c 100644
---- a/kit.css
++++ b/kit.css
@@@ -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); }
ddiff --git a/package.json b/package.json
iindex e42d828..c299c5e 100644
---- a/package.json
++++ b/package.json
@@@ -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": {
ddiff --git a/room.css b/room.css
iindex 802f5a4..4afd352 100644
---- a/room.css
++++ b/room.css
@@@ -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. */
ddiff --git a/test/kit-shape.test.js b/test/kit-shape.test.js
iindex 866bc91..446ead1 100644
---- a/test/kit-shape.test.js
++++ b/test/kit-shape.test.js
@@@ -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');