From 22bf9a81205a05fb5e91a8c396ac73879c503e10 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Thu, 3 Sep 2026 05:37:55 -0700 Subject: [PATCH] =?UTF-8?q?feat(v2):=20TASK-122=20Phase=20A=20=E2=80=94=20?= =?UTF-8?q?the=20ruled=20shell=20restyle=20(tint=20step,=20radius=20ladder?= =?UTF-8?q?,=20Inter,=20ink=20primary,=20halo=20focus,=20stable=20rows)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sam ruled the shell parity look on 2026-09-03 (canvas Board 3); TASK-122 is the row, TASK-123 the Phase A build. This lands the shared tokens plus the Activity page, its compose box and its reply box, so the live page matches the ruling. Tokens (v2.css :root and design-system/tokens.css move together): --v2-shell-bg #f1f1f4, --v2-content-radius 14px, radius ladder 6/10/14 (--v2-radius-sm 8→6, --v2-radius-lg 12→14), --v2-ink #111827 / --v2-ink-hover #1f2937 / --v2-on-ink #fff, --v2-shadow-pending 0 1px 2px rgba(15,23,42,.06), --v2-font "Inter Variable" first (self-hosted via @fontsource-variable/inter, imported in V2App.tsx above v2.css; SF Pro is the fallback), body 14/20, meta 12/16, labels 11/14. Shell: .v2-shell and the rail/pods/inspector panes take the shell tint; the content pane becomes the inset white card (8px gutter, 1px ring, 14px radius, flush below 760px; banner variant keeps its height). Activity: compose box ring + halo focus (the two outline:2px overrides are deleted; the global halo applies, the pod is outside the global halo's element list, so the pod picker carries its own. + expect(ruleBody(v2, '.v2-activity__compose-pod select:focus-visible')).toContain('box-shadow: var(--v2-focus-ring)'); + }); + + test('ink primary: filled Activity buttons are ink, and blue stays off them', () => { + expect(cssVariable(v2Root, '--v2-ink')).toBe('#111827'); + expect(cssVariable(tokens, '--c-ink')).toBe('#111827'); + const send = ruleBody(v2, '.v2-root .v2-activity__compose button'); + expect(send).toContain('var(--v2-ink)'); + expect(send).not.toContain('var(--v2-accent)'); + expect(ruleBody(v2, '.v2-root .v2-activity__queue-actions button')).not.toContain('var(--v2-accent)'); + }); + + test('Inter is self-hosted, first in the stack, and imported before v2.css', () => { + const app = read('../V2App.tsx'); + const fontImport = app.indexOf("import '@fontsource-variable/inter';"); + expect(fontImport).toBeGreaterThan(-1); + expect(fontImport).toBeLessThan(app.indexOf("import './v2.css';")); + expect(cssVariable(v2Root, '--v2-font')?.startsWith('"Inter Variable"')).toBe(true); + expect(cssVariable(tokens, '--c-font-sans')?.startsWith('"Inter Variable"')).toBe(true); + expect(v2Root).toContain('font-size: var(--v2-fs-body)'); + expect(v2Root).toContain('line-height: var(--v2-lh-body)'); + expect(cssVariable(v2Root, '--v2-lh-body')).toBe('20px'); + const pkg = JSON.parse(read('../../../package.json')); + expect(pkg.dependencies['@fontsource-variable/inter']).toBeDefined(); + }); + }); + }); diff --git a/frontend/src/v2/components/V2ActivityPage.tsx b/frontend/src/v2/components/V2ActivityPage.tsx index 57bd59a38..ae6cae9b6 100644 --- a/frontend/src/v2/components/V2ActivityPage.tsx +++ b/frontend/src/v2/components/V2ActivityPage.tsx @@ -426,7 +426,7 @@ const V2ActivityPage: React.FC = () => { ) : (
{queue.map((item) => ( -
+
diff --git a/frontend/src/v2/v2.css b/frontend/src/v2/v2.css index ff8054670..b5fad6157 100644 --- a/frontend/src/v2/v2.css +++ b/frontend/src/v2/v2.css @@ -32,6 +32,17 @@ body.modern-ui.v2-canvas { --v2-text-tertiary: #7b8494; --v2-text-muted: #8a93a3; + /* TASK-122 Phase A (Sam ruled 2026-09-03): the tint step. The shell ground + sits behind rail, pods and inspector; the content pane is an inset white + card. Mirrors --c-shell-bg / --c-content-radius in design-system/tokens.css. */ + --v2-shell-bg: #f1f1f4; + --v2-content-radius: 14px; + /* Ink primary — filled buttons are ink; blue stays for links, mention marks + and the focus halo. Mirrors --c-ink / --c-ink-hover / --c-on-ink. */ + --v2-ink: #111827; + --v2-ink-hover: #1f2937; + --v2-on-ink: #ffffff; + /* BEND-1 (Wren spec 2026-08-30, Sam-approved): the featured-tier name needs a step between 16 and 20. Mirrors --c-fs-feature in design-system/tokens.css; the two move together. */ @@ -86,26 +97,33 @@ body.modern-ui.v2-canvas { --v2-sky: #60a5fa; --v2-rose: #fb7185; - /* Typography — SF-first stack. SF loads natively on Apple devices via - -apple-system / BlinkMacSystemFont; non-Apple fallbacks (Segoe / Inter) - keep weights and metrics close. No webfont download. */ - --v2-font: "SF Pro Text", -apple-system, BlinkMacSystemFont, "SF Pro", - "Helvetica Neue", "Segoe UI", "Inter", Roboto, sans-serif; - --v2-font-display: "SF Pro Display", -apple-system, BlinkMacSystemFont, - "SF Pro", "Helvetica Neue", "Segoe UI", "Inter", sans-serif; + /* Typography — Inter (variable, self-hosted via @fontsource-variable/inter, + imported in V2App.tsx above this sheet). SF Pro is the fallback only while + the woff2 loads or if it fails. One face: display weight comes from + font-weight, not a second family. Mirrors --c-font-sans. */ + --v2-font: "Inter Variable", "Inter", "SF Pro Text", -apple-system, + BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --v2-font-display: var(--v2-font); + --v2-fs-body: 14px; + --v2-lh-body: 20px; + --v2-fs-meta: 12px; + --v2-lh-meta: 16px; + --v2-fs-label: 11px; + --v2-lh-label: 14px; --v2-font-mono: "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "JetBrains Mono", "Fira Code", monospace; /* Geometry */ - --v2-radius-sm: 8px; + --v2-radius-sm: 6px; --v2-radius: 10px; - --v2-radius-lg: 12px; + --v2-radius-lg: 14px; --v2-radius-pill: 999px; /* Elevation */ --v2-shadow-sm: none; --v2-shadow: none; --v2-shadow-lg: none; + --v2-shadow-pending: 0 1px 2px rgba(15, 23, 42, 0.06); /* the one chrome-card shadow: a PENDING decision or approval only */ /* Layout */ --v2-rail-w: 76px; @@ -131,8 +149,8 @@ body.modern-ui.v2-canvas { overflow: hidden; display: flex; flex-direction: column; - font-size: 14px; - line-height: 1.45; + font-size: var(--v2-fs-body); + line-height: var(--v2-lh-body); min-width: 0; } @@ -468,6 +486,7 @@ body.modern-ui.v2-canvas { grid-template-rows: 1fr; height: 100vh; width: 100%; + background: var(--v2-shell-bg); } .v2-shell--no-inspector { @@ -487,12 +506,12 @@ body.modern-ui.v2-canvas { overflow: hidden; display: flex; flex-direction: column; - border-right: 1px solid var(--v2-border-soft); - background: var(--v2-bg-subtle); + border-right: 1px solid transparent; /* the content card's ring replaces the pane hairlines */ + background: var(--v2-shell-bg); } .v2-pane--rail { - background: var(--v2-bg-subtle); + background: var(--v2-shell-bg); /* The rail is only 76px wide, so its language dropdown (a wider floating menu) must be allowed to escape the pane instead of being clipped by the base .v2-pane `overflow: hidden`. The rail's own content is self-contained @@ -500,15 +519,32 @@ body.modern-ui.v2-canvas { overflow: visible; } +/* The content pane is the inset card: 8px gutter top/right/bottom, 1px ring, + 14px radius (TASK-122). Height subtracts the gutters; the banner variant beats + `.v2-authenticated-shell__content .v2-pane { height: 100% }` on specificity. */ .v2-pane--main { + margin: 8px 8px 8px 0; + height: calc(100vh - 16px); background: #ffffff; - border-right: none; + border: 1px solid var(--v2-border); + border-radius: var(--v2-content-radius); + overflow: hidden; +} + +.v2-authenticated-shell .v2-pane--main { + height: calc(100% - 16px); +} + +/* Inspector open: the card meets the inspector gutter. --feature-wide has no + inspector column, so it keeps its right gutter. */ +.v2-shell:not(.v2-shell--no-inspector):not(.v2-shell--feature):not(.v2-shell--feature-wide) .v2-pane--main { + margin-right: 0; } .v2-pane--inspector { border-right: none; border-left: 1px solid var(--v2-border-soft); - background: var(--v2-surface-tint); + background: var(--v2-shell-bg); } /* Mobile pods-drawer affordances — hidden on desktop, where the pods sidebar @@ -5120,7 +5156,7 @@ body.modern-ui.v2-canvas { .v2-feature { min-width: 0; - background: var(--v2-page-bg); + background: #ffffff; /* inside the card the ground is white; the card is the tint step */ } .v2-feature__header { @@ -5826,6 +5862,16 @@ body.modern-ui.v2-canvas { } @media (max-width: 760px) { + /* Phones: the card fills the screen; the drawers keep the shell tint. Both + selectors, or the banner variant keeps the desktop height. */ + .v2-pane--main, + .v2-authenticated-shell .v2-pane--main { + margin: 0; + height: 100%; + border: 0; + border-radius: 0; + } + .v2-shell, .v2-shell--no-inspector, .v2-shell--feature { @@ -8448,8 +8494,9 @@ body.modern-ui.v2-canvas { .v2-activity__title { font-size: clamp(26px, 3vw, 34px); + font-weight: 700; /* 850 was tuned for SF Pro Display; Inter reads right at 700 */ line-height: 1.08; - letter-spacing: -0.035em; + letter-spacing: -0.03em; } .v2-activity__subtitle, @@ -8518,7 +8565,7 @@ body.modern-ui.v2-canvas { min-height: 118px; gap: 4px; padding: 20px; - border: 1px solid var(--v2-border-soft); + border: 1px solid var(--v2-border); border-radius: var(--v2-radius); color: var(--v2-text-secondary); text-align: center; @@ -8542,11 +8589,15 @@ body.modern-ui.v2-canvas { .v2-activity__compose { margin-top: 22px; padding: 16px; - border: 1px solid var(--v2-border-soft); + border: 1px solid var(--v2-border); border-radius: var(--v2-radius); background: var(--v2-surface); } +.v2-activity__compose:focus-within { + border-color: var(--v2-border-strong); +} + .v2-activity__compose-heading, .v2-activity__compose-entry, .v2-activity__compose-pod { @@ -8572,8 +8623,10 @@ body.modern-ui.v2-canvas { align-items: flex-start; gap: 4px; color: var(--v2-text-muted); - font-size: 12px; - font-weight: 700; + font-size: var(--v2-fs-label); + line-height: var(--v2-lh-label); + font-weight: 600; + letter-spacing: 0.02em; } .v2-activity__compose-pod select { @@ -8592,36 +8645,41 @@ body.modern-ui.v2-canvas { flex: 1 1 auto; min-width: 0; resize: vertical; - padding: 10px 11px; + padding: 10px 12px; border: 1px solid var(--v2-border); border-radius: var(--v2-radius-sm); background: var(--v2-surface); color: var(--v2-text-primary); font: inherit; - font-size: 14px; - line-height: 1.45; } -.v2-root .v2-activity__compose textarea:focus-visible, +/* Halo focus (TASK-122): the global `.v2-root textarea:focus-visible` rule + carries the box-shadow halo; this only gives it an edge.