Skip to content

refactor: drop the vendored chat and the copied brand tokens - #25

Merged
lemarier merged 4 commits into
mainfrom
david/base-ui
Sep 19, 2026
Merged

lemarier merged 4 commits into
mainfrom
david/base-ui

Conversation

@lemarier

@lemarier lemarier commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Change

Replaces the vendored AI Elements chat components with our own and removes Radix from the site.

The Buddy chat imported 22 symbols from 2,187 lines of vendored code. The transcript, the message and the suggestion chip are now ours at a fraction of the size, carrying the same markup; the attachment grid is ours too, without the variant system and media-type table that Buddy never used, since it accepts images and lays them out one way.

The prompt input was the load-bearing piece: 1,307 lines exporting a model picker, a command palette and an action menu that nothing rendered, and those exports were the only reason the tooltip, hover card, select, dropdown menu, command and dialog components existed. Reduced to the eleven symbols the chat imports, all six became unreferenced and are deleted, along with radix-ui, cmdk and streamdown. The file-handling core — accept, size and count validation, drag, drop and paste — is kept rather than rewritten, because that is the part with real behaviour behind it and rewriting it would have risked the most for the least.

What is left needs no primitive library at all: the composer is a form, a textarea and buttons, and Button no longer composes into another element so its slot is gone with the rest of Radix. Base UI stays the choice for when something does need a primitive, which is what /ops in the data repo uses; adding the dependency here while nothing imports it would only be decoration.

Net: 600 insertions against 5,222 deletions.

Validation

just check passes (exit 0): lint, tests, type check, site build and Storybook build.

node .storybook/verify.mjs reports 55 stories rendering with no page errors, and its six screenshots match the same run against main.

The screenshots are not a pixel-exact oracle here, which is worth knowing independently of this change. Two runs of the same build differ: the Buddy stories vary by tens to a few hundred pixels, in varying places, most often at the avatar and in the map area. A run either matches exactly or differs by one of a few recurring amounts. Comparisons were therefore repeated, and were also checked with the noisiest region masked; the change reads as identical whenever the noise settles, and the noise is present on main alone.

Two accessibility fixes came out of review and are not covered by those screenshots: a crawl of all 55 stories finds zero attachment tiles and zero scroll buttons rendered, because both need application state the stories do not reach. They are verified instead by the compiled CSS — .focus-visible\:opacity-100:focus-visible{opacity:1} and @media (pointer:coarse){…{opacity:1}} are both emitted — and the same stylesheet is what shows the original defect: Tailwind wraps group-hover in @media (hover:hover), so on a touch device the reveal rule never applied and the only per-photo remove control sat at opacity: 0 permanently.

A census of rendered data-slot attributes across every story was used to establish what actually ships before deleting anything: only button and input-group appear. Triggers render whether or not an overlay is open, so the absence of tooltip-trigger, select-trigger and the rest is evidence those components were unreachable, with the caveat that stories do not cover every application state.

Not verified: overlay components in an opened state, because none survive. No Changeset — the site is not a published package.

Also: colour and the plate come from the package

styles/theme.css held its own copy of both — eighteen hand-maintained colour values and the whole Plate 89 block, alongside a dated palette snapshot in assets/brand/brand-tokens.css. The values were identical to the package's, checked token by token, so the site keeps its --o89-* names and resolves them from --color-* rather than restating them. The 796 call sites are untouched.

The fork was deliberate: the brand sheet follows a light operating-system preference and this site is dark only. data-theme="dark" on the document pins it, which is what the data site already does. Storybook renders in its own document, so its preview sets the same attribute — without that every story would read on paper.

Adopting the package brings three differences the local copy had drifted away from: the action plate's rim is rgba(255,255,255,0.85) rather than 0.9, and two transitions are 0.22s and 0.24s rather than 0.25s and 0.3s. The rim is the only visible one, and it is 322 pixels on a 1px edge of one button.

Verified: all six screenshots byte-identical, all 22 --o89-* tokens resolve with none empty, data-theme reads dark in every story, and a crawl confirms 39 plates, 15 action plates, 34 wraps and 15 text links actually render across the stories — the six screenshotted views contain none of them, so the plate was checked by rebuilding the previous state and diffing a story that does. That diff is the 322 pixels above and nothing else.

These were the vendored AI Elements versions, which export fifteen, eight
and two symbols where Buddy renders two, three and one. Ours carry the
same markup and drop the rest, which is what let the scroll area, the
button group and the separator go with them.

The suggestion strip's ScrollArea container was never rendered: both
callers lay the strip out themselves.
The prompt input was 1307 lines exporting a model picker, a command
palette and an action menu that Buddy never rendered; it kept the tooltip,
hover card, select, dropdown menu, command and dialog components alive
between them. Reduced to the eleven symbols the chat imports, all six fall
out, and with them radix-ui, cmdk and streamdown.

Attachments are ours now as well. Buddy accepts images and lays them out
as a grid, so the variant system and the media-type table are gone.

Nothing left needs a primitive library: the composer is a form, a textarea
and buttons. Base UI remains the choice when something does need one.
Copilot AI lite review requested due to automatic review settings September 18, 2026 21:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: 72d40f54-8809-45bd-85a4-f599a168a258

📥 Commits

