openvibers/openvibe.shared Commits
4.140.0: the Compose export maps Material 3's container roles to Vibe surfaces (#328)
Changes
@@@ -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@@@ -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 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,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)",@@@ -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": {@@@ -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 motionDuraticonst 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');@@@ -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).
@@ -1,5 +1,16 @@
@@ -1,5 +1,16 @@
# Changelog
# 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
## 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
- **The menu's feed rows lead with their picture.** A row with a thumbnail (Trending on Video, a live channel) no
@@ -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.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
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,13 +4,14 @@
@@ -4,13 +4,14 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "4.139.0",
+ "version": "4.140.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)",
"contracts": "openvibe-contracts v0.312.0 (devDependency: the release manifest contract)",
"contracts": "openvibe-contracts v0.312.0 (devDependency: the release manifest contract)",
"sdk": "openvibe-sdk v0.94.0",
"sdk": "openvibe-sdk v0.94.0",
"features": [
"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 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)",
"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)",
"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)",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"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",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -43,6 +43,10 @@ assert.match(kt, /val ColorBgBase = Color\(0xFF0A0F1C\)/);
@@ -43,6 +43,10 @@ assert.match(kt, /val ColorBgBase = Color\(0xFF0A0F1C\)/);
assert.match(kt, /val VibeDarkColorScheme: ColorScheme = darkColorScheme\(/);
assert.match(kt, /val VibeDarkColorScheme: ColorScheme = darkColorScheme\(/);
assert.match(kt, /val VibeLightColorScheme: ColorScheme = lightColorScheme\(/);
assert.match(kt, /val VibeLightColorScheme: ColorScheme = lightColorScheme\(/);
assert.match(kt, /primary = VibeDarkTokens\.ColorAccentBase,/);
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.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.doesNotMatch(kt.slice(0, kt.indexOf('object OvScale')), /RadiusMd/, 'not on the theme objects');
assert.match(kt, /val TypeSizeBody = 15\.sp/);
assert.match(kt, /val TypeSizeBody = 15\.sp/);
@@ -55,6 +59,7 @@ assert.match(swift, /public enum OvScale \{[\s\S]*public static let motionDurati
@@ -55,6 +59,7 @@ assert.match(swift, /public enum OvScale \{[\s\S]*public static let motionDurati
const dart = tokens.compile(vibe, 'flutter');
const dart = tokens.compile(vibe, 'flutter');
assert.match(dart, /static const colorBgBase = Color\(0xFF0A0F1C\);/);
assert.match(dart, /static const colorBgBase = Color\(0xFF0A0F1C\);/);
assert.match(dart, /const vibeDarkScheme = ColorScheme\(\n {2}brightness: Brightness\.dark,/);
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'));
const fig = JSON.parse(tokens.compile([vibe, vibeLight], 'figma'));
assert.deepStrictEqual(fig.modes, ['dark', 'light']);
assert.deepStrictEqual(fig.modes, ['dark', 'light']);
const bg = fig.variables.find((v) => v.name === 'color/bg/base');
const bg = fig.variables.find((v) => v.name === 'color/bg/base');
@@ -282,6 +282,17 @@
@@ -282,6 +282,17 @@
['outline', 'color.line.strong'], ['outlineVariant', 'color.line.base'],
['outline', 'color.line.strong'], ['outlineVariant', 'color.line.base'],
['error', 'color.status.bad.base'], ['onError', 'color.accent.onStrong'],
['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' } = {}) {
function compose(input, { pkg = 'network.openvibe.theme' } = {}) {
const docs = docsOf(input);
const docs = docsOf(input);
@@ -297,7 +308,7 @@
@@ -297,7 +308,7 @@
for (const t of colorTokens(doc)) parts.push(` val ${pascal(t.path)} = Color(${argb(t.c)})`);
for (const t of colorTokens(doc)) parts.push(` val ${pascal(t.path)} = Color(${argb(t.c)})`);
parts.push('}', '');
parts.push('}', '');
const fn = m.mode === 'light' ? 'lightColorScheme' : 'darkColorScheme';
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, ')', '');
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).
// The kit's scales: the same on every theme (openvibe-shared/components' reference views use OvScale).