Fix: use system fonts and the light theme in the picker (#751)

Replace external fonts with system stacks and an outlined SVG logo. Adopt the light paper theme, refine Kinpaku accents, and preserve picker interactions with regression coverage.

AI assistance: Codex, under maintainer direction.
This commit is contained in:
Paul Bakaus
2026-09-06 12:44:15 -07:00
committed by GitHub
parent 831cabee8b
commit 720628f4ea
5 changed files with 165 additions and 1 deletions
@@ -0,0 +1,68 @@
---
version: 1
slug: "crates-context-src-question-page-rs"
primary_target: "crates/context/src/question_page.rs"
related_targets: ["crates/context/src/serve_question.rs"]
---
## Scope
Operate mode: the existing local question picker. Help builders compare the
current hand, choose, steer, or re-roll. Preserve payloads, copy, choices,
confirmation, loading, error handling, and responsive deck behavior.
## Direction contract
THESIS: Quiet picker chrome lets the offered directions carry the character.
OWN-WORLD: Inherit impeccable-site's paper-and-instruments proposal: neutral paper,
ink type, patina state text, small gold marks, tactile controls. Use system fonts
and the existing outlined SVG logo; no font files or external font requests.
STORY: Compare the options, inspect details, choose or request another hand.
FIRST VIEWPORT: Small brand at top; readable heading and build-path switch above
the existing card deck; steering and re-roll actions remain reachable below.
FORM: Existing picker structure, user-pinned site theme; no concept roll required.
FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance
## Authority
The user confirmed impeccable-site #34 (head eb5348e48fcd94d0d74895488b985988a401a2e2)
as the light-theme authority, with system fonts for the picker. This is a scoped adaptation,
not a rewrite of the repository's global DESIGN.md. No new raster assets.
## Built surface
Recorded from `crates/context/src/question_page.rs`, rendered
`.impeccable/review/after.html`, and the desktop/mobile captures alongside it.
Finish reviewer disposition: **ship**, with no requested fixes.
- **Paper and ink:** light-only chrome uses neutral paper (`oklch(97.8% 0 0)`),
raised paper (`oklch(99.5% 0 0)`), ink headings (`oklch(13% 0 0)`), and body
text (`oklch(22% 0 0)`). Dark instrument fills carry primary actions and image
controls; gold appears in the logo, lead-card border, and active switch dot.
- **Local palette adaptations:** patina is the site's text-safe deep value
(`oklch(49% 0.11 190)`); faint metadata shares muted ink (`oklch(46% 0 0)`).
Dividers and the build-path switch boundary use ink at 12% opacity; other
control boundaries use 45%. The lead-card outline (including hover) and
active switch dot use default Kinpaku (`oklch(84% 0.19 80.46)`), not deep gold.
- **Explicit font override:** headings, body, and controls use `system-ui,
-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`; metadata uses
`ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace`. Body is 15px/1.55;
headings are 2rem at weight 600, reduced to 1.5rem in portrait; card titles
are 1.125rem at weight 600. The existing wordmark is inline SVG path outlines
with a tight viewBox, displayed at 164 × 30px. No font files are required.
- **Comparison layout:** a 90rem maximum content width shares its inset with
the deck and footer. Landscape uses a horizontal snapping deck; portrait
uses a vertical deck capped at `min(68dvh, 44rem)` with Back/More controls.
The build-path switch moves below the heading in portrait. Footer controls
are sticky in landscape and remain in document flow in portrait.
- **Tactile controls:** cards and dialogs use 8px corners and layered soft
shadows. Actions and the steering field use 6px corners. The build-path
switch has a recessed gray track, a raised active cap, and a gold dot;
re-roll controls share the cap shadow and inset pressed state. Keyboard
focus uses a 2px patina outline. Image zoom, card flips, loading placeholders,
confirmation, and completion/error surfaces retain their existing roles.
These are picker-local facts, not a replacement global design system. Existing
card badges and dense uppercase metadata are preserved content treatments,
not new typography rules for other surfaces. The user-authorized system fonts
are an intentional local override. No raster assets were created or added.
@@ -106,6 +106,16 @@ fn detached_posts_require_key_and_loopback_host_origin() {
assert!(page.contains("/heartbeat' + keyQ"));
assert!(page.contains("/build-path' + keyQ"));
// The picker is self-contained: system fonts and an outlined SVG
// wordmark, with no bundled font routes or external stylesheet.
assert!(!page.contains("@font-face"));
assert!(page.contains("--ks-font: system-ui,"));
assert!(page.contains("aria-label=\"Impeccable\""));
for path in ["/fonts/albert-sans.woff2", "/fonts/alumni-sans.ttf", "/fonts/%2e%2e/q.json"] {
let (st, _) = raw_request(port, "GET", path, &[], None);
assert_eq!(st, 404, "no font asset routes");
}
// The build-path flip takes the same gate as /answer.
let flip = r#"{"value":"comp"}"#;
let (st, _) = raw_request(port, "POST", "/build-path", &[json], Some(flip));
File diff suppressed because one or more lines are too long
+12
View File
@@ -1264,6 +1264,18 @@ impl ServerState {
mod tests {
use super::*;
#[test]
fn question_page_uses_system_fonts_and_inline_branding() {
assert!(!PAGE.contains("fonts.googleapis.com"));
assert!(!PAGE.contains("fonts.gstatic.com"));
assert!(!PAGE.contains("@font-face"));
assert!(!PAGE.contains("/fonts/"));
assert!(PAGE.contains("--ks-font: system-ui,"));
assert!(PAGE.contains("aria-label=\"Impeccable\""));
assert!(PAGE.contains("color-scheme: light"));
assert!(!PAGE.contains("color-scheme: dark"));
}
// JS scenarios: tests/serve-question.test.mjs (public repo main,
// eaaecbd1 + 2e075dc5 + 7982002d).
+70
View File
@@ -129,6 +129,76 @@ function spawnSyncGen(prompt, out, size = null) {
// serve-question interactive cycles
// --------------------------------------------------------------------------
describe('new-work-e2e: serve-question decision page', () => {
it('renders a light system-font picker without font or external requests', async () => {
const cwd = makeWorkspace();
const key = 'system-fonts';
const context = await browser.newContext({ colorScheme: 'dark' });
const externalRequests = [];
const fontResponses = [];
try {
const { url } = await startDaemon(cwd, {
title: 'Choose the visual world',
options: [{ id: 'assigned', label: 'Local typography' }],
buildPath: { value: 'code', toggle: true },
}, key);
const origin = new URL(url).origin;
await context.route('**/*', (route) => {
if (new URL(route.request().url()).origin !== origin) {
externalRequests.push(route.request().url());
return route.abort();
}
return route.continue();
});
const page = await context.newPage();
page.on('response', (response) => {
if (response.request().resourceType() === 'font') {
fontResponses.push({ url: response.url(), status: response.status(), type: response.headers()['content-type'] });
}
});
await page.goto(url);
const appearance = await page.evaluate(async () => {
await document.fonts.ready;
const tokenColor = (token) => {
const probe = document.createElement('span');
probe.style.color = `var(${token})`;
document.body.append(probe);
const color = getComputedStyle(probe).color;
probe.remove();
return color;
};
return {
fonts: document.fonts.size,
family: getComputedStyle(document.body).fontFamily,
scheme: getComputedStyle(document.documentElement).colorScheme,
logoPaths: document.querySelectorAll('.brand svg path').length,
logoText: document.querySelectorAll('.brand svg text').length,
kinpaku: tokenColor('--ks-kinpaku'),
rule: tokenColor('--ks-rule'),
leadBorder: getComputedStyle(document.querySelector('.face.lead')).borderTopColor,
switchBorder: getComputedStyle(document.querySelector('.bp-switch')).borderTopColor,
activeDot: getComputedStyle(document.querySelector('.bp-opt.active'), '::before').backgroundColor,
};
});
assert.equal(appearance.fonts, 0, 'no custom font faces');
assert.match(appearance.family, /^system-ui,/);
assert.equal(appearance.scheme, 'light', 'picker stays light even with a dark OS preference');
assert.ok(appearance.logoPaths > 2, 'the brand mark and wordmark are vector outlines');
assert.equal(appearance.logoText, 0, 'the logo does not depend on a font');
assert.equal(appearance.leadBorder, appearance.kinpaku, 'lead outline uses default Kinpaku');
assert.equal(appearance.activeDot, appearance.kinpaku, 'active switch dot uses default Kinpaku');
assert.equal(appearance.switchBorder, appearance.rule, 'switch track has a quiet border');
await page.locator('.card').first().hover();
assert.equal(await page.locator('.face.lead').evaluate((el) => getComputedStyle(el).borderTopColor), appearance.kinpaku, 'hover preserves the default Kinpaku outline');
await page.getByRole('img', { name: 'Impeccable', exact: true }).waitFor();
assert.deepEqual(externalRequests, [], 'dialog must not request third-party resources');
assert.deepEqual(fontResponses, [], 'no bundled or remote font downloads');
} finally {
await context.close();
await stopDaemon(cwd, key);
rmSync(cwd, { recursive: true, force: true });
}
});
it('(a) pick assigned returns the option, hero/board fields, and the CHOSEN CARD directive', async () => {
const cwd = makeWorkspace();
const key = 'pick';