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."}

- - + + ) : settings ? (
-
- Color mode -

appearance.setMode(value)} > - Choose how Buzz looks on this device. Your choice is saved - automatically. -

-
{( [ ["light", "Light", Sun], ["dark", "Dark", Moon], ] as const ).map(([value, label, Icon]) => ( - + value={value} + variant="card" + label={ + + + } + /> ))} -
-
+ +
Text size

diff --git a/src/app/NotificationSettings.module.css b/src/app/NotificationSettings.module.css index 37e9bf99..560c8a31 100644 --- a/src/app/NotificationSettings.module.css +++ b/src/app/NotificationSettings.module.css @@ -7,9 +7,3 @@ flex-wrap: wrap; gap: var(--space-2); } - -.actions > button { - height: auto; - min-height: var(--size-control); - padding-block: var(--space-2); -} diff --git a/src/app/PluginImport.tsx b/src/app/PluginImport.tsx index 4cd29cb7..a6572bbf 100644 --- a/src/app/PluginImport.tsx +++ b/src/app/PluginImport.tsx @@ -1,3 +1,6 @@ +import { Field } from "../shared/design-system/ui/Field"; +import { Input } from "../shared/design-system/ui/Input"; +import { Radio, RadioGroup } from "../shared/design-system/ui/RadioGroup"; import { Button } from "../shared/design-system/ui/Button"; import { useEffect, useRef, useState } from "react"; import { @@ -111,25 +114,23 @@ export function PluginImport({ void load(() => imports.git(repository, reference)); }} > - - +

HTTPS or SSH; GitHub owner/repository also works. SSH uses your agent and known hosts. Password prompts and credential helpers are @@ -180,38 +181,31 @@ export function PluginImport({ output folder, or use a repository that includes built artifacts.

) : ( -
- - Choose a plugin folder - - {preview.candidates.map((item) => ( - - ))} -
+ ))} + + )} {preview.warnings.length > 0 && (
diff --git a/src/app/RecoveryScreen.tsx b/src/app/RecoveryScreen.tsx index 80b213b9..d0a56146 100644 --- a/src/app/RecoveryScreen.tsx +++ b/src/app/RecoveryScreen.tsx @@ -1,3 +1,4 @@ +import { Button } from "../shared/design-system/ui/Button"; // FOUNDATION: Recovery must remain usable without executing any plugin. import { useSyncExternalStore } from "react"; import type { PluginManager } from "../plugins/manager"; @@ -17,18 +18,23 @@ export function RecoveryScreen({ plugins }: { plugins: PluginManager }) { Error details

{reason}

- + {canReset ? ( <>

Resetting saves a backup and restores bundled defaults. Installed plugin files remain on disk and can be reinstalled.

- + ) : (

diff --git a/src/app/Settings.module.css b/src/app/Settings.module.css index 02e96651..b9c03bef 100644 --- a/src/app/Settings.module.css +++ b/src/app/Settings.module.css @@ -5,10 +5,6 @@ min-height: 0; container-type: inline-size; overflow: hidden; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius-card); - box-shadow: var(--elevation-card); } .layout { display: grid; @@ -23,7 +19,7 @@ overflow-y: auto; } .sidebar { - border-right: 1px solid var(--border); + border-right: 1px solid var(--border-standard); padding: var(--space-2); } .navigation { @@ -31,13 +27,6 @@ flex-direction: column; gap: var(--space-1); } -.navigationRow { - border-radius: var(--radius-row); - @apply flex items-center gap-3 border-0 bg-transparent px-3 py-2 text-left text-label-sm text-muted hover:bg-neutral-3 aria-[current=page]:bg-neutral-2 aria-[current=page]:text-ink; -} -.navigationRow svg { - flex-shrink: 0; -} .detail { padding: var(--space-6); container-type: inline-size; @@ -56,7 +45,7 @@ } .sidebar { border-right: 0; - border-bottom: 1px solid var(--border); + border-bottom: 1px solid var(--border-standard); } .navigation { flex-direction: row; diff --git a/src/app/Settings.tsx b/src/app/Settings.tsx index 2592c961..088f25ef 100644 --- a/src/app/Settings.tsx +++ b/src/app/Settings.tsx @@ -1,3 +1,5 @@ +import { Panel } from "../shared/design-system/ui/Panel"; +import { NavigationItem } from "../shared/design-system/ui/NavigationItem"; import { Button } from "../shared/design-system/ui/Button"; import { Switch } from "../shared/design-system/ui/Switch"; import { useEffect, useState, useSyncExternalStore } from "react"; @@ -80,165 +82,174 @@ export function Settings({ const catalog = ready?.catalog; const externalPluginsPaused = ready?.externalPluginsPaused; return ( -

-
- -
- - - - {developerMode && ( -
+
- - + + ); } diff --git a/src/app/pages.integration.test.mjs b/src/app/pages.integration.test.mjs index 1f9860e9..9d005114 100644 --- a/src/app/pages.integration.test.mjs +++ b/src/app/pages.integration.test.mjs @@ -195,7 +195,7 @@ test("the app runtime exposes ready bundled pages and removes them on disable", assert.equal(projects.layout, "workspace"); assert.match( renderToStaticMarkup(createElement(projects.component)), - /^
]*>]*>Projects<\/h1><\/div><\/section><\/div>$/, + /^
]*>]*>Projects<\/h1><\/div><\/section><\/div>$/, ); await services.plugins.change("disable", "buzz.projects"); assert.deepEqual(services.pages.snapshot(), []); diff --git a/src/app/shell/AppShell.tsx b/src/app/shell/AppShell.tsx index e922d971..7cb1d9b6 100644 --- a/src/app/shell/AppShell.tsx +++ b/src/app/shell/AppShell.tsx @@ -1,3 +1,4 @@ +import { NavigationItem } from "../../shared/design-system/ui/NavigationItem"; import type { ReactNode } from "react"; import { IconHome as House } from "@tabler/icons-react"; import { isTauri } from "@tauri-apps/api/core"; @@ -66,28 +67,28 @@ export function AppShell({ />
diff --git a/src/app/shell/Home.tsx b/src/app/shell/Home.tsx index ce045396..6dc1fcf0 100644 --- a/src/app/shell/Home.tsx +++ b/src/app/shell/Home.tsx @@ -1,3 +1,4 @@ +import { NavigationItem } from "../../shared/design-system/ui/NavigationItem"; import { IconArrowUpRight as ArrowUpRight, IconAdjustments as Settings2, @@ -26,42 +27,25 @@ export function Home({ A little space for everything.

Make yourself at home.

-
+
{orderPages(pages).map((page) => { const { label, icon: Icon } = pagePresentation(page); return ( - + label={label} + icon={
); diff --git a/src/app/shell/NavigationControls.tsx b/src/app/shell/NavigationControls.tsx index 5c009bef..402738ad 100644 --- a/src/app/shell/NavigationControls.tsx +++ b/src/app/shell/NavigationControls.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from "../../shared/design-system/ui/Tooltip"; import { IconButton } from "../../shared/design-system/ui/IconButton"; import { useSyncExternalStore } from "react"; import { @@ -12,26 +13,28 @@ export function NavigationControls({ navigation }: { navigation: Navigation }) { className="mr-2 flex shrink-0 items-center gap-0.5" aria-label="Navigation history" > -
); diff --git a/src/bundled/channels/Channels.module.css b/src/bundled/channels/Channels.module.css index b36a01f7..a2bb989a 100644 --- a/src/bundled/channels/Channels.module.css +++ b/src/bundled/channels/Channels.module.css @@ -4,7 +4,7 @@ .root { height: 100%; min-height: 0; - color: var(--text); + color: var(--text-standard); font-size: var(--text-body-sm); line-height: var(--text-body-sm--line-height); letter-spacing: var(--text-body-sm--letter-spacing); @@ -25,39 +25,18 @@ } .sidebar, .conversation { + height: 100%; min-width: 0; min-height: 0; overflow: hidden; display: flex; flex-direction: column; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius-card); - box-shadow: var(--elevation-card); } .sidebar { padding: var(--space-2); } .search { - display: flex; - align-items: center; - gap: var(--space-2); - padding: var(--space-1h) var(--space-2); margin-bottom: var(--space-4); - border-radius: var(--radius-control); - background: var(--surface-input); - color: var(--text-muted); -} -.search input { - border: 0; - outline: none; - background: none; - width: 100%; - font: inherit; - color: var(--text); -} -.search:focus-within { - outline: 2px solid var(--focus); } .channelListFrame { position: relative; @@ -73,28 +52,12 @@ flex-direction: column; gap: var(--space-half); } -.sidebar .unreadEdge { +.unreadEdge { position: absolute; left: 50%; transform: translateX(-50%); - display: flex; - align-items: center; - gap: var(--space-1); width: max-content; max-width: calc(100% - 8px); - padding: var(--space-1h) var(--space-2); - border: 1px solid transparent; - border-radius: var(--radius-pill); - background: var(--primary); - color: var(--on-primary); - box-shadow: var(--elevation-card); - font-size: var(--text-caption); - line-height: var(--text-caption--line-height); - letter-spacing: var(--text-caption--letter-spacing); - cursor: pointer; -} -.unreadEdge svg { - flex-shrink: 0; } .unreadEdge[data-edge="above"] { top: 4px; @@ -102,13 +65,6 @@ .unreadEdge[data-edge="below"] { bottom: 4px; } -.unreadEdge:hover { - border-color: var(--on-primary); -} -.unreadEdge:focus-visible { - outline: 2px solid var(--focus); - outline-offset: 2px; -} .channelSection { flex-shrink: 0; } @@ -117,96 +73,24 @@ } .channelSection > summary { padding: var(--space-1h) var(--space-2); - color: var(--text-muted); + color: var(--text-subtle); font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); font-weight: var(--type-weight-medium); cursor: pointer; } -.channelList button { - width: 100%; - display: flex; - align-items: center; - text-align: left; - gap: var(--space-2); - border: 0; - background: none; - padding: var(--space-1) var(--space-2); - min-height: 30px; - font-size: var(--text-label-sm); - line-height: var(--text-label-sm--line-height); - letter-spacing: var(--text-label-sm--letter-spacing); - border-radius: var(--radius-row); - flex-shrink: 0; -} -.channelList button svg { - flex-shrink: 0; - color: var(--text-muted); -} -.channelList button:hover { - background: var(--surface-hover); -} -.channelList button[aria-current="page"] { - background: var(--surface-accent); - color: var(--text); - font-weight: var(--type-weight-medium); -} .preferenceNotice { padding: var(--space-2); - color: var(--text-muted); - font-size: var(--text-caption); - line-height: var(--text-caption--line-height); - letter-spacing: var(--text-caption--letter-spacing); - border-top: 1px solid var(--border); -} -.preferenceNotice button { - display: block; - margin-top: var(--space-1); - border: 0; - background: none; - text-decoration: underline; - cursor: pointer; -} -.heading { - min-height: 58px; - padding: var(--space-4) var(--space-panel-inset); - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--space-2); - flex-shrink: 0; -} -.heading button:not(:where([data-buzz-ui], [data-buzz-ui] *)) { - display: inline-flex; - align-items: center; - gap: var(--space-1h); - border: 0; - background: var(--surface-control); - padding: var(--space-1h) var(--space-2); + color: var(--text-subtle); font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); -} -.heading - button[aria-pressed="true"]:not(:where([data-buzz-ui], [data-buzz-ui] *)) { - background: var(--selected); - color: var(--on-selected); -} -.channelTitle { - display: flex; - align-items: center; - gap: var(--space-2); - min-width: 0; -} -.channelTitle strong { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + border-top: 1px solid var(--border-standard); } .empty { padding: var(--space-8) var(--space-4); - color: var(--text-muted); + color: var(--text-subtle); font-size: var(--text-body-sm); line-height: var(--text-body-sm--line-height); letter-spacing: var(--text-body-sm--letter-spacing); @@ -238,8 +122,8 @@ .connectIcon { padding: var(--space-5); border-radius: var(--radius-panel); - color: var(--link); - background: var(--surface-control); + color: var(--text-standard); + background: var(--affordance-subtle); margin-bottom: var(--space-4); } .connect h1 { @@ -249,7 +133,7 @@ } .connect p { max-width: 550px; - color: var(--text-muted); + color: var(--text-subtle); } .connect button { margin: var(--space-4) 0; @@ -280,32 +164,15 @@ .sidebar { padding: var(--space-2) var(--space-1); } - .sidebar, - .conversation { - border-radius: var(--radius-card-compact); - } .withPanel .panelStack { width: 100%; } - .heading { - padding: var(--space-3); - } - .channelList button { - font-size: var(--text-caption); - line-height: var(--text-caption--line-height); - letter-spacing: var(--text-caption--letter-spacing); - } .roster { font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); } } -.channelList button span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} .diagnostics { position: relative; flex-shrink: 0; @@ -318,13 +185,13 @@ border-radius: var(--radius-row); cursor: pointer; list-style: none; - color: var(--text-muted); + color: var(--text-subtle); } .diagnostics > summary::-webkit-details-marker { display: none; } .diagnostics > summary:hover { - background: var(--surface-accent); + background: var(--affordance-selected); } .diagnosticsMenu { position: absolute; @@ -335,9 +202,9 @@ max-height: 60vh; overflow: auto; padding: var(--space-3); - border: 1px solid var(--border); + border: 1px solid var(--border-standard); border-radius: var(--radius-control); - background: var(--surface); + background: var(--surface-panel); box-shadow: var(--elevation-popover); font-size: var(--text-caption); line-height: var(--text-caption--line-height); @@ -357,9 +224,9 @@ font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); - background: var(--warning-surface); - color: var(--warning); - border-bottom: 1px solid var(--border); + background: var(--affordance-warning); + color: var(--text-warning); + border-bottom: 1px solid var(--border-standard); } .unreadBadge { @@ -371,11 +238,11 @@ line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); font-weight: var(--type-weight-medium); - background: var(--surface-hover); + background: var(--affordance-subtle-hover); text-align: center; } .unreadBadge[data-attention="true"] { - color: inherit; + color: var(--text-standard); outline: 1px solid currentColor; } @@ -389,6 +256,6 @@ min-height: 180px; max-height: 65%; flex-shrink: 0; - border-top: 1px solid var(--border); + border-top: 1px solid var(--border-standard); overflow: hidden; } diff --git a/src/bundled/channels/ChannelsPage.tsx b/src/bundled/channels/ChannelsPage.tsx index 850fceb5..90eb8472 100644 --- a/src/bundled/channels/ChannelsPage.tsx +++ b/src/bundled/channels/ChannelsPage.tsx @@ -1,3 +1,8 @@ +import { Panel } from "../../shared/design-system/ui/Panel"; +import { PanelHeader } from "../../shared/design-system/ui/PanelHeader"; +import { Button } from "../../shared/design-system/ui/Button"; +import { SearchField } from "../../shared/design-system/ui/SearchField"; +import { NavigationItem } from "../../shared/design-system/ui/NavigationItem"; import { useChannelPanels } from "./useChannelPanels"; import type { PageNavigation } from "../../features/navigation/service"; import type { Navigation } from "../../features/navigation/controller"; @@ -21,7 +26,6 @@ import { } from "react"; import { IconHash as Hash, - IconSearch as Search, IconDots as MoreHorizontal, IconPlugConnected as PlugZap, IconMessageCircle as MessageCircle, @@ -91,9 +95,9 @@ export function ChannelsPage({

{session.status === "error" && ( <> - +

For development, set BUZZ_DEV_VIEWER to your Buzz public key in .env.local, then restart{" "} @@ -478,200 +482,212 @@ function ChannelWorkspace({

- -
-
-
- {current?.channelType === "dm" ? ( -
- - {current ? ( - + + +
+
+ {current ? ( + + ) : ( +
Select a channel to read it.
+ )} + {current && ( + setSent({ channelId: current.id, id })} + /> + )} + {drawer.content} +
+ {(panel || showingThread || companion) && (
{showingThread && ( @@ -750,12 +766,12 @@ const ChannelBody = memo(function ChannelBody({ return (

{window.error}

- +
); if (window.status !== "ready" && !window.rows.length) diff --git a/src/bundled/channels/LiveStatus.tsx b/src/bundled/channels/LiveStatus.tsx index 444c8348..68bad4f0 100644 --- a/src/bundled/channels/LiveStatus.tsx +++ b/src/bundled/channels/LiveStatus.tsx @@ -1,3 +1,4 @@ +import { Button } from "../../shared/design-system/ui/Button"; import { useSyncExternalStore } from "react"; import type { RelaySession } from "../../features/relay/session"; import styles from "./Channels.module.css"; @@ -84,9 +85,9 @@ export function LiveStatus({ return (
{reason} Retained messages remain readable. - +
); } diff --git a/src/bundled/channels/OutboxStatus.tsx b/src/bundled/channels/OutboxStatus.tsx index a52b6003..aa6d9b17 100644 --- a/src/bundled/channels/OutboxStatus.tsx +++ b/src/bundled/channels/OutboxStatus.tsx @@ -1,3 +1,4 @@ +import { Button } from "../../shared/design-system/ui/Button"; import { isWorkflowOperation } from "../../features/workflows/protocol"; import { useState, useSyncExternalStore } from "react"; import type { RelayProfiler } from "../../features/relay/profiling"; @@ -46,15 +47,15 @@ export function OutboxStatus({ {" "} {!isWorkflowOperation(item.event) && (item.delivery === "failed" || item.delivery === "unknown") && ( - + )}{" "} {item.delivery !== "sending" && ( - + )} ))} diff --git a/src/bundled/channels/RelayTimings.tsx b/src/bundled/channels/RelayTimings.tsx index 2459269c..f33a4f74 100644 --- a/src/bundled/channels/RelayTimings.tsx +++ b/src/bundled/channels/RelayTimings.tsx @@ -1,3 +1,4 @@ +import { Button } from "../../shared/design-system/ui/Button"; import { useState } from "react"; import type { RelayProfiler } from "../../features/relay/profiling"; @@ -9,10 +10,10 @@ export function RelayTimings({ profiling }: { profiling: RelayProfiler }) { return (
Relay timings - - +
         {samples
           .slice(-40)
diff --git a/src/bundled/channels/SidebarUnread.tsx b/src/bundled/channels/SidebarUnread.tsx
index 1dcd8a27..5858ce57 100644
--- a/src/bundled/channels/SidebarUnread.tsx
+++ b/src/bundled/channels/SidebarUnread.tsx
@@ -1,3 +1,4 @@
+import { Button } from "../../shared/design-system/ui/Button";
 import {
   IconArrowDown as ArrowDown,
   IconArrowUp as ArrowUp,
@@ -118,17 +119,18 @@ export function SidebarUnread({
         if (!edges[edge].length) return null;
         const Icon = edge === "above" ? ArrowUp : ArrowDown;
         return (
-          
+          
+ +
); })}
diff --git a/src/bundled/channels/UnreadBadge.tsx b/src/bundled/channels/UnreadBadge.tsx index 79c0f276..df4bd195 100644 --- a/src/bundled/channels/UnreadBadge.tsx +++ b/src/bundled/channels/UnreadBadge.tsx @@ -1,3 +1,4 @@ +import { Button } from "../../shared/design-system/ui/Button"; import { useCallback, useMemo, useState, useSyncExternalStore } from "react"; import type { RelaySession } from "../../features/relay/session"; import styles from "./Channels.module.css"; @@ -65,17 +66,17 @@ export function UnreadOptions({ return ( <> {channelId && ( - + )} {channelId && sync.capability === "frontier-sync" && ( - + )} {error &&

{error}

}
@@ -107,13 +108,13 @@ export function UnreadOptions({ Read sync: {sync.capability} · {sync.status}

{sync.error &&

{sync.error}

} - + {sync.capability === "frontier-sync" && ( - + )}
diff --git a/src/bundled/emoji/Emoji.module.css b/src/bundled/emoji/Emoji.module.css index 541d82e4..f0451fee 100644 --- a/src/bundled/emoji/Emoji.module.css +++ b/src/bundled/emoji/Emoji.module.css @@ -16,33 +16,25 @@ --picker-inner-radius: calc(var(--picker-container-radius) - var(--space-2)); --picker-search-radius: var(--radius-row); --picker-scrollbar-space: var(--space-4); - --picker-search-background: var(--neutral-2); - --picker-search-foreground: var(--text-primary); - --picker-search-muted: var(--text-secondary); - --picker-search-ring: var(--text-primary); - --picker-tab-foreground: var(--text-primary); - --picker-tab-muted: var(--text-secondary); - --picker-tab-selected: var(--neutral-3); - --picker-scrollbar-thumb: var(--neutral-7); + --picker-search-background: var(--surface-inset); + --picker-search-foreground: var(--text-standard); + --picker-search-muted: var(--text-subtle); + --picker-search-ring: var(--text-standard); + --picker-tab-foreground: var(--text-standard); + --picker-tab-muted: var(--text-subtle); + --picker-tab-selected: var(--affordance-selected); + --picker-scrollbar-thumb: var(--border-prominent); position: absolute; bottom: 42px; left: 0; z-index: 5; max-width: 100%; overflow: hidden; - border: 1px solid var(--border); + border: 1px solid var(--border-standard); border-radius: var(--radius-card); - background: var(--surface); + background: var(--surface-panel); box-shadow: var(--elevation-card); } -.emojiPopover:not([data-has-tabs]) { - /* Search sits inside 8px of picker padding plus its 2px margin. */ - --picker-search-radius: max( - 0px, - calc(var(--picker-container-radius) - var(--space-2) - var(--space-half)) - ); - --picker-search-top-space: 0px; -} .sharedSearchIcon { position: absolute; top: 15px; @@ -60,109 +52,14 @@ left: auto; max-width: calc(100vw - 32px); } -.reactionTrigger { - display: inline-flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - padding: 0; - border: 1px solid var(--border); - border-radius: var(--radius-chip); - background: var(--surface-control); - color: var(--text-muted); - cursor: pointer; -} -.reactionTrigger:hover { - color: var(--text); -} -.reactionTrigger:focus-visible { - outline: 2px solid var(--text); - outline-offset: 2px; -} -.emojiPopover[data-has-tabs] .sharedSearchIcon { - top: 67px; -} @media (max-width: 900px) { .emojiPopover { --picker-container-radius: var(--radius-card-compact); border-radius: var(--radius-card-compact); } } -.pickerTabs { +.emojiMart { position: relative; - display: flex; - gap: var(--space-1); - padding: var(--space-2); - border-bottom: 1px solid var(--border); -} -.pickerTabs button { - position: relative; - z-index: 1; - display: flex; - width: auto; - height: 30px; - align-items: center; - justify-content: center; - flex: 1; - border: 0; - border-radius: var(--radius-row); - background: transparent; - font-size: var(--text-label-sm); - font-weight: var(--text-label-sm--font-weight); - line-height: var(--text-label-sm--line-height); -} -.pickerTabs button:first-of-type { - border-top-left-radius: var(--picker-inner-radius); -} -.pickerTabs button:last-of-type { - border-top-right-radius: var(--picker-inner-radius); -} -@media (hover: hover) and (pointer: fine) { - .pickerTabs .inactiveTab:hover { - color: var(--picker-tab-foreground); - } -} -.pickerTabs .inactiveTab { - color: var(--picker-tab-muted); -} -.pickerTabs .activeTab { - background: transparent; - color: var(--picker-tab-foreground); -} -.tabIndicator { - --tab-indicator-x: 0; - --tab-indicator-scale: 1; - position: absolute; - top: 8px; - bottom: 8px; - left: 8px; - width: calc((100% - 20px) / 2); - border-radius: var(--radius-row); - background: var(--picker-tab-selected); - pointer-events: none; - transform: translateX(var(--tab-indicator-x)) - scaleX(var(--tab-indicator-scale)); - transform-origin: left center; -} -.pickerTabs[data-active-tab="emoji"] .tabIndicator { - border-top-left-radius: var(--picker-inner-radius); -} -.pickerTabs[data-active-tab="gifs"] .tabIndicator { - --tab-indicator-x: calc(100% + 4px); - border-top-right-radius: var(--picker-inner-radius); -} -.pickerTabs[data-animate] .tabIndicator { - transition: transform 160ms cubic-bezier(0.77, 0, 0.175, 1); -} -.pickerTabs[data-active-tab="emoji"][data-press-target="gifs"] .tabIndicator { - --tab-indicator-scale: 1.08; - transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1); -} -.pickerTabs[data-active-tab="gifs"][data-press-target="emoji"] .tabIndicator { - --tab-indicator-x: calc(92% + 4px); - --tab-indicator-scale: 1.08; - transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1); } .emojiMart em-emoji-picker { width: 100%; @@ -173,19 +70,12 @@ .emojiStatus { padding: var(--space-2); border-radius: var(--radius-row); - background: var(--surface); + background: var(--surface-panel); box-shadow: var(--elevation-card); font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); } -.emojiStatus button { - width: auto; - height: auto; - padding: var(--space-1) var(--space-2); - border-radius: var(--radius-chip); - margin: var(--space-2) 0; -} .gifPicker { position: relative; display: flex; @@ -194,76 +84,7 @@ flex-direction: column; } .gifSearch { - position: relative; - display: flex; - height: 48px; - align-items: center; - margin: var(--space-1) var(--space-2); -} -.gifSearch input { - width: calc(100% - 4px); - height: 28px; - padding: 0 var(--space-8); - margin-inline: var(--space-half); - border: 0; - border-radius: var(--picker-search-radius); - background: var(--picker-search-background); - box-shadow: 0 0 0 1px var(--picker-search-background); - color: var(--picker-search-foreground); - font-family: var(--font-legacy-sans); - font-size: var(--text-body-sm); - line-height: var(--text-body-sm--line-height); - letter-spacing: var(--text-body-sm--letter-spacing); - appearance: none; - transition: - opacity 100ms ease, - background 100ms ease, - box-shadow 100ms ease; -} -.gifSearch input::-webkit-search-decoration, -.gifSearch input::-webkit-search-cancel-button, -.gifSearch input::-webkit-search-results-button, -.gifSearch input::-webkit-search-results-decoration { - appearance: none; -} -.gifSearch .gifClear { - position: absolute; - top: 50%; - right: 10px; - z-index: 1; - display: flex; - width: 16px; - height: 16px; - align-items: center; - justify-content: center; - padding: 0; - border: 0; - background: transparent; - color: var(--picker-search-muted); - transform: translateY(-50%); -} -.gifSearch .gifClear:hover { - background: transparent; -} -.gifSearch .gifClear svg { - width: 16px; - height: 16px; -} -.gifSearch input::placeholder { - color: var(--picker-search-muted); -} -.gifSearch input:focus { - outline: none; - box-shadow: 0 0 0 2px var(--picker-search-ring); -} -@media (prefers-reduced-motion: reduce) { - .pickerTabs[data-animate] .tabIndicator { - transition-duration: 0ms; - } - .pickerTabs[data-press-target] .tabIndicator { - --tab-indicator-scale: 1; - transition-duration: 0ms; - } + margin: var(--space-2); } .gifResults { min-height: 0; @@ -306,7 +127,7 @@ break-inside: avoid; border: 0; border-radius: var(--radius-row); - background: var(--surface-hover); + background: var(--affordance-subtle-hover); } .gifGrid button:hover { filter: brightness(1.08); @@ -324,7 +145,7 @@ .gifLoading span { height: 82px; border-radius: var(--radius-row); - background: var(--surface-hover); + background: var(--affordance-subtle-hover); } .gifLoading span:nth-child(4n + 1), .gifLoading span:nth-child(4n) { @@ -338,18 +159,13 @@ align-items: center; justify-content: center; padding: var(--space-5); - color: var(--text-muted); + color: var(--text-subtle); text-align: center; } -.gifEmpty button { - width: auto; - height: auto; - padding: var(--space-1) var(--space-2); -} .gifAttribution { padding: var(--space-1h) var(--space-2); - border-top: 1px solid var(--border); - color: var(--text-muted); + border-top: 1px solid var(--border-standard); + color: var(--text-subtle); font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); diff --git a/src/bundled/emoji/EmojiPicker.tsx b/src/bundled/emoji/EmojiPicker.tsx index 80f4f610..852885c1 100644 --- a/src/bundled/emoji/EmojiPicker.tsx +++ b/src/bundled/emoji/EmojiPicker.tsx @@ -1,3 +1,6 @@ +import { Tabs } from "../../shared/design-system/ui/Tabs"; +import { Button } from "../../shared/design-system/ui/Button"; +import { IconButton } from "../../shared/design-system/ui/IconButton"; import { useEffect, useId, @@ -43,8 +46,6 @@ export function EmojiPicker({ }) { const [open, setOpen] = useState(false); const [tab, setTab] = useState<"emoji" | "gifs">("emoji"); - const [animateTab, setAnimateTab] = useState(false); - const [pressedTab, setPressedTab] = useState<"emoji" | "gifs">(); const [gifAvailability, setGifAvailability] = useState<{ community: string; supported: boolean | undefined; @@ -110,8 +111,6 @@ export function EmojiPicker({ if (!controller.signal.aborted) { setGifAvailability({ community, supported }); if (!supported) { - setAnimateTab(false); - setPressedTab(undefined); setTab("emoji"); } } @@ -120,8 +119,7 @@ export function EmojiPicker({ if (!controller.signal.aborted) { setGifAvailability({ community, supported: undefined }); setGifDiscoveryRequested(false); - setAnimateTab(false); - setPressedTab(undefined); + setTab("emoji"); } }, @@ -183,132 +181,78 @@ export function EmojiPicker({ ?.value ?? search.current; dispose?.(); }; - }, [ - open, - disabled, - session, - scope, - catalog, - attempt, - perLine, - tab, - animateTab, - host, - ]); + }, [open, disabled, session, scope, catalog, attempt, perLine, tab, host]); + const emojiContent = ( +
+
{!gifs && !error ? (

{error}

- +
) : gifs?.length ? (
diff --git a/src/bundled/emoji/emoji-mart.ts b/src/bundled/emoji/emoji-mart.ts index 2fc4499f..f5e45e74 100644 --- a/src/bundled/emoji/emoji-mart.ts +++ b/src/bundled/emoji/emoji-mart.ts @@ -156,15 +156,21 @@ export function mountEmojiMart({ // The documented web-component attribute updates in place: do not recreate the // picker/dictionary (or lose search, focus and scroll) just to change appearance. const documentRoot = host.ownerDocument.documentElement; - const themeObserver = new MutationObserver(() => { + const syncAppearance = () => { picker.setAttribute( "theme", parseColorMode(documentRoot.dataset.colorMode), ); - }); + picker.toggleAttribute( + "data-keyboard-navigation", + documentRoot.hasAttribute("data-keyboard-navigation"), + ); + }; + syncAppearance(); + const themeObserver = new MutationObserver(syncAppearance); themeObserver.observe(documentRoot, { attributes: true, - attributeFilter: ["data-color-mode"], + attributeFilter: ["data-color-mode", "data-keyboard-navigation"], }); // Like the legacy picker, own search focus/corrections after async shadow render. const root = picker.shadowRoot; @@ -175,24 +181,24 @@ export function mountEmojiMart({ --font-size: var(--text-body-sm); } #root, input, button { - color: var(--text-primary); + color: var(--text-standard); font-family: var(--font-sans); font-size: var(--text-body-sm); line-height: var(--text-body-sm--line-height); } #root { - --color-a: var(--text-primary); - --color-b: var(--text-secondary); - --color-c: var(--text-tertiary); - --em-color-border: var(--border-primary); - --em-color-border-over: var(--neutral-3); - --buzz-category-fill: var(--neutral-3); - --buzz-category-icon: var(--text-secondary); - --buzz-category-icon-selected: var(--text-primary); - --buzz-category-label: var(--text-secondary); - --buzz-scrollbar-thumb: var(--neutral-7); - background: var(--bg-panel); - color: var(--text-primary); + --color-a: var(--text-standard); + --color-b: var(--text-subtle); + --color-c: var(--text-metadata); + --em-color-border: var(--border-standard); + --em-color-border-over: var(--affordance-selected); + --buzz-category-fill: var(--affordance-selected); + --buzz-category-icon: var(--text-subtle); + --buzz-category-icon-selected: var(--text-standard); + --buzz-category-label: var(--text-subtle); + --buzz-scrollbar-thumb: var(--border-prominent); + background: var(--surface-panel); + color: var(--text-standard); font-family: var(--font-sans); } #root { @@ -228,7 +234,7 @@ export function mountEmojiMart({ width: 100% !important; } .category .sticky { - background: var(--bg-panel); + background: var(--surface-panel); color: var(--buzz-category-label); font-size: var(--text-caption); font-weight: var(--type-weight-normal); @@ -254,6 +260,11 @@ export function mountEmojiMart({ box-shadow 100ms ease; } .search input[type="search"]:focus { + background: var(--picker-search-background); + box-shadow: 0 0 0 1px var(--picker-search-background); + outline: none; + } + :host([data-keyboard-navigation]) .search input[type="search"]:focus-visible { background: var(--picker-search-background); box-shadow: 0 0 0 2px var(--picker-search-ring); outline: none; @@ -370,8 +381,8 @@ export function mountEmojiMart({ display: none; } #root > .menu { - background: var(--bg-float); - border-color: var(--border-primary); + background: var(--surface-popover); + border-color: var(--border-standard); border-radius: var(--radius-row); padding: var(--space-1); box-shadow: var(--shadow-sm); @@ -388,11 +399,11 @@ export function mountEmojiMart({ padding: var(--space-1) var(--space-1h); } .menu .option:hover { - background: var(--neutral-3); - color: var(--text-primary); + background: var(--affordance-selected); + color: var(--text-standard); } .menu input[type="radio"]:checked + .option { - box-shadow: 0 0 0 2px var(--text-primary); + box-shadow: 0 0 0 2px var(--text-standard); } @media (prefers-reduced-motion: reduce) { #nav button::before { diff --git a/src/bundled/github/GitHub.module.css b/src/bundled/github/GitHub.module.css index fc63913d..a56921c6 100644 --- a/src/bundled/github/GitHub.module.css +++ b/src/bundled/github/GitHub.module.css @@ -5,7 +5,7 @@ font-size: var(--text-body-sm); line-height: var(--text-body-sm--line-height); letter-spacing: var(--text-body-sm--letter-spacing); - color: var(--text); + color: var(--text-standard); } .identity, .byline { @@ -21,7 +21,7 @@ .identity small, .byline, .note { - color: var(--text-muted); + color: var(--text-subtle); font-size: var(--text-caption); line-height: var(--text-caption--line-height); letter-spacing: var(--text-caption--letter-spacing); @@ -33,8 +33,8 @@ width: 40px; height: 40px; border-radius: var(--radius-control); - background: var(--surface-accent); - color: var(--accent); + background: var(--affordance-selected); + color: var(--text-standard); } .external { display: flex; @@ -43,8 +43,8 @@ margin: var(--space-4) 0; padding: var(--space-2) var(--space-3); border-radius: var(--radius-row); - background: var(--surface-accent); - color: var(--accent); + background: var(--affordance-selected); + color: var(--text-standard); text-decoration: none; } .external:hover { @@ -60,14 +60,14 @@ .state { border-radius: var(--radius-chip); padding: var(--space-half) var(--space-1h); - background: var(--surface-accent); + background: var(--affordance-selected); text-transform: capitalize; } .facts { padding: var(--space-3); - border: 1px solid var(--border); + border: 1px solid var(--border-standard); border-radius: var(--radius-control); - background: var(--surface); + background: var(--surface-panel); } .facts > div { display: flex; @@ -76,7 +76,7 @@ padding: var(--space-1) 0; } .facts dt { - color: var(--text-muted); + color: var(--text-subtle); flex-shrink: 0; } .facts dd { @@ -90,5 +90,5 @@ .note { margin-top: var(--space-6); padding-top: var(--space-3); - border-top: 1px solid var(--border); + border-top: 1px solid var(--border-standard); } diff --git a/src/bundled/github/index.tsx b/src/bundled/github/index.tsx index c9717bfa..93e38096 100644 --- a/src/bundled/github/index.tsx +++ b/src/bundled/github/index.tsx @@ -1,3 +1,4 @@ +import { Button } from "../../shared/design-system/ui/Button"; import { useEffect, useMemo, useState } from "react"; import { IconExternalLink as ExternalLink, @@ -99,9 +100,9 @@ function ObjectPanel({ ) : typeof result === "string" ? (

{result}

- +
) : ( <> diff --git a/src/bundled/link-lab/LinkLab.module.css b/src/bundled/link-lab/LinkLab.module.css index 0c1daa66..345c384c 100644 --- a/src/bundled/link-lab/LinkLab.module.css +++ b/src/bundled/link-lab/LinkLab.module.css @@ -3,7 +3,7 @@ margin: 0 auto; padding: calc(var(--space-6) * 2) var(--space-6); font-family: var(--font-sans); - color: var(--text-primary); + color: var(--text-standard); } .page h1 { @@ -12,7 +12,7 @@ .intro, .status { - color: var(--text-secondary); + color: var(--text-subtle); } .samples { @@ -24,12 +24,13 @@ .samples p { margin: var(--space-2) 0 0; font-size: var(--text-body-lg); - line-height: 1.8; + line-height: var(--text-body-lg--line-height); + letter-spacing: var(--text-body-lg--letter-spacing); } .label { font-size: var(--text-body-sm); - color: var(--text-secondary); + color: var(--text-subtle); } .services { @@ -38,14 +39,3 @@ gap: var(--space-3) calc(var(--space-4) + var(--space-half)); margin-top: var(--space-3); } - -.page input { - width: 100%; - margin: calc(var(--space-2) + var(--space-half)) 0; - padding: calc(var(--space-2) + var(--space-half)) var(--space-3); - border: 1px solid var(--border-primary); - border-radius: var(--radius-chip); - background: var(--bg-panel); - color: inherit; - font: inherit; -} diff --git a/src/bundled/link-lab/index.tsx b/src/bundled/link-lab/index.tsx index 6ed7b3d5..ba2080da 100644 --- a/src/bundled/link-lab/index.tsx +++ b/src/bundled/link-lab/index.tsx @@ -1,3 +1,4 @@ +import { Input } from "../../shared/design-system/ui/Input"; import { useState } from "react"; import type { PluginModule } from "../../plugins/api"; import { targetLink } from "../../features/navigation/targets"; @@ -144,7 +145,7 @@ function LinkLab() { - setUrl(event.target.value)} diff --git a/src/bundled/mentions/MentionPicker.tsx b/src/bundled/mentions/MentionPicker.tsx index 58e84dfb..e5ad8f1f 100644 --- a/src/bundled/mentions/MentionPicker.tsx +++ b/src/bundled/mentions/MentionPicker.tsx @@ -1,3 +1,7 @@ +import { NavigationItem } from "../../shared/design-system/ui/NavigationItem"; +import { SearchField } from "../../shared/design-system/ui/SearchField"; +import { Button } from "../../shared/design-system/ui/Button"; +import { IconButton } from "../../shared/design-system/ui/IconButton"; import { Avatar } from "../../shared/design-system/ui/Avatar"; import { IconAt as AtSign } from "@tabler/icons-react"; import { @@ -78,7 +82,9 @@ export function MentionPicker({ } }} > - + icon={