Skip to content

Shadcn components migration#198

Open
aidenybai wants to merge 7 commits intomainfrom
cursor/shadcn-components-migration-0076
Open

Shadcn components migration#198
aidenybai wants to merge 7 commits intomainfrom
cursor/shadcn-components-migration-0076

Conversation

@aidenybai
Copy link
Owner

@aidenybai aidenybai commented Feb 26, 2026

Migrate all website components to use shadcn UI primitives to standardize UI, improve accessibility, and add comprehensive hover/focus state regression tests.


Open in Web Open in Cursor 


Summary by cubic

Migrated the website to shadcn UI primitives for consistent styling and better accessibility. Added Playwright tests to guard hover/focus states and fixed spacing issues found during review.

  • New Features

    • Added shadcn primitives: Button, Badge, Card, DropdownMenu, HoverCard, Input, ScrollArea, Separator, Table, Tabs, Collapsible.
    • Introduced Playwright e2e suite and helpers for hover/focus regression on blog, homepage, benchmarks, and open-file pages.
  • Refactors

    • Migrated pages and components to shadcn (blog, changelog, privacy, not-found, open-file, benchmarks, code blocks).
    • Replaced custom Scrollable/Collapsible with Radix versions; standardized focus-visible rings and link-as-button (asChild).
    • Addressed spacing regressions and minor styling fixes across buttons, links, tables, and tooltips based on review.
    • Updated config and tooling: utils alias to "@/utils/cn", added type defs, new scripts (typecheck, test:e2e), and installed Radix and Playwright dependencies.

Written for commit bfa87f9. Summary will update on new commits.


Note

Medium Risk
Moderate risk because it broadly replaces interactive elements (buttons, dropdowns, tabs, scroll areas, collapsibles, tables) with new Radix-based primitives across multiple pages, which can subtly affect behavior and accessibility. Added e2e coverage reduces regression risk but UI/keyboard interactions still need review across browsers.

Overview
Migrates the website’s interactive UI from bespoke elements/styles to shadcn-style primitives (new Button, Badge, Card, Input, Table, Tabs, Separator) and Radix wrappers (DropdownMenu, HoverCard, ScrollArea, Collapsible), updating multiple pages/components to use asChild links and consistent focus-visible ring styling.

Refactors key interactions: open-file editor selector is now a Radix dropdown, BenchmarkTooltip uses a Radix hover card with delayed open, benchmark tables and install tabs render via shared UI primitives, and Scrollable now delegates to Radix scroll area instead of custom scrollbar logic.

Adds Playwright e2e tests focused on hover/focus state regressions across homepage/blog/benchmarks/open-file, plus new typecheck and test:e2e scripts, Radix/Playwright dependencies, and config updates (including components.json utils alias and new TS module declarations).

Written by Cursor Bugbot for commit bfa87f9. This will update automatically on new commits. Configure here.

cursoragent and others added 6 commits February 26, 2026 13:36
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
@cursor
Copy link

cursor bot commented Feb 26, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@vercel
Copy link
Contributor

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-grab-website Ready Ready Preview, Comment Feb 27, 2026 6:40am

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 26, 2026

Open in StackBlitz

@react-grab/cli

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/cli@198

grab

npm i https://pkg.pr.new/aidenybai/react-grab/grab@198

@react-grab/ami

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/ami@198

@react-grab/amp

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/amp@198

@react-grab/claude-code

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/claude-code@198

@react-grab/codex

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/codex@198

@react-grab/copilot

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/copilot@198

@react-grab/cursor

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/cursor@198

@react-grab/droid

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/droid@198

@react-grab/gemini

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/gemini@198

@react-grab/opencode

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/opencode@198

react-grab

npm i https://pkg.pr.new/aidenybai/react-grab@198

@react-grab/relay

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/relay@198

@react-grab/utils

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/utils@198

commit: 99c65c9

@aidenybai aidenybai marked this pull request as ready for review February 27, 2026 06:12
@pullfrog
Copy link
Contributor

pullfrog bot commented Feb 27, 2026

This run croaked 😵

The workflow encountered an error before any progress could be reported. Please check the workflow run logs for details.

Pullfrog  | View workflow run | Triggered by Pullfrogpullfrog.com𝕏

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

10 issues found across 40 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/website/components/ui/scrollable.tsx">

<violation number="1" location="packages/website/components/ui/scrollable.tsx:20">
P1: Using `maxHeight` without an explicit height on `ScrollArea` can clip overflow content instead of enabling scrolling.</violation>
</file>

<file name="packages/website/components/ui/input.tsx">

<violation number="1" location="packages/website/components/ui/input.tsx:13">
P2: Input text size is below the documented mobile minimum (16px), which can cause iOS zoom-on-focus and poorer readability.</violation>
</file>

<file name="packages/website/e2e/blog-interactions.spec.ts">