Reviewing files that changed from the base of the PR and between 8a5d6a9 and 7308561.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/website/.storybook/preview-head.html
  • apps/website/package.json
  • apps/website/src/react/index.html
  • apps/website/src/react/styles/theme.css
  • pnpm-workspace.yaml

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change replaces legacy AI element modules with chat components for attachments, conversations, messages, and suggestions. Buddy imports now use the chat paths. Prompt input APIs and tooltip behavior were reduced. Unused Radix-based UI modules and dependencies were removed. Button now always renders a native button. The website and Storybook now use the dark theme and shared brand tokens.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 8 files. (5 skipped: 5… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the two primary changes: replacing vendored chat components and removing copied brand tokens.
Description check ✅ Passed The description directly explains the chat refactor, dependency removal, theme migration, accessibility fixes, and validation results.
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 8 files. (5 skipped: 5 unsupported.)

  • Fix all pre-merge checks with AI

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
apps/website/src/react/components/chat/attachments.tsx-98-98 (1)

98-98: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the remove button visible without hover.

AttachmentRemove is the only visible per-file removal control in BuddyWorkspace. opacity-0 hides it until group-hover:opacity-100; the shared Button focus ring cannot make the fully transparent button visible. Touch users cannot rely on hover, and sighted keyboard users cannot see the control when it receives focus. The Backspace shortcut only removes the last attachment and is not a visible substitute.

-        "absolute top-2 right-2 size-6 rounded-full bg-background/80 p-0 opacity-0 backdrop-blur-sm transition-opacity group-hover:opacity-100 hover:bg-background [&>svg]:size-3",
+        "absolute top-2 right-2 size-6 rounded-full bg-background/80 p-0 backdrop-blur-sm hover:bg-background [&>svg]:size-3",
apps/website/src/react/components/chat/conversation.tsx-38-49 (1)

38-49: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an accessible name to the scroll button.

When ConversationScrollButton is rendered without aria-label, its icon-only native button has no accessible name. BuddyPoc.tsx uses this default form, and ArrowDownIcon is hidden from assistive technology by default. Add a default label before {...props} so callers can override it.

Proposed fix
     <Button
+      aria-label="Scroll to latest message"
       className={cn(

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: c7e5afe6-ee47-401d-81eb-8cb2f9d49c1c

📥 Commits

Reviewing files that changed from the base of the PR and between e7a9a46 and fba84aa.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • apps/website/package.json
  • apps/website/src/react/components/ai-elements/attachments.tsx
  • apps/website/src/react/components/ai-elements/conversation.tsx
  • apps/website/src/react/components/ai-elements/message.tsx
  • apps/website/src/react/components/ai-elements/suggestion.tsx
  • apps/website/src/react/components/buddy/BuddyPoc.tsx
  • apps/website/src/react/components/buddy/BuddyWorkspace.tsx
  • apps/website/src/react/components/chat/attachments.tsx
  • apps/website/src/react/components/chat/conversation.tsx
  • apps/website/src/react/components/chat/message.tsx
  • apps/website/src/react/components/chat/prompt-input.tsx
  • apps/website/src/react/components/chat/suggestion.tsx
  • apps/website/src/react/components/ui/button-group.tsx
  • apps/website/src/react/components/ui/button.tsx
  • apps/website/src/react/components/ui/command.tsx
  • apps/website/src/react/components/ui/dialog.tsx
  • apps/website/src/react/components/ui/dropdown-menu.tsx
  • apps/website/src/react/components/ui/hover-card.tsx
  • apps/website/src/react/components/ui/scroll-area.tsx
  • apps/website/src/react/components/ui/select.tsx
  • apps/website/src/react/components/ui/separator.tsx
  • apps/website/src/react/components/ui/tooltip.tsx
💤 Files with no reviewable changes (14)
  • apps/website/package.json
  • apps/website/src/react/components/ui/command.tsx
  • apps/website/src/react/components/ui/separator.tsx
  • apps/website/src/react/components/ai-elements/suggestion.tsx
  • apps/website/src/react/components/ui/select.tsx
  • apps/website/src/react/components/ui/button-group.tsx
  • apps/website/src/react/components/ui/hover-card.tsx
  • apps/website/src/react/components/ai-elements/conversation.tsx
  • apps/website/src/react/components/ui/scroll-area.tsx
  • apps/website/src/react/components/ai-elements/message.tsx
  • apps/website/src/react/components/ai-elements/attachments.tsx
  • apps/website/src/react/components/ui/tooltip.tsx
  • apps/website/src/react/components/ui/dropdown-menu.tsx
  • apps/website/src/react/components/ui/dialog.tsx

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Tailwind wraps group-hover in `@media (hover:hover)`, so on a phone the
rule never applies and the button stayed at opacity 0 with no other way
to remove a specific photo: Backspace only drops the last one. An
opacity-0 button also takes its focus ring down with it, so a keyboard
could reach the control but not see it.

The scroll button had no accessible name where BuddyPoc renders it
without one, and its icon is aria-hidden.
theme.css held its own copy of both: eighteen hand-maintained colour
values and the whole Plate 89 block. The values were identical to the
package's, so the site keeps its --o89-* names and resolves them from
--color-* instead of restating them, and the 796 call sites are untouched.

The fork existed because the brand sheet follows a light operating-system
preference and the website is dark only. data-theme="dark" on the document
pins it, which is what the data site does; Storybook renders in its own
document, so its preview sets the same attribute.

The plate arrives slightly different from the copy that drifted: the action
rim is the package's 0.85 rather than 0.9, and two transitions are 0.22s and
0.24s rather than 0.25s and 0.3s.
@lemarier lemarier changed the title refactor: drop Radix and the vendored chat components refactor: drop the vendored chat and the copied brand tokens Sep 19, 2026
@lemarier
lemarier merged commit ee95ff8 into main Sep 19, 2026
3 checks passed
@lemarier
lemarier deleted the david/base-ui branch September 19, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants