openvibers/openvibe.shared Commits

4.140.0: the Compose export maps Material 3's container roles to Vibe surfaces (#328)

OP OpenVibers committed 7 days ago · 20329472ae · 6 files, +32 −4

Cards, the navigation bar and its selected pill, dialogs and chips read surfaceContainer*, secondaryContainer and
friends, which the export left out; Compose filled them with Material's lavender (OpenVibe Link's first screens).
Compose only: Flutter's constructor takes them from 3.22.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 50113b7..e89fd63 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,5 +1,16 @@
# Changelog
++## 4.140.0 — 2026-11-07
++
++- **Native: no more Material lavender.**
++ - The Compose export (`tokens.compile(…, 'compose')`, `scripts/native-check.js`) now maps Material 3's container
++ roles to Vibe surfaces: `surfaceContainer*`, `surfaceBright`/`Dim`, `surfaceTint`, `secondaryContainer` (the
++ navigation bar's selected pill) and `inverseSurface`.
++ - Before, Compose filled cards, the bottom bar, dialogs and chips with its own lavender. OpenVibe Link for
++ Android's first screens showed it.
++ - Flutter's scheme is unchanged: its constructor takes these roles only from 3.22.
++ - No web change, so no pin round is needed for this release.
++
## 4.139.0 — 2026-11-07
- **The menu's feed rows lead with their picture.** A row with a thumbnail (Trending on Video, a live channel) no
ddiff --git a/README.md b/README.md
iindex dfe8db6..3e2f013 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.139.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
++**Status:** alpha, v4.140.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 49dcb28..df6a605 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -4,13 +4,14 @@
"code": true,
"wave": "W2",
"package": "openvibe-shared",
-- "version": "4.139.0",
++ "version": "4.140.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)",
"contracts": "openvibe-contracts v0.312.0 (devDependency: the release manifest contract)",
"sdk": "openvibe-sdk v0.94.0",
"features": [
++ "the Compose export maps Material 3's container roles (cards, the navigation bar and its selected pill, dialogs) to Vibe surfaces, so native apps never show Material's lavender (4.140.0)",
"the menu's feed rows with a picture show the picture, not a site icon beside it; a row without one in that group keeps its icon in a picture-sized tile (4.139.0)",
"the footer's shipped line and brand logos load when the footer nears the screen (38 KB off most first visits); the footer, Boost and the glyphs load their files by content hash (4.138.0)",
"every script the bar, the Dock and the notifications load goes by its content hash (?v=), so a release reaches every open page at once instead of up to a day later; one menu button on a phone (4.137.0)",
ddiff --git a/package.json b/package.json
iindex 2adb867..2b9c711 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.139.0",
++ "version": "4.140.0",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/test/tokens.test.js b/test/tokens.test.js
iindex ef1d839..b03d39a 100644
---- a/test/tokens.test.js
++++ b/test/tokens.test.js
@@@ -43,6 +43,10 @@ assert.match(kt, /val ColorBgBase = Color\(0xFF0A0F1C\)/);
assert.match(kt, /val VibeDarkColorScheme: ColorScheme = darkColorScheme\(/);
assert.match(kt, /val VibeLightColorScheme: ColorScheme = lightColorScheme\(/);
assert.match(kt, /primary = VibeDarkTokens\.ColorAccentBase,/);
++// 4.140.0: Material 3's container roles (cards, the bar, its selected pill) take Vibe surfaces, not Material's lavender.
++assert.match(kt, /surfaceContainer = VibeDarkTokens\.ColorBgRaised,/);
++assert.match(kt, /surfaceContainerHighest = VibeLightTokens\.ColorBgSubtle,/);
++assert.match(kt, /secondaryContainer = VibeDarkTokens\.ColorBgHover,/);
assert.match(kt, /object OvScale \{[\s\S]*val RadiusMd = 14\.dp/, 'the scales are theme-independent: OvScale');
assert.doesNotMatch(kt.slice(0, kt.indexOf('object OvScale')), /RadiusMd/, 'not on the theme objects');
assert.match(kt, /val TypeSizeBody = 15\.sp/);
@@@ -55,6 +59,7 @@ assert.match(swift, /public enum OvScale \{[\s\S]*public static let motionDurati
const dart = tokens.compile(vibe, 'flutter');
assert.match(dart, /static const colorBgBase = Color\(0xFF0A0F1C\);/);
assert.match(dart, /const vibeDarkScheme = ColorScheme\(\n {2}brightness: Brightness\.dark,/);
++assert.doesNotMatch(dart, /surfaceContainer/, 'Flutter keeps the roles every version takes');
const fig = JSON.parse(tokens.compile([vibe, vibeLight], 'figma'));
assert.deepStrictEqual(fig.modes, ['dark', 'light']);
const bg = fig.variables.find((v) => v.name === 'color/bg/base');
ddiff --git a/tokens.js b/tokens.js
iindex 1bcac49..d55bdcc 100644
---- a/tokens.js
++++ b/tokens.js
@@@ -282,6 +282,17 @@
['outline', 'color.line.strong'], ['outlineVariant', 'color.line.base'],
['error', 'color.status.bad.base'], ['onError', 'color.accent.onStrong'],
];
++ // Material 3's container roles (cards, the navigation bar and its selected pill, dialogs, chips, menus). Without them
++ // Compose fills those parts with Material's own lavender: OpenVibe Link's first screens showed it (11-07). Compose
++ // only; Flutter's ColorScheme takes them only from 3.22, and SwiftUI has no such roles.
++ const M3_COMPOSE_CONTAINERS = [
++ ['surfaceContainerLowest', 'color.bg.base'], ['surfaceContainerLow', 'color.bg.raised'],
++ ['surfaceContainer', 'color.bg.raised'], ['surfaceContainerHigh', 'color.bg.surface'],
++ ['surfaceContainerHighest', 'color.bg.subtle'], ['surfaceBright', 'color.bg.hover'], ['surfaceDim', 'color.bg.base'],
++ ['surfaceTint', 'color.bg.raised'],
++ ['secondaryContainer', 'color.bg.hover'], ['onSecondaryContainer', 'color.accent.light'],
++ ['inverseSurface', 'color.text.primary'], ['inverseOnSurface', 'color.bg.base'],
++ ];
function compose(input, { pkg = 'network.openvibe.theme' } = {}) {
const docs = docsOf(input);
@@@ -297,7 +308,7 @@
for (const t of colorTokens(doc)) parts.push(` val ${pascal(t.path)} = Color(${argb(t.c)})`);
parts.push('}', '');
const fn = m.mode === 'light' ? 'lightColorScheme' : 'darkColorScheme';
-- const roles = M3_ROLES.filter(([, path]) => pick(doc, path)).map(([role, path]) => ` ${role} = ${obj}.${pascal(path.split('.'))},`);
++ const roles = [...M3_ROLES, ...M3_COMPOSE_CONTAINERS].filter(([, path]) => pick(doc, path)).map(([role, path]) => ` ${role} = ${obj}.${pascal(path.split('.'))},`);
parts.push(`/** Material 3 roles: MaterialTheme(colorScheme = ${name}${m.mode === 'light' ? 'Light' : 'Dark'}ColorScheme) { … } */`, `val ${name}${m.mode === 'light' ? 'Light' : 'Dark'}ColorScheme: ColorScheme = ${fn}(`, ...roles, ')', '');
}
// The kit's scales: the same on every theme (openvibe-shared/components' reference views use OvScale).