<violation number="1" location="packages/website/e2e/blog-interactions.spec.ts:19">
P2: The hover color assertion is timing-sensitive and can be flaky because it checks computed color immediately after triggering a transitioned hover state.</violation>
</file>

<file name="packages/website/components/ui/card.tsx">

<violation number="1" location="packages/website/components/ui/card.tsx:1">
P2: Avoid marking this base Card primitive as a Client Component; the unnecessary `"use client"` boundary expands client-side bundling for all consumers.</violation>
</file>

<file name="packages/website/components/blocks/read-tool-call-block.tsx">

<violation number="1" location="packages/website/components/blocks/read-tool-call-block.tsx:47">
P2: Long parameter text will not wrap after migrating to shadcn `Button` because the base component enforces `whitespace-nowrap`.</violation>
</file>

<file name="packages/website/e2e/open-file-interactions.spec.ts">

<violation number="1" location="packages/website/e2e/open-file-interactions.spec.ts:57">
P2: This test claims keyboard selectability but uses mouse click for activation, so keyboard-only selection regressions can slip through.</violation>
</file>

<file name="packages/website/components/ui/button.tsx">

<violation number="1" location="packages/website/components/ui/button.tsx:52">
P1: The Button does not set a default `type`, so it implicitly becomes `submit` inside forms and can trigger unintended submissions.</violation>
</file>

<file name="packages/website/components/ui/table.tsx">

<violation number="1" location="packages/website/components/ui/table.tsx:1">
P2: Remove the unnecessary `"use client"` directive; this table primitive is server-safe and should not be forced into the client bundle.</violation>
</file>

<file name="packages/website/components/install-tabs.tsx">

<violation number="1" location="packages/website/components/install-tabs.tsx:387">
P2: The `Tabs` primitive applies `gap-4` (16px) by default via its base class `flex flex-col gap-4`. This className is not overridden here, so a visible 16px whitespace gap now appears between the `TabsList` header and the code content area. The original code used a plain `div` with no gap, keeping the tab bar flush against the content. Add `gap-0` to the className to preserve the original flush layout.</violation>
</file>

<file name="packages/website/components/benchmark-tooltip.tsx">

<violation number="1" location="packages/website/components/benchmark-tooltip.tsx:218">
P2: `HoverCardContent` is unmounted by Radix when the hover card closes, which means the `AnimatePresence` exit animation inside it never runs. The original implementation kept the tooltip DOM-mounted during exit, allowing the smooth fade-out and scale-down transition. Add `forceMount` to `HoverCardContent` (and conditionally render/hide it) so that `AnimatePresence` can orchestrate the exit animation before the element is removed from the DOM.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

style={{ maxHeight }}
>
<div className="relative">
<ScrollArea className={cn("pr-2", className)} style={{ maxHeight }}>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P1: Using maxHeight without an explicit height on ScrollArea can clip overflow content instead of enabling scrolling.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/website/components/ui/scrollable.tsx, line 20:

<comment>Using `maxHeight` without an explicit height on `ScrollArea` can clip overflow content instead of enabling scrolling.</comment>

<file context>
@@ -13,104 +15,12 @@ export const Scrollable = ({
-        style={{ maxHeight }}
-      >
+    <div className="relative">
+      <ScrollArea className={cn("pr-2", className)} style={{ maxHeight }}>
         {children}
-      </div>
</file context>
Fix with Cubic

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

</motion.div>
)}
</AnimatePresence>
</HoverCardContent>
Copy link

Choose a reason for hiding this comment

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

HoverCard forceMount renders always-visible empty tooltip container

High Severity

HoverCardContent with forceMount keeps the Radix Popper-positioned container permanently mounted and visible in the DOM, even when the hover card is closed. The container has border, background, and shadow styles (from the base HoverCardContent classes) but no data-[state=closed]:hidden CSS to hide it when closed. The inner AnimatePresence only conditionally renders the chart content — the outer wrapper remains visible as an empty floating box near the "3× faster" link on the blog page. The old implementation conditionally rendered the entire tooltip only when hovered.

Additional Locations (1)

Fix in Cursor Fix in Web

<thead>
<tr className="border-b border-[#2a2a2a]">
<th className="text-left py-2 px-4 text-xs font-medium text-neutral-500 uppercase tracking-wider">
<Table className="max-w-2xl border-collapse text-sm">
Copy link

Choose a reason for hiding this comment

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

Table wrapper div breaks flex centering of summary table

Medium Severity

The Table component wraps <table> in <div className="relative w-full overflow-x-auto">. When placed inside the parent <div className="overflow-x-auto flex justify-center">, this wrapper's w-full makes it fill the entire flex container, so justify-center no longer has any visible effect. The old code had the <table> as a direct flex child with max-w-2xl, which allowed it to be centered. The summary metrics table now renders left-aligned instead of centered.

Additional Locations (1)

Fix in Cursor Fix in Web

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