From 67c2b15aa2d47f91621b37491594b377f4328a9b Mon Sep 17 00:00:00 2001 From: Maggie Appleton <5599295+MaggieAppleton@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:49:10 +0100 Subject: [PATCH 1/3] Badge Chat and Document activity with one still petrol dot, and even out the title bar With the document expanded, the title bar's toggle shows the Chat glyph instead of inward arrows, and unread activity is a badge on that glyph's corner, knocked out of the bar with a ring that follows the ghost fill. The Document view's new-changes mark is the same solid dot, inline after the label and no longer pulsing. The phone's tab marks share the treatment. The title bar's project, title and Zoom crumb now read at the views' text size, the repository icon returns before the project, and the project sits as far from the toggle's rule as the toggle's glyph sits from the edge. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/decision-view-control.tsx | 4 +- apps/web/src/document-activity.test.ts | 9 +---- apps/web/src/document-activity.tsx | 10 ++--- apps/web/src/icon-assets.test.ts | 2 +- apps/web/src/phone-workspace.css | 8 +++- apps/web/src/room-workspace.tsx | 1 + apps/web/src/theme.css | 54 +++++++++++++++++++++++--- apps/web/src/workspace.tsx | 22 +++++------ e2e/document-activity.e2e.ts | 9 +++++ e2e/responsive-activity.e2e.ts | 4 ++ 10 files changed, 89 insertions(+), 34 deletions(-) diff --git a/apps/web/src/decision-view-control.tsx b/apps/web/src/decision-view-control.tsx index ae1260a50..2ddad7c7c 100644 --- a/apps/web/src/decision-view-control.tsx +++ b/apps/web/src/decision-view-control.tsx @@ -70,7 +70,7 @@ export function DecisionViewControl( aria-current={view === "plan" ? "page" : undefined} aria-label={view === "plan" ? undefined : documentActivityLabel(documentActivity)} aria-pressed={view === "plan"} - className={`btn btn-sm relative transition-[background-color,box-shadow,color] ${ + className={`btn btn-sm gap-1.5 transition-[background-color,box-shadow,color] ${ view === "plan" ? "bg-ground font-medium text-gray-800" : "text-text-tertiary hover:bg-hover" @@ -79,7 +79,7 @@ export function DecisionViewControl( type="button" > Document - {view !== "plan" && } + {view !== "plan" && } ); } diff --git a/e2e/document-activity.e2e.ts b/e2e/document-activity.e2e.ts index c5ed8ed4b..a5c736c5f 100644 --- a/e2e/document-activity.e2e.ts +++ b/e2e/document-activity.e2e.ts @@ -54,6 +54,15 @@ for ( await expect(documentTab).toHaveAccessibleName("Document, new changes"); await expect(documentTab.locator("[data-document-activity='unseen']")).toBeVisible(); + if (name === "desktop") { + // A still dot after the label, inside the tab and centred on its text. + let dot = documentTab.locator("[data-document-activity='unseen']"); + await expect(dot).toHaveCSS("animation-name", "none"); + let mark = (await dot.boundingBox())!; + let tab = (await documentTab.boundingBox())!; + expect(mark.x + mark.width).toBeLessThanOrEqual(tab.x + tab.width); + expect(Math.abs(mark.y + mark.height / 2 - (tab.y + tab.height / 2))).toBeLessThanOrEqual(1); + } await documentTab.click(); await expect(content(page)).toContainText("Revised paragraph."); diff --git a/e2e/responsive-activity.e2e.ts b/e2e/responsive-activity.e2e.ts index 406d04851..90aad2973 100644 --- a/e2e/responsive-activity.e2e.ts +++ b/e2e/responsive-activity.e2e.ts @@ -133,6 +133,10 @@ test("a closed desktop Chat tab keeps unread activity visible", async ({ join, p let toggle = page.getByRole("button", { name: "Show chat, 1 unread" }); await expect(toggle).toBeVisible(); await expect(toggle.locator('span[aria-hidden="true"]')).toHaveCount(1); + // The badge sits on the Chat glyph's corner, clear of the label. + let badge = (await toggle.locator('span[aria-hidden="true"]').boundingBox())!; + let label = (await toggle.getByText("Chat", { exact: true }).boundingBox())!; + expect(badge.x + badge.width).toBeLessThanOrEqual(label.x); let count = toggle.locator('[data-motion-feedback="count"]'); expect( await count.evaluate(element => From 61f27408c75260f77efc1ec55c5121f11065bb46 Mon Sep 17 00:00:00 2001 From: Maggie Appleton <5599295+MaggieAppleton@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:49:10 +0100 Subject: [PATCH 2/3] Renew design-contract review for the Chat toggle badge The reviewed dynamic owners in workspace.tsx keep the same data flow: SurfaceToggle still composes motionContract("feedback") into its icon span and, while unread, into the badge span (now nested in the icon); the Chat pane presence class is unchanged. Co-Authored-By: Claude Opus 5.5 --- scripts/design-contract/exceptions/dynamic-web.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/design-contract/exceptions/dynamic-web.json b/scripts/design-contract/exceptions/dynamic-web.json index f8c552d4a..1c07e47ba 100644 --- a/scripts/design-contract/exceptions/dynamic-web.json +++ b/scripts/design-contract/exceptions/dynamic-web.json @@ -276,7 +276,7 @@ 1 ] ], - "sourceHash": "f6dc937a28e2d6641497848821c5fae44a597375191207598dfba0fa58fea5ee" + "sourceHash": "6dcdd2c5d6243bdc297c311742fc47e8e5da433d201b6e1b728c42c714d9a9c4" }, { "file": "apps/web/src/navigation-dialog.tsx", From f24e23322d7be8d046ae8d5adab78f34ef3bf9e1 Mon Sep 17 00:00:00 2001 From: Maggie Appleton <5599295+MaggieAppleton@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:09:11 +0100 Subject: [PATCH 3/3] Hold the activity ring at 2px on every display and drop the unstyled dot class The knockout ring used the edge width, which is a hairline on Retina, so it now uses half a spacing step. The document mark's test checks for animation on its data attribute and the shared dot class instead of a class nothing styled. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/document-activity.test.ts | 3 ++- apps/web/src/document-activity.tsx | 4 +--- apps/web/src/phone-workspace.css | 2 +- apps/web/src/theme.css | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/web/src/document-activity.test.ts b/apps/web/src/document-activity.test.ts index 63ade6e98..032171163 100644 --- a/apps/web/src/document-activity.test.ts +++ b/apps/web/src/document-activity.test.ts @@ -66,5 +66,6 @@ test("labels name the activity for assistive technology", () => { test("the document mark stays still while the Planner writes", () => { let css = readFileSync(join(import.meta.dir, "theme.css"), "utf8"); - expect(css).not.toMatch(/\.document-activity-dot[^{]*\{[^}]*animation:/); + expect(css).not.toMatch(/\[data-document-activity[^{]*\{[^}]*animation:/); + expect(css).not.toMatch(/\.activity-dot[^{]*\{[^}]*animation:/); }); diff --git a/apps/web/src/document-activity.tsx b/apps/web/src/document-activity.tsx index b71cc95bc..d9798133a 100644 --- a/apps/web/src/document-activity.tsx +++ b/apps/web/src/document-activity.tsx @@ -84,9 +84,7 @@ export function DocumentActivityDot( return (