ai: scope console theme to content wrapper, fix font loading

Two bugs in the previous /ai redesign:

1. theme="console" on AppShell put the entire shell (sidebar, appbar,
   appbar dropdowns, the lot) inside [data-theme="console"], so the
   console palette + JetBrains Mono override leaked into the sidebar
   and made light mode look broken on /ai. Scoped now: the AppShell
   stays in skyrise (so light/dark toggle keeps working everywhere),
   and only the route content area gets data-theme="console" via an
   inner wrapper.

2. The Google Fonts @import inside console.css was being silently
   dropped because @import rules must precede all other rules in the
   final bundle, and skyrise's content lands first. Moved JetBrains
   Mono + Newsreader into app.css's top-level @import url() alongside
   the existing Inter/Instrument Sans/Geist Mono families.

Atmosphere ::before was also position: fixed, which painted the grain
overlay across the whole viewport (including the sidebar) regardless
of where data-theme lived. Now position: absolute on the wrapper, with
isolation: isolate to keep z-index local.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-05-02 19:39:51 +10:00
parent 4f699bb90e
commit 066a16bb8b
3 changed files with 31 additions and 14 deletions

View File

@@ -1,4 +1,4 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Instrument+Sans:ital,wght@0,400..700;1,400..700&family=Geist+Mono:wght@100..900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Instrument+Sans:ital,wght@0,400..700;1,400..700&family=Geist+Mono:wght@100..900&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400&display=swap");
/* Active theme — must be first so its @import url() font directives resolve /* Active theme — must be first so its @import url() font directives resolve
* to the top of the output. Themes are self-contained: tokens + fonts. */ * to the top of the output. Themes are self-contained: tokens + fonts. */

View File

@@ -316,8 +316,15 @@ export default function AIRoute() {
const availableModels = status.kind === "live" ? status.models : ["mock"] const availableModels = status.kind === "live" ? status.models : ["mock"]
return ( return (
<AppShell title="AI" theme="console"> <AppShell title="AI">
<LLMProvider adapter={adapter} model={activeModel}> <LLMProvider adapter={adapter} model={activeModel}>
{/* Console aesthetic is scoped to this wrapper only, so the appbar
* and sidebar keep using the global skyrise tokens (light/dark
* toggle still works for them). */}
<div
data-theme="console"
className="-m-6 flex h-full min-h-0 flex-col bg-[var(--console-ink)] text-[var(--console-text)]"
>
<ChatSurface <ChatSurface
models={availableModels} models={availableModels}
model={activeModel} model={activeModel}
@@ -328,6 +335,7 @@ export default function AIRoute() {
isMock={status.kind === "mock"} isMock={status.kind === "mock"}
onRetryProbe={probe} onRetryProbe={probe}
/> />
</div>
</LLMProvider> </LLMProvider>
</AppShell> </AppShell>
) )

View File

@@ -11,7 +11,9 @@
* with skyrise / vibespace tokens. * with skyrise / vibespace tokens.
* ============================================================================ */ * ============================================================================ */
@import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400&display=swap"); /* Fonts (JetBrains Mono + Newsreader) are loaded by app.css's top-level
* @import url() — keep this file free of @import statements so it can sit
* inside the bundle without violating "@import before any rule". */
[data-theme="console"] { [data-theme="console"] {
/* ── Palette ─────────────────────────────────────────────────────────── */ /* ── Palette ─────────────────────────────────────────────────────────── */
@@ -90,10 +92,17 @@
var(--console-ink); var(--console-ink);
} }
/* Wrapper must be a positioning context so the grain overlay (and any
* other absolutely-positioned atmosphere) doesn't escape to the viewport. */
[data-theme="console"] {
position: relative;
isolation: isolate;
}
/* Grain — single SVG turbulence, low opacity. Doesn't ship as an asset. */ /* Grain — single SVG turbulence, low opacity. Doesn't ship as an asset. */
[data-theme="console"]::before { [data-theme="console"]::before {
content: ""; content: "";
position: fixed; position: absolute;
inset: 0; inset: 0;
pointer-events: none; pointer-events: none;
opacity: 0.035; opacity: 0.035;