Keep page-level text readable on gradients/patterns; test fixes
This commit is contained in:
parent
b396a01bc4
commit
f91dd9df43
@ -6,19 +6,19 @@
|
|||||||
:root{--bg:#0f1115;--page-bg:#0f1115;--panel:#171a21;--card:#1d212a;--ink:#e9ecf1;--mute:#9aa3b2;--acc:#7c9cff;--acc-ink:#0b0e16;--line:#2a2f3a;--ok:#38c172;--warn:#f6ad55;
|
:root{--bg:#0f1115;--page-bg:#0f1115;--panel:#171a21;--card:#1d212a;--ink:#e9ecf1;--mute:#9aa3b2;--acc:#7c9cff;--acc-ink:#0b0e16;--line:#2a2f3a;--ok:#38c172;--warn:#f6ad55;
|
||||||
--font:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;--hfont:var(--font);--scale:1;--radius:14px;--space:1;--cols:auto-fill;
|
--font:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;--hfont:var(--font);--scale:1;--radius:14px;--space:1;--cols:auto-fill;
|
||||||
--grid-cols:repeat(auto-fill,minmax(220px,1fr));--chat-bg:var(--panel);--chat-ink:var(--ink);--chat-acc:var(--acc);--chat-acc-ink:var(--acc-ink);--chat-w:370px;--chat-h:520px;
|
--grid-cols:repeat(auto-fill,minmax(220px,1fr));--chat-bg:var(--panel);--chat-ink:var(--ink);--chat-acc:var(--acc);--chat-acc-ink:var(--acc-ink);--chat-w:370px;--chat-h:520px;
|
||||||
--p-bg:#171a21;--p-ink:#ffffff}
|
--p-bg:#171a21;--p-ink:#ffffff;--page-ink:var(--ink);--page-mute:var(--mute)}
|
||||||
*{box-sizing:border-box}
|
*{box-sizing:border-box}
|
||||||
body{margin:0;font-family:var(--font);font-size:calc(15px * var(--scale));line-height:1.5;background:var(--page-bg);background-attachment:fixed;color:var(--ink);min-height:100vh}
|
body{margin:0;font-family:var(--font);font-size:calc(15px * var(--scale));line-height:1.5;background:var(--page-bg);background-attachment:fixed;color:var(--ink);min-height:100vh}
|
||||||
h1,h2,h3{font-family:var(--hfont)}
|
h1,h2,h3{font-family:var(--hfont)}
|
||||||
a{color:var(--acc)}button{font:inherit;cursor:pointer}
|
a{color:var(--acc)}button{font:inherit;cursor:pointer}
|
||||||
.top{display:flex;justify-content:space-between;align-items:center;padding:calc(14px * var(--space)) 28px;border-bottom:1px solid var(--line)}
|
.top{display:flex;justify-content:space-between;align-items:center;padding:calc(14px * var(--space)) 28px;border-bottom:1px solid var(--line)}
|
||||||
.brand{font-weight:700;font-size:1.2em}.logo{color:var(--acc)}
|
.top,.top .link{color:var(--page-ink)}.brand{font-weight:700;font-size:1.2em}.logo{color:var(--acc)}
|
||||||
nav{display:flex;gap:14px;align-items:center}.link{background:none;border:0;color:var(--ink);text-decoration:none;padding:4px}
|
nav{display:flex;gap:14px;align-items:center}.link{background:none;border:0;color:var(--ink);text-decoration:none;padding:4px}
|
||||||
main{max-width:1100px;margin:0 auto;padding:calc(28px * var(--space)) 28px;display:flex;flex-direction:column;gap:calc(14px * var(--space))}
|
main{max-width:1100px;margin:0 auto;padding:calc(28px * var(--space)) 28px;display:flex;flex-direction:column;gap:calc(14px * var(--space))}
|
||||||
.hero-sec h1{font-size:2.65em;line-height:1.1;margin:calc(20px * var(--space)) 0 10px}.hero-sec p{color:var(--mute);max-width:660px;margin:0}
|
.hero-sec h1{font-size:2.65em;line-height:1.1;margin:calc(20px * var(--space)) 0 10px;color:var(--page-ink)}.hero-sec p{color:var(--page-mute);max-width:660px;margin:0}
|
||||||
.chips{display:flex;flex-wrap:wrap;gap:10px}.chip{background:var(--card);border:1px solid var(--line);color:var(--ink);padding:8px 14px;border-radius:999px}
|
.chips{display:flex;flex-wrap:wrap;gap:10px}.chip{background:var(--card);border:1px solid var(--line);color:var(--ink);padding:8px 14px;border-radius:999px}
|
||||||
.chip:hover{border-color:var(--acc)}.facts{color:var(--mute);padding-left:18px;margin:0}
|
.chip:hover{border-color:var(--acc)}.facts{color:var(--page-mute);padding-left:18px;margin:0}
|
||||||
.foot{color:var(--mute);font-size:.85em;border-top:1px solid var(--line);padding-top:calc(14px * var(--space));margin-top:calc(20px * var(--space))}
|
.foot{color:var(--page-mute);font-size:.85em;border-top:1px solid var(--line);padding-top:calc(14px * var(--space));margin-top:calc(20px * var(--space))}
|
||||||
.is-hidden{display:none !important}
|
.is-hidden{display:none !important}
|
||||||
#stage{display:flex;flex-direction:column;gap:calc(18px * var(--space))}
|
#stage{display:flex;flex-direction:column;gap:calc(18px * var(--space))}
|
||||||
#stage:empty{display:none}
|
#stage:empty{display:none}
|
||||||
|
|||||||
@ -99,6 +99,13 @@
|
|||||||
set("--space", String({ compact: 0.65, normal: 1, comfy: 1.4 }[t.density] || 1));
|
set("--space", String({ compact: 0.65, normal: 1, comfy: 1.4 }[t.density] || 1));
|
||||||
const cols = Math.min(6, Math.max(0, parseInt(t.columns) || 0));
|
const cols = Math.min(6, Math.max(0, parseInt(t.columns) || 0));
|
||||||
set("--grid-cols", cols ? `repeat(${cols}, minmax(0, 1fr))` : "repeat(auto-fill, minmax(220px, 1fr))");
|
set("--grid-cols", cols ? `repeat(${cols}, minmax(0, 1fr))` : "repeat(auto-fill, minmax(220px, 1fr))");
|
||||||
|
// text that sits directly on the page background (header, hero, bullets, footer) must stay readable on
|
||||||
|
// gradients/patterns/images too: keep the theme colour only if it clears 4.5:1 against every background stop
|
||||||
|
const stops = (isObj(t.background) && Array.isArray(t.background.colors) ? t.background.colors.filter(okColor) : []);
|
||||||
|
const bgStops = stops.length ? stops.map(solid) : [solid(pal.bg)];
|
||||||
|
const minC = (ink) => Math.min(...bgStops.map((b) => contrast(ink, b)));
|
||||||
|
const pageInk = minC(pal.text) >= 4.5 ? pal.text : (minC("#111111") >= minC("#ffffff") ? "#111111" : "#ffffff");
|
||||||
|
set("--page-ink", pageInk); set("--page-mute", minC(pal.muted) >= 4.5 ? pal.muted : pageInk);
|
||||||
// protected panels: solid surface colour + whichever of black/white reads best (always >= 4.5:1)
|
// protected panels: solid surface colour + whichever of black/white reads best (always >= 4.5:1)
|
||||||
const pbg = solid(pal.surface); set("--p-bg", pbg); set("--p-ink", bestInk(pbg));
|
const pbg = solid(pal.surface); set("--p-bg", pbg); set("--p-ink", bestInk(pbg));
|
||||||
// chat popup
|
// chat popup
|
||||||
|
|||||||
@ -70,6 +70,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
await page.click("nav [data-event=sell]");
|
await page.click("nav [data-event=sell]");
|
||||||
await page.waitForSelector("[data-component=listing-form]", { timeout: 15000 });
|
await page.waitForSelector("[data-component=listing-form]", { timeout: 15000 });
|
||||||
await page.type("[name=title]", "Test-only bookshelf");
|
await page.type("[name=title]", "Test-only bookshelf");
|
||||||
|
await page.$eval("[name=price]", (n) => (n.value = ""));
|
||||||
await page.type("[name=price]", "30");
|
await page.type("[name=price]", "30");
|
||||||
await page.type("[name=description]", "Fake listing created by the browser test");
|
await page.type("[name=description]", "Fake listing created by the browser test");
|
||||||
await page.click("[data-component=listing-form] button[type=submit]");
|
await page.click("[data-component=listing-form] button[type=submit]");
|
||||||
|
|||||||
@ -65,8 +65,13 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
await ask("make the background a sunset gradient");
|
await ask("make the background a sunset gradient");
|
||||||
const bg1 = await cs("body", "backgroundImage");
|
const bg1 = await cs("body", "backgroundImage");
|
||||||
check("sunset gradient background", /gradient/.test(bg1), bg1.slice(0, 90));
|
check("sunset gradient background", /gradient/.test(bg1), bg1.slice(0, 90));
|
||||||
if (SHOTS) await page.screenshot({ path: SHOTS + "/6-theme-sunset-gradient.png" });
|
if (SHOTS) { await page.evaluate(() => window.scrollTo(0, 0)); await sleep(3800); await page.screenshot({ path: SHOTS + "/6-theme-sunset-gradient.png" }); }
|
||||||
await guardrails("sunset gradient");
|
await guardrails("sunset gradient");
|
||||||
|
const heroC = await page.evaluate(() => {
|
||||||
|
const t = JSON.parse(localStorage.getItem("am_theme_v1") || "{}"); const stops = ((t.background || {}).colors || []);
|
||||||
|
return ["headline", "subhead"].map((id) => Math.min(...stops.map((c) => window.__amContrast(getComputedStyle(document.getElementById(id)).color, c))));
|
||||||
|
});
|
||||||
|
check("hero text readable on the gradient (>= 4.5:1 vs every stop)", heroC.every((c) => c >= 4.5), heroC.map((c) => c.toFixed(1) + ":1").join(", "));
|
||||||
await ask("show me everything");
|
await ask("show me everything");
|
||||||
|
|
||||||
await ask("switch to light mode with a green accent");
|
await ask("switch to light mode with a green accent");
|
||||||
@ -74,7 +79,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
const acc = await page.evaluate(() => { const s = document.createElement("i"); s.style.color = getComputedStyle(document.documentElement).getPropertyValue("--acc"); document.body.append(s); const c = getComputedStyle(s).color; s.remove(); return c; });
|
const acc = await page.evaluate(() => { const s = document.createElement("i"); s.style.color = getComputedStyle(document.documentElement).getPropertyValue("--acc"); document.body.append(s); const c = getComputedStyle(s).color; s.remove(); return c; });
|
||||||
const [r, g, b] = acc.match(/\d+/g).map(Number);
|
const [r, g, b] = acc.match(/\d+/g).map(Number);
|
||||||
check("light mode with green accent", t2.mode === "light" && g > r && g > b, `mode=${t2.mode} accent=${acc}`);
|
check("light mode with green accent", t2.mode === "light" && g > r && g > b, `mode=${t2.mode} accent=${acc}`);
|
||||||
if (SHOTS) await page.screenshot({ path: SHOTS + "/7-theme-light-green.png" });
|
if (SHOTS) { await page.evaluate(() => window.scrollTo(0, 0)); await sleep(3800); await page.screenshot({ path: SHOTS + "/7-theme-light-green.png" }); }
|
||||||
await guardrails("light + green");
|
await guardrails("light + green");
|
||||||
|
|
||||||
await ask("use a serif font and make the text bigger");
|
await ask("use a serif font and make the text bigger");
|
||||||
@ -93,7 +98,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|||||||
await ask("make it look retro");
|
await ask("make it look retro");
|
||||||
const ff2 = await cs("body", "fontFamily"); const t4 = await theme();
|
const ff2 = await cs("body", "fontFamily"); const t4 = await theme();
|
||||||
check("retro preset applied", /VT323|Press Start|Space Mono|mono|Courier/i.test(ff2) || /retro/i.test(JSON.stringify(t4)), ff2.slice(0, 50));
|
check("retro preset applied", /VT323|Press Start|Space Mono|mono|Courier/i.test(ff2) || /retro/i.test(JSON.stringify(t4)), ff2.slice(0, 50));
|
||||||
if (SHOTS) { await ask("show me everything"); await page.evaluate(() => window.scrollTo(0, 0)); await page.screenshot({ path: SHOTS + "/8-theme-retro.png" }); }
|
if (SHOTS) { await ask("show me everything"); await page.evaluate(() => window.scrollTo(0, 0)); await sleep(3800); await page.screenshot({ path: SHOTS + "/8-theme-retro.png" }); }
|
||||||
await guardrails("retro");
|
await guardrails("retro");
|
||||||
|
|
||||||
await ask("hide the bullet points");
|
await ask("hide the bullet points");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user