diff --git a/app/globals.css b/app/globals.css index e17e287..eeb3716 100644 --- a/app/globals.css +++ b/app/globals.css @@ -104,7 +104,6 @@ code-block chrome + mermaid-error + demo-card all use them. */ --font-ui: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace; --font-prose: var(--font-sans3), ui-sans-serif, system-ui, sans-serif; - --font-title: "lh-cmu", serif; --font-mono: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace; --font-sans: var(--font-sans3), ui-sans-serif, system-ui, sans-serif; /* The modular type ladder — each size carries its own line-height. @@ -114,6 +113,12 @@ legibility floor; quiet labels get their quiet from color + tracking, not a notch of size. Reading prose = the 16px rung via --lh-prose-size.) */ + /* --font-title lives in the NON-inline @theme below: inline tokens get + their literal values baked into the utilities (.font-title{font-family: + lh-cmu,serif}), which made the appearance "session names in serif" + toggle write a runtime --font-title no consumer ever read (owner, fix + wave: toggle looked dead). Non-inline emits :root { --font-title: … } + and the utility reads var(--font-title) — appearance overrides win. */ --text-xs: 11px; --text-xs--line-height: 1.4; --text-sm: 12.5px; @@ -444,3 +449,10 @@ body { .lh-electron .lh-closed-toggle { margin-left: 92px; } + +/* --font-title deliberately NOT @theme inline (see comment above): the + session-name-serif toggle overrides this var at runtime, so the + .font-title utility must read it through var(), not carry a baked copy. */ +@theme { + --font-title: "lh-cmu", serif; +} diff --git a/components/settings/appearance-section.tsx b/components/settings/appearance-section.tsx index ed4bb0b..6b12247 100644 --- a/components/settings/appearance-section.tsx +++ b/components/settings/appearance-section.tsx @@ -73,6 +73,13 @@ export function AppearanceSection({ ariaLabel="prose font size" testid="input-prose-size" onCommit={(v) => { + // Empty = factory default (owner, fix wave): omit the key so the record + // UNSETS and the field stays empty (absence, not null, is the + // unset for numbers). Number("") is 0, so the old clamp wrote 12. + if (v === "") { + onSectionChange({ ...appearance, proseFontSize: undefined }); + return; + } const n = Number(v); if (!Number.isFinite(n)) return; // bad input: no write, factory stays onSectionChange({ ...appearance, proseFontSize: Math.min(24, Math.max(12, Math.round(n))) }); @@ -94,6 +101,12 @@ export function AppearanceSection({ ariaLabel="heading weight" testid="input-heading-weight" onCommit={(v) => { + // Empty = factory default (owner, fix wave): Number("") is 0, so + // Absence is the unset representation — the old clamp wrote 100. + if (v === "") { + onSectionChange({ ...appearance, mdRendering: { ...md, headingWeight: undefined } }); + return; + } const n = Number(v); if (!Number.isFinite(n)) return; onSectionChange({