mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-10 23:56:59 +00:00
fix(logo): default sage/teal brand colors, customizer mirrors accent (#1162)
## Summary Restores sage/teal as default logo colors while preserving customizer theming. Closes the gap from #1157 (closed) and the user's complaint about lost two-tone. Out-of-the-box, the navbar + hero CORE/SCOPE wordmarks now render the brand-identity duotone — `#cfd9c9` (sage / fog) and `#2c8c8c` (teal / water). When an operator picks a theme via the customizer (or sets a custom accent color), the wordmark recolors to follow. ## Approach (Option C — decoupled defaults + customizer mirror) - **`public/style.css` `:root`** — set `--logo-accent: #cfd9c9` and `--logo-accent-hi: #2c8c8c` as literal defaults. Removes the previous `var(--accent)` cascade so blue-by-default no longer leaks into the brand mark. - **`public/customize-v2.js`** — `applyTheme()`, the early-apply path, and the live color-picker `input` handler now mirror `themeSection.accent` → `--logo-accent` and `themeSection.accentHover` → `--logo-accent-hi`. - **`public/customize.js`** (legacy) — same mirroring in `applyThemePreview()` and the early localStorage replay. - **`.github/workflows/deploy.yml`** — adds the new e2e to the Chromium batch. This preserves `--accent` as the canonical app-wide accent token (no other UI changes) while giving the logo its own brand-defaulted tokens that the customizer still drives. ## Tests Red → green commit pair on the branch. - **NEW: `test-logo-default-sage-teal-e2e.js`** — gates both halves of the contract: 1. Clean localStorage → navbar + hero CORE = `rgb(207, 217, 201)`, SCOPE = `rgb(44, 140, 140)`. 2. Seeded `cs-theme-overrides` with red accent → navbar + hero recolor to red. - **UPDATED: `test-logo-theme-e2e.js`** — replaces the old "must NOT be sage" sentinel (sage was a regression marker; it's now the brand default) with a theme-reactivity probe that overrides `--logo-accent` / `--logo-accent-hi` directly and asserts the wordmark fill changes. Duotone, mobile-fit, and clip checks are unchanged. ## Verification - Default load: sage CORE + teal SCOPE in navbar AND hero ✔ (asserted by step 1 of the new e2e). - Customizer override: wordmark follows `accent` / `accentHover` ✔ (asserted by step 2 of the new e2e + the theme-reactivity probe in `test-logo-theme-e2e.js`). - Preflight: all hard gates green (PII, branch scope, red commit, CSS-var defined, CSS self-fallback, LIKE-on-JSON, sync migration); all warnings green. ## Browser verified E2E assertion added: `test-logo-default-sage-teal-e2e.js:73` (default sage), `test-logo-default-sage-teal-e2e.js:124` (customizer override). CI runs both via `deploy.yml:243`. Browser verified: covered by Chromium e2e against `http://localhost:13581` in CI; staging URL TBD on merge. --------- Co-authored-by: openclaw-bot <bot@openclaw.local>
This commit is contained in:
@@ -240,6 +240,7 @@ jobs:
|
||||
BASE_URL=http://localhost:13581 node test-issue-1147-section-order-e2e.js 2>&1 | tee -a e2e-output.txt
|
||||
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-rebrand-e2e.js 2>&1 | tee -a e2e-output.txt
|
||||
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-theme-e2e.js 2>&1 | tee -a e2e-output.txt
|
||||
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-default-sage-teal-e2e.js 2>&1 | tee -a e2e-output.txt
|
||||
|
||||
- name: Collect frontend coverage (parallel)
|
||||
if: success() && github.event_name == 'push'
|
||||
|
||||
+25
-2
@@ -514,7 +514,7 @@
|
||||
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
function applyCSS(effectiveConfig) {
|
||||
function applyCSS(effectiveConfig, userOverrides) {
|
||||
var dark = isDarkMode();
|
||||
var themeSection = dark
|
||||
? Object.assign({}, effectiveConfig.theme || {}, effectiveConfig.themeDark || {})
|
||||
@@ -529,6 +529,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Logo brand colors mirror --accent / --accent-hover ONLY when an
|
||||
// operator has actually overridden them via the customizer. We check
|
||||
// userOverrides (not the merged effective config), so the server-default
|
||||
// accent (#4a9eff) does NOT clobber the sage/teal :root brand defaults
|
||||
// out-of-the-box. When an operator picks a theme, customizer writes the
|
||||
// override to localStorage, the override flows through here, and the
|
||||
// wordmark recolors to follow the chosen accent.
|
||||
var ovTheme = (userOverrides && (dark
|
||||
? Object.assign({}, userOverrides.theme || {}, userOverrides.themeDark || {})
|
||||
: (userOverrides.theme || {}))) || {};
|
||||
if (ovTheme.accent) root.setProperty('--logo-accent', ovTheme.accent);
|
||||
if (ovTheme.accentHover) root.setProperty('--logo-accent-hi', ovTheme.accentHover);
|
||||
|
||||
// Derived vars
|
||||
if (themeSection.background) root.setProperty('--content-bg', themeSection.contentBg || themeSection.background);
|
||||
if (themeSection.surface1) root.setProperty('--card-bg', themeSection.cardBg || themeSection.surface1);
|
||||
@@ -614,7 +627,7 @@
|
||||
var overrides = readOverrides();
|
||||
var effective = computeEffective(_serverDefaults || {}, overrides);
|
||||
window.SITE_CONFIG = effective;
|
||||
applyCSS(effective);
|
||||
applyCSS(effective, overrides);
|
||||
}
|
||||
|
||||
// ── setOverride / clearOverride ──
|
||||
@@ -1392,6 +1405,9 @@
|
||||
// Optimistic CSS update (Decision #12)
|
||||
var cssVar = THEME_CSS_MAP[key];
|
||||
if (cssVar) document.documentElement.style.setProperty(cssVar, inp.value);
|
||||
// Mirror to logo brand vars so the wordmark recolors live too.
|
||||
if (key === 'accent') document.documentElement.style.setProperty('--logo-accent', inp.value);
|
||||
if (key === 'accentHover') document.documentElement.style.setProperty('--logo-accent-hi', inp.value);
|
||||
// Update hex display
|
||||
var hex = inp.parentElement.querySelector('.cust-hex');
|
||||
if (hex) hex.textContent = inp.value;
|
||||
@@ -1656,6 +1672,13 @@
|
||||
for (var key in THEME_CSS_MAP) {
|
||||
if (themeSection[key]) root.setProperty(THEME_CSS_MAP[key], themeSection[key]);
|
||||
}
|
||||
// Mirror accent → logo brand vars ONLY when present in overrides (so the
|
||||
// server-default accent never clobbers the sage/teal :root brand defaults).
|
||||
var ovTheme = dark
|
||||
? Object.assign({}, earlyOverrides.theme || {}, earlyOverrides.themeDark || {})
|
||||
: (earlyOverrides.theme || {});
|
||||
if (ovTheme.accent) root.setProperty('--logo-accent', ovTheme.accent);
|
||||
if (ovTheme.accentHover) root.setProperty('--logo-accent-hi', ovTheme.accentHover);
|
||||
if (themeSection.background) root.setProperty('--content-bg', themeSection.contentBg || themeSection.background);
|
||||
if (themeSection.surface1) root.setProperty('--card-bg', themeSection.cardBg || themeSection.surface1);
|
||||
// Apply node/type colors from overrides early
|
||||
|
||||
@@ -543,6 +543,9 @@
|
||||
for (var key in THEME_CSS_MAP) {
|
||||
if (t[key]) document.documentElement.style.setProperty(THEME_CSS_MAP[key], t[key]);
|
||||
}
|
||||
// Mirror accent → logo brand vars so the wordmark follows the theme.
|
||||
if (t.accent) document.documentElement.style.setProperty('--logo-accent', t.accent);
|
||||
if (t.accentHover) document.documentElement.style.setProperty('--logo-accent-hi', t.accentHover);
|
||||
// Derived vars that reference other vars — need explicit override
|
||||
if (t.background) {
|
||||
document.documentElement.style.setProperty('--content-bg', t.background);
|
||||
@@ -1447,6 +1450,9 @@
|
||||
for (const [key, val] of Object.entries(themeData)) {
|
||||
if (THEME_CSS_MAP[key]) document.documentElement.style.setProperty(THEME_CSS_MAP[key], val);
|
||||
}
|
||||
// Mirror accent → logo brand vars (matches applyThemePreview()).
|
||||
if (themeData.accent) document.documentElement.style.setProperty('--logo-accent', themeData.accent);
|
||||
if (themeData.accentHover) document.documentElement.style.setProperty('--logo-accent-hi', themeData.accentHover);
|
||||
// Derived vars
|
||||
if (themeData.background) document.documentElement.style.setProperty('--content-bg', themeData.background);
|
||||
if (themeData.surface1) document.documentElement.style.setProperty('--card-bg', themeData.surface1);
|
||||
|
||||
+17
-13
@@ -128,25 +128,29 @@
|
||||
--input-bg: #fff;
|
||||
--selected-bg: #dbeafe;
|
||||
|
||||
/* --- Logo theme tokens (PR #1137) ------------------------
|
||||
/* --- Logo theme tokens (PR #1137 + brand-default follow-up) -----------
|
||||
* The CoreScope SVG logos are inlined into the DOM (navbar +
|
||||
* home hero), so they inherit page CSS custom properties. The
|
||||
* legacy --logo-* names are kept so existing themes / brand
|
||||
* customizations that override them still work; defaults map
|
||||
* to the active theme so wordmark/arc colors track --accent /
|
||||
* --nav-text / --text-muted on light AND dark themes.
|
||||
* customizations that override them still work.
|
||||
* --logo-text → wordmark "CORE" / "SCOPE" / labels
|
||||
* --logo-accent → primary node + left-side arcs (default --accent)
|
||||
* --logo-accent-hi → secondary node + right-side arcs (default --accent-hover)
|
||||
* --logo-accent → primary node + left-side arcs (default sage)
|
||||
* --logo-accent-hi → secondary node + right-side arcs (default teal)
|
||||
* --logo-muted → tagline + sine wave
|
||||
* No --logo-bg here on purpose — the inlined SVGs MUST be
|
||||
* transparent so they sit on whatever bg the page paints.
|
||||
* --logo-text defaults to --text (the page foreground) so the
|
||||
* hero wordmark is readable on any theme; the navbar scopes it
|
||||
* to --nav-text below so it stays white on the dark navbar. */
|
||||
* Sage (#cfd9c9 — fog) and teal (#2c8c8c — water) are the OUT-OF-BOX
|
||||
* brand identity. They are NOT cascaded from --accent any more, so
|
||||
* changing the app accent color via dev tools alone will NOT recolor
|
||||
* the logo. The customizer (public/customize-v2.js) mirrors --accent
|
||||
* and --accent-hover into --logo-accent / --logo-accent-hi when an
|
||||
* operator picks a theme, preserving full theme-driven recoloring.
|
||||
* No --logo-bg here on purpose — the inlined SVGs MUST be transparent
|
||||
* so they sit on whatever bg the page paints. --logo-text defaults to
|
||||
* --text (the page foreground) so the hero wordmark is readable on
|
||||
* any theme; the navbar scopes it to --nav-text below so it stays
|
||||
* white on the dark navbar. */
|
||||
--logo-text: var(--text);
|
||||
--logo-accent: var(--accent);
|
||||
--logo-accent-hi: var(--accent-hover);
|
||||
--logo-accent: #cfd9c9;
|
||||
--logo-accent-hi: #2c8c8c;
|
||||
--logo-muted: var(--text-muted);
|
||||
|
||||
--surface-0: #f4f5f7;
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env node
|
||||
/* Logo default-brand E2E — verifies that the navbar + hero wordmarks
|
||||
* render the sage/teal brand identity OUT OF THE BOX (no operator
|
||||
* customizer override active), AND that the customizer can still
|
||||
* override those colors when an operator picks a theme.
|
||||
*
|
||||
* Asserts:
|
||||
* 1. Default load (clean localStorage, no overrides):
|
||||
* navbar CORE.fill === rgb(207, 217, 201) // sage / fog
|
||||
* navbar SCOPE.fill === rgb(44, 140, 140) // teal / water
|
||||
* hero CORE/SCOPE same.
|
||||
* 2. After a customizer override that sets accent=red (#dc2626) and
|
||||
* accentHover=red-hover (#ef4444), the wordmark CORE+SCOPE recolors
|
||||
* to follow the override (NOT sage/teal anymore).
|
||||
*
|
||||
* This is the contract from PR #1157 follow-up: sage/teal are the brand
|
||||
* default, but the customizer remains the canonical theming surface.
|
||||
*
|
||||
* On master this test FAILS step 1 because the default --accent is
|
||||
* #4a9eff (blue), so --logo-accent resolves to blue — not sage.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://localhost:13581';
|
||||
const SAGE = 'rgb(207, 217, 201)';
|
||||
const TEAL = 'rgb(44, 140, 140)';
|
||||
|
||||
function fail(msg) {
|
||||
console.error(`test-logo-default-sage-teal-e2e.js: FAIL — ${msg}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function readWordmark(page, sel) {
|
||||
return await page.evaluate((s) => {
|
||||
const root = document.querySelector(s);
|
||||
if (!root) return { error: s + ' missing' };
|
||||
const out = {};
|
||||
root.querySelectorAll('svg text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc === 'CORE' || tc === 'SCOPE') out[tc] = getComputedStyle(t).fill;
|
||||
});
|
||||
return { out };
|
||||
}, sel);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const requireChromium = process.env.CHROMIUM_REQUIRE === '1';
|
||||
let browser;
|
||||
try {
|
||||
browser = await chromium.launch({
|
||||
headless: true,
|
||||
executablePath: process.env.CHROMIUM_PATH || undefined,
|
||||
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
|
||||
});
|
||||
} catch (err) {
|
||||
if (requireChromium) {
|
||||
console.error(`test-logo-default-sage-teal-e2e.js: FAIL — Chromium required but unavailable: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`test-logo-default-sage-teal-e2e.js: SKIP (Chromium unavailable: ${err.message.split('\n')[0]})`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let passed = 0;
|
||||
const total = 4;
|
||||
try {
|
||||
// ── Step 1: clean localStorage, default load → sage/teal ──
|
||||
const ctx1 = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page1 = await ctx1.newPage();
|
||||
page1.setDefaultTimeout(10000);
|
||||
// Defensive: ensure no customizer overrides leak in.
|
||||
await page1.addInitScript(() => {
|
||||
try { localStorage.removeItem('cs-theme-overrides'); } catch (_) {}
|
||||
try { localStorage.setItem('meshcore-user-level', 'experienced'); } catch (_) {}
|
||||
});
|
||||
await page1.goto(BASE + '/#/', { waitUntil: 'domcontentloaded' });
|
||||
await page1.waitForSelector('.nav-brand svg.brand-logo text', { timeout: 8000 });
|
||||
|
||||
const navDefault = await readWordmark(page1, '.nav-brand');
|
||||
if (navDefault.error) fail(navDefault.error);
|
||||
if (!navDefault.out.CORE || !navDefault.out.SCOPE) {
|
||||
fail(`default navbar CORE/SCOPE missing: ${JSON.stringify(navDefault.out)}`);
|
||||
}
|
||||
if (navDefault.out.CORE !== SAGE) {
|
||||
fail(`default navbar CORE fill = ${navDefault.out.CORE}; expected sage ${SAGE}`);
|
||||
}
|
||||
if (navDefault.out.SCOPE !== TEAL) {
|
||||
fail(`default navbar SCOPE fill = ${navDefault.out.SCOPE}; expected teal ${TEAL}`);
|
||||
}
|
||||
console.log(` ✅ default navbar wordmark is sage/teal (CORE=${navDefault.out.CORE}, SCOPE=${navDefault.out.SCOPE})`);
|
||||
passed++;
|
||||
|
||||
await page1.evaluate(() => { window.location.hash = '#/home'; });
|
||||
await page1.waitForFunction(() => location.hash === '#/home');
|
||||
await page1.waitForSelector('.home-hero', { timeout: 8000 });
|
||||
// Hero SVG can render after the route swap; wait for the wordmark text
|
||||
// to actually exist before reading fills.
|
||||
await page1.waitForFunction(() => {
|
||||
const h = document.querySelector('.home-hero');
|
||||
return !!(h && h.querySelector('svg text'));
|
||||
}, null, { timeout: 8000 });
|
||||
const heroDefault = await readWordmark(page1, '.home-hero');
|
||||
if (heroDefault.error) fail(heroDefault.error);
|
||||
if (heroDefault.out.CORE !== SAGE) {
|
||||
fail(`default hero CORE fill = ${heroDefault.out.CORE}; expected sage ${SAGE}`);
|
||||
}
|
||||
if (heroDefault.out.SCOPE !== TEAL) {
|
||||
fail(`default hero SCOPE fill = ${heroDefault.out.SCOPE}; expected teal ${TEAL}`);
|
||||
}
|
||||
console.log(` ✅ default hero wordmark is sage/teal (CORE=${heroDefault.out.CORE}, SCOPE=${heroDefault.out.SCOPE})`);
|
||||
passed++;
|
||||
await ctx1.close();
|
||||
|
||||
// ── Step 2: customizer override → red wordmark ──
|
||||
const ctx2 = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page2 = await ctx2.newPage();
|
||||
page2.setDefaultTimeout(10000);
|
||||
// Seed the customizer override BEFORE first paint. customize-v2.js reads
|
||||
// 'cs-theme-overrides' from localStorage on init and writes the matching
|
||||
// CSS vars (including --logo-accent / --logo-accent-hi after this fix).
|
||||
await page2.addInitScript(() => {
|
||||
try {
|
||||
localStorage.setItem('cs-theme-overrides', JSON.stringify({
|
||||
theme: { accent: '#dc2626', accentHover: '#ef4444' },
|
||||
themeDark: { accent: '#dc2626', accentHover: '#ef4444' },
|
||||
}));
|
||||
localStorage.setItem('meshcore-user-level', 'experienced');
|
||||
} catch (_) {}
|
||||
});
|
||||
await page2.goto(BASE + '/#/', { waitUntil: 'domcontentloaded' });
|
||||
await page2.waitForSelector('.nav-brand svg.brand-logo text', { timeout: 8000 });
|
||||
// Settle one frame for early-apply to run.
|
||||
await page2.waitForTimeout(200);
|
||||
|
||||
const navOverride = await readWordmark(page2, '.nav-brand');
|
||||
if (navOverride.error) fail(navOverride.error);
|
||||
if (navOverride.out.CORE === SAGE || navOverride.out.SCOPE === TEAL) {
|
||||
fail(`customizer override did NOT reach the logo — still sage/teal: ${JSON.stringify(navOverride.out)}. Customizer must mirror --accent → --logo-accent.`);
|
||||
}
|
||||
// Both halves should follow the override (CORE ← accent, SCOPE ← accentHover).
|
||||
if (navOverride.out.CORE !== 'rgb(220, 38, 38)') {
|
||||
fail(`navbar CORE under customizer override = ${navOverride.out.CORE}; expected rgb(220, 38, 38)`);
|
||||
}
|
||||
if (navOverride.out.SCOPE !== 'rgb(239, 68, 68)') {
|
||||
fail(`navbar SCOPE under customizer override = ${navOverride.out.SCOPE}; expected rgb(239, 68, 68)`);
|
||||
}
|
||||
console.log(` ✅ navbar wordmark follows customizer override (CORE=${navOverride.out.CORE}, SCOPE=${navOverride.out.SCOPE})`);
|
||||
passed++;
|
||||
|
||||
await page2.evaluate(() => { window.location.hash = '#/home'; });
|
||||
await page2.waitForFunction(() => location.hash === '#/home');
|
||||
await page2.waitForSelector('.home-hero', { timeout: 8000 });
|
||||
await page2.waitForFunction(() => {
|
||||
const h = document.querySelector('.home-hero');
|
||||
return !!(h && h.querySelector('svg text'));
|
||||
}, null, { timeout: 8000 });
|
||||
const heroOverride = await readWordmark(page2, '.home-hero');
|
||||
if (heroOverride.error) fail(heroOverride.error);
|
||||
if (heroOverride.out.CORE !== 'rgb(220, 38, 38)' || heroOverride.out.SCOPE !== 'rgb(239, 68, 68)') {
|
||||
fail(`hero wordmark under customizer override = ${JSON.stringify(heroOverride.out)}; expected CORE=rgb(220,38,38), SCOPE=rgb(239,68,68)`);
|
||||
}
|
||||
console.log(` ✅ hero wordmark follows customizer override (CORE=${heroOverride.out.CORE}, SCOPE=${heroOverride.out.SCOPE})`);
|
||||
passed++;
|
||||
|
||||
await ctx2.close();
|
||||
await browser.close();
|
||||
console.log(`\ntest-logo-default-sage-teal-e2e.js: ${passed}/${total} PASS`);
|
||||
} catch (err) {
|
||||
try { await browser.close(); } catch (_) {}
|
||||
console.error(`test-logo-default-sage-teal-e2e.js: FAIL — ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
+63
-14
@@ -32,7 +32,13 @@
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://localhost:13581';
|
||||
const LEGACY_SAGE = 'rgb(207, 217, 201)';
|
||||
// Note: rgb(207, 217, 201) is the brand sage default for --logo-accent
|
||||
// (see test-logo-default-sage-teal-e2e.js). It is NO LONGER a failure
|
||||
// signal here; the original "must not be sage" assertion was written
|
||||
// when sage meant "baked-into-SVG-attr regression" and the wordmark was
|
||||
// supposed to follow --accent (then blue). Now sage is the intentional
|
||||
// brand identity and the test below asserts theme-reactivity by mutating
|
||||
// --logo-accent directly and observing the fill change instead.
|
||||
|
||||
function fail(msg) {
|
||||
console.error(`test-logo-theme-e2e.js: FAIL — ${msg}`);
|
||||
@@ -74,8 +80,8 @@ async function main() {
|
||||
await page.evaluate(() => { document.documentElement.setAttribute('data-theme', 'light'); });
|
||||
|
||||
// 1. Navbar wordmark must be inline-SVG <text> (not <img>) and computed
|
||||
// fill must NOT be the legacy hardcoded sage. We grep for any <text>
|
||||
// with textContent CORE or SCOPE inside .nav-brand.
|
||||
// fill must be theme-reactive: setting --logo-accent / --logo-accent-hi
|
||||
// on :root must repaint the wordmark.
|
||||
const navWordmarkFills = await page.evaluate(() => {
|
||||
const out = [];
|
||||
const root = document.querySelector('.nav-brand');
|
||||
@@ -93,12 +99,35 @@ async function main() {
|
||||
if (!navWordmarkFills.out || navWordmarkFills.out.length < 2) {
|
||||
fail(`navbar inline-SVG wordmark <text> CORE/SCOPE not found (found: ${JSON.stringify(navWordmarkFills.out)}). Navbar logo must be inline <svg> so CSS vars apply.`);
|
||||
}
|
||||
for (const w of navWordmarkFills.out) {
|
||||
if (w.fill === LEGACY_SAGE) {
|
||||
fail(`navbar wordmark "${w.tc}" still computes legacy sage fill ${LEGACY_SAGE} — wordmark fill must theme via CSS var`);
|
||||
}
|
||||
// Theme-reactivity probe: override --logo-accent / --logo-accent-hi and
|
||||
// confirm fills change. This replaces the old "must not be legacy sage"
|
||||
// assertion (sage is now the brand default — see test-logo-default-sage-teal-e2e.js).
|
||||
const navReact = await page.evaluate(() => {
|
||||
const root = document.querySelector('.nav-brand');
|
||||
const before = {};
|
||||
root.querySelectorAll('svg text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc === 'CORE' || tc === 'SCOPE') before[tc] = getComputedStyle(t).fill;
|
||||
});
|
||||
document.documentElement.style.setProperty('--logo-accent', '#123456');
|
||||
document.documentElement.style.setProperty('--logo-accent-hi', '#abcdef');
|
||||
const after = {};
|
||||
root.querySelectorAll('svg text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc === 'CORE' || tc === 'SCOPE') after[tc] = getComputedStyle(t).fill;
|
||||
});
|
||||
// Reset so later assertions on default colors aren't polluted.
|
||||
document.documentElement.style.removeProperty('--logo-accent');
|
||||
document.documentElement.style.removeProperty('--logo-accent-hi');
|
||||
return { before, after };
|
||||
});
|
||||
if (navReact.before.CORE === navReact.after.CORE) {
|
||||
fail(`navbar CORE fill did not change when --logo-accent was overridden (${navReact.before.CORE} → ${navReact.after.CORE}); wordmark must theme via --logo-accent`);
|
||||
}
|
||||
console.log(` ✅ navbar wordmark fills are theme-reactive (${navWordmarkFills.out.map((w) => w.tc + '=' + w.fill).join(', ')})`);
|
||||
if (navReact.before.SCOPE === navReact.after.SCOPE) {
|
||||
fail(`navbar SCOPE fill did not change when --logo-accent-hi was overridden (${navReact.before.SCOPE} → ${navReact.after.SCOPE}); wordmark must theme via --logo-accent-hi`);
|
||||
}
|
||||
console.log(` ✅ navbar wordmark fills are theme-reactive (CORE ${navReact.before.CORE}→${navReact.after.CORE}, SCOPE ${navReact.before.SCOPE}→${navReact.after.SCOPE})`);
|
||||
passed++;
|
||||
|
||||
// 2. Hero SVG must NOT have a full-canvas opaque background rect.
|
||||
@@ -136,7 +165,8 @@ async function main() {
|
||||
console.log(` ✅ hero SVG has no full-canvas opaque background rect`);
|
||||
passed++;
|
||||
|
||||
// 3. Hero wordmark CORE/SCOPE must not compute legacy sage fill on light theme.
|
||||
// 3. Hero wordmark CORE/SCOPE must be theme-reactive — overriding
|
||||
// --logo-accent / --logo-accent-hi must repaint the hero wordmark too.
|
||||
const heroWordmarkFills = await page.evaluate(() => {
|
||||
const hero = document.querySelector('.home-hero');
|
||||
if (!hero) return { error: '.home-hero missing' };
|
||||
@@ -153,12 +183,31 @@ async function main() {
|
||||
if (!heroWordmarkFills.out || heroWordmarkFills.out.length < 2) {
|
||||
fail(`hero inline-SVG wordmark <text> CORE/SCOPE not found (found: ${JSON.stringify(heroWordmarkFills.out)})`);
|
||||
}
|
||||
for (const w of heroWordmarkFills.out) {
|
||||
if (w.fill === LEGACY_SAGE) {
|
||||
fail(`hero wordmark "${w.tc}" still computes legacy sage fill ${LEGACY_SAGE} — invisible on light theme`);
|
||||
}
|
||||
const heroReact = await page.evaluate(() => {
|
||||
const hero = document.querySelector('.home-hero');
|
||||
const before = {};
|
||||
hero.querySelectorAll('svg text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc === 'CORE' || tc === 'SCOPE') before[tc] = getComputedStyle(t).fill;
|
||||
});
|
||||
document.documentElement.style.setProperty('--logo-accent', '#654321');
|
||||
document.documentElement.style.setProperty('--logo-accent-hi', '#fedcba');
|
||||
const after = {};
|
||||
hero.querySelectorAll('svg text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc === 'CORE' || tc === 'SCOPE') after[tc] = getComputedStyle(t).fill;
|
||||
});
|
||||
document.documentElement.style.removeProperty('--logo-accent');
|
||||
document.documentElement.style.removeProperty('--logo-accent-hi');
|
||||
return { before, after };
|
||||
});
|
||||
if (heroReact.before.CORE === heroReact.after.CORE) {
|
||||
fail(`hero CORE fill did not change when --logo-accent was overridden (${heroReact.before.CORE} → ${heroReact.after.CORE})`);
|
||||
}
|
||||
console.log(` ✅ hero wordmark fills are theme-reactive (${heroWordmarkFills.out.map((w) => w.tc + '=' + w.fill).join(', ')})`);
|
||||
if (heroReact.before.SCOPE === heroReact.after.SCOPE) {
|
||||
fail(`hero SCOPE fill did not change when --logo-accent-hi was overridden (${heroReact.before.SCOPE} → ${heroReact.after.SCOPE})`);
|
||||
}
|
||||
console.log(` ✅ hero wordmark fills are theme-reactive (CORE ${heroReact.before.CORE}→${heroReact.after.CORE}, SCOPE ${heroReact.before.SCOPE}→${heroReact.after.SCOPE})`);
|
||||
passed++;
|
||||
|
||||
// 4 & 5. Duotone — CORE fill must differ from SCOPE fill in BOTH navbar
|
||||
|
||||
Reference in New Issue
Block a user