From 8461e9199698ce42b33f22d8c7e1b0019c0bd269 Mon Sep 17 00:00:00 2001
From: Arjun Mahanti
Date: Thu, 17 Sep 2026 09:42:33 -0400
Subject: [PATCH 1/3] Adopt shared semantic controls across the Buzz app
Signed-off-by: Arjun Mahanti
---
docs/design-system-adoption.md | 54 +++
docs/design-system.md | 25 +-
scripts/design-system/check-contrast.mjs | 2 +
src/app/App.tsx | 9 +-
src/app/AppearanceSettings.tsx | 49 +--
src/app/NotificationSettings.module.css | 6 -
src/app/PluginImport.tsx | 70 ++-
src/app/RecoveryScreen.tsx | 14 +-
src/app/Settings.module.css | 15 +-
src/app/Settings.tsx | 317 +++++++-------
src/app/pages.integration.test.mjs | 2 +-
src/app/shell/AppShell.tsx | 25 +-
src/app/shell/Home.tsx | 40 +-
src/app/shell/NavigationControls.tsx | 43 +-
src/app/shell/PageSearch.tsx | 60 ++-
src/app/shell/ProfileButton.tsx | 12 +-
src/bundled/channels/Channels.module.css | 173 +-------
src/bundled/channels/ChannelsPage.tsx | 400 +++++++++---------
src/bundled/channels/LiveStatus.tsx | 5 +-
src/bundled/channels/OutboxStatus.tsx | 9 +-
src/bundled/channels/RelayTimings.tsx | 9 +-
src/bundled/channels/SidebarUnread.tsx | 24 +-
src/bundled/channels/UnreadBadge.tsx | 17 +-
src/bundled/emoji/Emoji.module.css | 220 +---------
src/bundled/emoji/EmojiPicker.tsx | 176 +++-----
src/bundled/emoji/GifPicker.tsx | 40 +-
src/bundled/emoji/emoji-mart.ts | 55 ++-
src/bundled/github/GitHub.module.css | 22 +-
src/bundled/github/index.tsx | 5 +-
src/bundled/link-lab/LinkLab.module.css | 20 +-
src/bundled/link-lab/index.tsx | 3 +-
src/bundled/mentions/MentionPicker.tsx | 72 ++--
src/bundled/mentions/Mentions.module.css | 50 +--
src/bundled/profiles/Profiles.module.css | 8 +-
src/bundled/terminal/Terminal.module.css | 14 +-
src/bundled/workflows/ConfirmAction.tsx | 56 +--
src/bundled/workflows/WorkflowEditor.tsx | 7 +-
src/bundled/workflows/WorkflowForm.tsx | 5 +-
src/bundled/workflows/workflows.css | 51 +--
src/bundled/workflows/workflows.journey.mjs | 2 +-
.../communities/Communities.module.css | 116 +----
src/features/communities/CommunityDialog.tsx | 98 ++---
.../communities/CommunitySwitcher.tsx | 91 ++--
src/features/communities/ProfileFields.tsx | 22 +-
.../conversation/Completions.module.css | 17 +-
.../conversation/LinkPreview.module.css | 7 +-
src/features/conversation/ReactionTool.tsx | 10 +-
.../messages/ChannelTimeline.test.tsx | 3 +-
src/features/messages/ChannelTimeline.tsx | 5 +-
src/features/messages/DeliveryNotice.tsx | 10 +-
.../messages/EditableInput.module.css | 6 +-
src/features/messages/MessageComposer.tsx | 23 +-
src/features/messages/MessageRow.tsx | 8 +-
src/features/messages/Messages.module.css | 171 +++-----
src/features/messages/ThreadPanel.test.tsx | 3 +-
src/features/messages/ThreadPanel.tsx | 9 +-
src/features/panels/PanelCard.tsx | 35 +-
src/features/panels/Panels.module.css | 14 +-
src/shared/InlineReference.module.css | 10 +-
.../design-system/styles/components.css | 12 +-
src/shared/design-system/styles/forms.css | 3 +
src/shared/design-system/styles/tokens.css | 6 +
src/shared/design-system/tokens/registry.ts | 14 +
src/shared/design-system/ui/AlertDialog.tsx | 46 ++
src/shared/design-system/ui/Panel.tsx | 3 +-
src/shared/design-system/ui/RadioGroup.tsx | 51 ++-
src/shared/design-system/ui/Textarea.tsx | 6 +-
src/shared/design-system/ui/controls.test.tsx | 12 +-
src/shared/design-system/ui/registry.ts | 23 +-
src/shared/styles/globals.css | 28 +-
src/shared/styles/tokens.css | 116 ++---
tests/browser/appearance.spec.mjs | 26 +-
tests/browser/emoji.spec.mjs | 16 +-
tests/browser/gifs.spec.mjs | 211 ++-------
.../browser/navigation-scroll-intent.spec.mjs | 2 +-
tests/browser/navigation-sidebar.spec.mjs | 10 +-
tests/browser/profiles-appearance.spec.mjs | 12 +-
tests/browser/sidebar-unread.spec.mjs | 8 +-
tests/browser/terminal-renderer.spec.mjs | 4 +-
.../design-system/ui/componentSpecimens.tsx | 38 +-
80 files changed, 1440 insertions(+), 2051 deletions(-)
create mode 100644 docs/design-system-adoption.md
create mode 100644 src/shared/design-system/ui/AlertDialog.tsx
diff --git a/docs/design-system-adoption.md b/docs/design-system-adoption.md
new file mode 100644
index 00000000..52fb1018
--- /dev/null
+++ b/docs/design-system-adoption.md
@@ -0,0 +1,54 @@
+# Shared design system adoption
+
+Buzz keeps Base UI for interaction behavior and uses its own public palette,
+Inter and JetBrains Mono. No private fonts, packages, artwork or business examples
+are required. The visual direction follows Block UI: clear semantic color roles,
+pill actions, consistent fields and shared states.
+
+## From the audit to the app
+
+| Audit area | Shared owner | App adoption |
+| --- | --- | --- |
+| Colors and type | Semantic surface, text, border and affordance roles | Host aliases forward to shared roles; feature CSS uses role names; inline links have paired text and hover roles. |
+| Action sizes and states | Button and IconButton | Retry, refresh, delete, recovery, composer send and picker triggers. Buttons use 32/40/52px minimum sizes and allow labels to wrap. |
+| Forms and choices | Field, Input, Textarea, RadioGroup, Checkbox | Profile, community setup, appearance, plugin import and workflow editing. |
+| Search | SearchField | Channels, pages, members and GIFs preserve their query, ref and keyboard handlers. |
+| Navigation | NavigationItem | Settings, channel rows, shell destinations, Home and community choices. Route destinations remain buttons, not tabs. |
+| Tabs | Tabs | Emoji/GIF uses associated panels and Base UI keyboard activation. Workflow mode retains its existing externally owned editor view. |
+| Modals | Dialog and AlertDialog | Page search, community chooser/setup and workflow confirmations. Pending work prevents dismissal; focus returns to the opener. |
+| Panels and headers | Panel and PanelHeader | Settings, channels and companion cards use shared paint. Grids, scrolling, docks and subscriptions stay with the feature. |
+| Hints | Tooltip | Navigation history uses keyboard-accessible, dismissible hints. Accessible names stay on the controls. |
+
+## Deliberate local ownership
+
+- The rich message editor keeps its caret, IME, selection and completion logic.
+ Completion rows retain `aria-activedescendant` while using shared colors and type.
+- GIF tiles retain native media-selection buttons and image geometry. Search, retry
+ and picker triggers use shared controls.
+- Emoji Mart keeps its shadow-root adapter and compact search geometry. It reads
+ shared semantic colors, type and the host’s keyboard-focus mode. It does not own
+ another appearance preference.
+- Native disclosures remain for persisted channel groups and diagnostic content.
+ They are disclosures, not application menus; their content and state remain local.
+- Avatars, previews, links, mentions, thread summaries and recipient removal retain
+ their identity and navigation behavior. Shared appearance does not move their data.
+- Panel marks its surface separately from interactive components. Native product
+ and plugin content inside it can still receive host defaults.
+- Legacy utility names remain available through the host bridge for existing
+ callers and plugins. They are aliases, not another palette. Use the semantic
+ names and shared components for new work.
+
+## Checking a migration
+
+Check pointer and keyboard behavior, loading and failures, both color modes,
+narrow layouts and enlarged text. A rendered app check matters: a component can
+look right in the viewer while its stylesheet is missing from the host.
+
+Browser journeys retain community joining, workflow save/recovery, draft and
+sidebar persistence, media insertion and focus checks. Visual assertions should
+track the shared treatment. The media journey now checks tab/panel associations,
+keyboard activation and search clearing instead of the retired picker-specific
+stretch animation. No browser journey is removed by this migration.
+
+Before review/integration, run the contribution workflow’s full batch checks.
+Draft PRs and a running preview are not claims of native or full-suite validation.
diff --git a/docs/design-system.md b/docs/design-system.md
index 2421d658..29ac27a6 100644
--- a/docs/design-system.md
+++ b/docs/design-system.md
@@ -1,11 +1,10 @@
# Design system and appearance
-> **The design system going forward:** the imported system is documented in
-> [the handoff README](../src/shared/design-system/README.md) and displayed at
-> `/tests/fixtures/design-system.html`. New UI and existing surfaces moving off the
-> current styles should use it. This initial port does not migrate existing surfaces,
-> so the host styling described below still governs those callers until they move;
-> the host remains the single owner of appearance throughout the transition.
+The shared components live in `src/shared/design-system` and appear at
+`/tests/fixtures/design-system.html`. Buzz follows Block UI’s approach to semantic
+roles, controls and states, using Base UI for behavior and public fonts and assets.
+The app imports the same form and overlay styles as the component viewer.
+See [the adoption map](design-system-adoption.md) for ownership and retained adapters.
The **host** owns appearance, including startup and recovery. A plugin must not be
required to render the shell correctly. Pages still own their layout and behavior;
@@ -89,15 +88,11 @@ picker. Browser regressions cover host Settings changes through an open widget,
opening in Dark, and no updates after disposal. Mutation probes exercise the startup
script, Settings writes/retry, host lifetime and widget initial/update/disposal paths.
-The initial integration batch passed the theme/picker journeys in both browsers,
-Node/Vitest/plugin-manager tests, formatting/types, build and Clippy. The broad browser
-run was **89/90**, not green: WebKit's `initial-position.spec.mjs` reload case reported
-a localhost access-control console warning. The identical failure reproduced on
-pre-theme `742a770` (one failure, two passes); its cause is not diagnosed or suppressed.
-Native Rust test targets compile but contain zero tests. Human light/dark visual
-approval and independent source review do not replace attended packaged-app
-chrome/relaunch acceptance. Browser evidence also does not cover third-party plugins
-that hard-code their own colors.
+Run the design guards, unit tests and relevant browser journeys for a changed
+component. Native window chrome and relaunch still need an attended packaged-app
+check; browser checks do not establish native acceptance or third-party plugin
+styling. Keep full batch validation separate from an interactive preview.
+
## Text size and shortcuts
diff --git a/scripts/design-system/check-contrast.mjs b/scripts/design-system/check-contrast.mjs
index c4ef893f..417719c8 100644
--- a/scripts/design-system/check-contrast.mjs
+++ b/scripts/design-system/check-contrast.mjs
@@ -89,6 +89,7 @@ const TEXT_ROLES = [
"--text-warning",
"--text-success",
"--text-accent",
+ "--text-link",
"--text-primary",
"--text-secondary",
"--text-tertiary",
@@ -109,6 +110,7 @@ const TEXT_ROLES = [
* every fill it can actually sit on, and hover is one of them.
*/
const PAIRS = [
+ ["--text-link", "--affordance-link-hover"],
...["subtle", "subtle-hover", "subtle-pressed"].map((state) => [
"--text-standard",
`--affordance-${state}`,
diff --git a/src/app/App.tsx b/src/app/App.tsx
index bc0653b0..a9d1168a 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -1,3 +1,4 @@
+import { Button } from "../shared/design-system/ui/Button";
// FOUNDATION: Startup, navigation, contributed pages, and built-in Settings.
import { useEffect, useSyncExternalStore } from "react";
import { registerAppShortcuts } from "./shortcuts";
@@ -88,12 +89,12 @@ export function App({ services }: { services: AppServices }) {
? "This target needs its original account and an already joined community."
: "The destination is unavailable or isn’t supported yet. Your target has been kept for retry."}