Skip to content

Conversation

@ArjunCodess
Copy link
Contributor

@ArjunCodess ArjunCodess commented Sep 1, 2025

fixes #1891

Description

Updated shadcn/ui components to the latest version with modern import patterns and fixed several UI issues. Changed from legacy import { Component as ComponentPrimitive } from 'radix-ui' to import * as ComponentPrimitive from "@radix-ui/react-component" across all UI components. Fixed the create email component backdrop blur functionality and added tooltip support for keyboard shortcuts. Preserved custom modifications in complex components like the calendar while updating the core component structure.


Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 🎨 UI/UX improvement
  • ⚡ Performance improvement

Areas Affected

  • User Interface/Experience
  • Development Workflow

Testing Done

  • Manual testing performed
  • Cross-browser testing (if UI changes)
  • Mobile responsiveness verified (if UI changes)

Security Considerations

  • No sensitive data is exposed

Checklist

  • I have read the CONTRIBUTING document
  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • My changes generate no new warnings
  • All tests pass locally

Additional Notes

This migration addresses the outdated shadcn components throughout the codebase. Key improvements include fixing the dialogue backdrop blur issue in the create email component and adding proper tooltip functionality for keyboard shortcuts. Components with extensive customisations (like the calendar) were carefully preserved while updating the underlying primitives. The new import pattern offers improved tree-shaking and enhanced TypeScript support.

Screenshots/Recordings

  • Create email component now has proper backdrop blur functionality
image
  • Tooltips display correctly for keyboard shortcuts
image
  • All UI components maintain existing functionality

By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.


Summary by cubic

Migrated all shadcn/ui components to the latest version with modern Radix imports for better a11y, focus states, and consistency. Removed the legacy radix-ui package and fixed overlay/backdrop issues (e.g., Dialog, Drawer).

  • Refactors

    • Replaced all 'radix-ui' imports with @radix-ui/react-* and updated shadcn patterns.
    • Unified focus rings, transitions, and sizes across components (Accordion, Command, Dropdown, Select, etc.).
    • Dialog/Drawer/Sheet use consistent overlays (black/80), add close buttons, and smoother animations; resolves dialog backdrop blur.
    • Simplified Button (removed isLoading/loadingText), adjusted sizes, and switched to @radix-ui/react-slot.
    • CSS tweaks: new --sidebar token and base layer applying bg/foreground and outline defaults.
    • Dependencies: added individual @radix-ui/react-* packages, bumped react-day-picker to 9.9.0, pinned zod, and updated lockfile.
  • Migration

    • Update imports from 'radix-ui' to '@radix-ui/react-...'.
    • Replace any Button usages of isLoading/loadingText; remove references to dropped Badge variants (important, promotions, personal, updates, forums).
    • Avatar is now rounded-full by default; override if you need square avatars.

Summary by CodeRabbit

  • Style

    • Unified dialog overlays with an in-content close button; refreshed visuals for buttons, inputs, selects, tabs, switches, and menus.
    • Circular avatars, improved transitions (accordion, progress), updated skeleton color, and sidebar light/dark theme variables.
    • More consistent spacing, sizes, and scrollable dropdowns/menus.
  • Refactor

    • Migrated to modular Radix components and standardized slot-based APIs.
    • Removed legacy props (e.g., dialog overlay flag); simplified Button (no built-in loading state).
    • Scroll areas now always render scrollbars.
  • Chores

    • Replaced umbrella radix-ui with scoped packages; upgraded react-day-picker.
    • Pinned zod version.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Refactors UI to migrate from the monolithic radix-ui import to scoped @radix-ui/* packages, overhauls Dialog and Sheet APIs, simplifies Button/Input/Separator/Skeleton, updates ScrollArea signature, tweaks Command components, removes DialogContent’s showOverlay usage across call sites, adjusts global CSS variables/base, and updates dependencies including Radix packages and zod pin.

Changes

Cohort / File(s) Summary of changes
Dialog API overhaul
apps/mail/components/ui/dialog.tsx
Rewrote Dialog to explicit functional components with data-slot attributes; removed showOverlay prop; added always-present overlay and optional close button; updated exports (Dialog, Trigger, Portal, Overlay, Content, Close, Header, Footer, Title, Description).
DialogContent usage cleanup
apps/mail/app/(routes)/settings/connections/page.tsx, .../settings/danger-zone/page.tsx, apps/mail/components/connection/add.tsx, .../context/label-sidebar-context.tsx, .../create/email-composer.tsx, .../create/template-button.tsx, .../labels/label-dialog.tsx, .../mail/mail-display.tsx, .../onboarding.tsx, .../setup-phone.tsx, .../ui/pricing-dialog.tsx, .../ui/prompts-dialog.tsx
Removed DialogContent showOverlay prop at all call sites; rely on new Dialog overlay behavior.
Radix package migration
apps/mail/components/ui/accordion.tsx, .../alert.tsx, .../avatar.tsx, .../checkbox.tsx, .../collapsible.tsx, .../context-menu.tsx, .../dropdown-menu.tsx, .../label.tsx, .../navigation-menu.tsx, .../popover.tsx, .../progress.tsx, .../radio-group.tsx, .../select.tsx, .../separator.tsx, .../switch.tsx, .../tabs.tsx, .../toggle.tsx, .../toggle-group.tsx, .../tooltip.tsx, apps/mail/components/magicui/file-tree.tsx, apps/mail/components/responsive-modal.tsx, apps/mail/components/ui/sidebar.tsx, apps/mail/components/ui/pricing-switch.tsx, apps/mail/components/ui/form.tsx
Switched imports from radix-ui to scoped @radix-ui/react-* packages; minor class and accessibility tweaks; added 'use client' where needed; updated Slot/VisuallyHidden imports/usage.
Button/Input/Separator/Skeleton refactors
apps/mail/components/ui/button.tsx, .../input.tsx, .../separator.tsx, .../skeleton.tsx
Button simplified (removed loading props/UI, switched to Slot/asChild, updated variants). Input converted to function, new classes, no ref. Separator simplified to function, no ref, data-slot added. Skeleton signature to React.ComponentProps<'div'>, bg change to accent, data-slot added.
Command components
apps/mail/components/ui/command.tsx, apps/mail/components/context/command-palette-context.tsx
Command dialog/layout restyled; added exports: CommandInput, CommandList, CommandSeparator; switched Search icon; minor radius/spacing changes. Renamed CommandItem type to CommandItemType; fixed VisuallyHidden import/usage.
Sheet API refactor
apps/mail/components/ui/sheet.tsx
Replaced variant-based content with side-driven SheetContent; added composed Portal/Overlay and internal close button; added data-slot attributes; removed exported SheetPortal/SheetOverlay; converted Root/Trigger/Close to wrappers.
ScrollArea API change
apps/mail/components/ui/scroll-area.tsx
Removed custom props (hideScrollbar); switched to Radix-derived props; always render ScrollBar; updated padding utilities; maintained exports.
CSS variables/base
apps/mail/app/globals.css
Added --sidebar CSS variable for light/dark; updated base layer: simplified body styles; added universal border/outline styles.
Package/dependency updates
apps/mail/package.json, pnpm-workspace.yaml
Replaced radix-ui with multiple @radix-ui/react-* deps; upgraded react-day-picker to 9.9.0; pinned zod to 3.25.67 in workspace catalog.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App
  participant Dialog as Dialog (Radix)
  participant Overlay as Overlay (always-on)
  participant Content as DialogContent

  User->>App: Click DialogTrigger
  App->>Dialog: open=true
  Dialog->>Overlay: Render fixed overlay
  Dialog->>Content: Render content (showCloseButton?)
  alt Close via button
    User->>Content: Click Close (X)
    Content->>Dialog: onClose()
    Dialog-->>Overlay: Unmount
    Dialog-->>Content: Unmount
  else Click outside / Esc
    User->>Dialog: Dismiss
    Dialog-->>Overlay: Unmount
    Dialog-->>Content: Unmount
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant App
  participant Sheet as Sheet Root
  participant SOverlay as SheetOverlay
  participant SContent as SheetContent (side=top|right|bottom|left)

  User->>App: Click SheetTrigger
  App->>Sheet: open=true
  Sheet->>SOverlay: Render backdrop
  Sheet->>SContent: Slide-in from side
  User->>SContent: Interact
  User->>SContent: Click Close (X) / overlay / Esc
  SContent->>Sheet: onClose()
  Sheet-->>SOverlay: Unmount
  Sheet-->>SContent: Slide-out & unmount
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

design, high priority

Suggested reviewers

  • ahmetskilinc
  • MrgSub
  • nizzyabi

Poem

New slots ignite, the overlays glide,
Radix reborn, components unified.
Buttons trimmed, inputs lean—clean flight,
Sheets slide in from left or right.
CSS whispers: sidebar light.
Ship it fast—tonight’s the night. 🚀

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ArjunCodess ArjunCodess marked this pull request as ready for review September 1, 2025 15:09
@coderabbitai coderabbitai bot added design Improvements & changes to design & UX High Priority High Priority Work labels Sep 1, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 46

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (42)
apps/mail/components/ui/alert.tsx (4)

30-38: Fix DOM type mismatch for AlertTitle.

Ref says paragraph, element is h5. In strict mode, this will bite. Make the ref an HTMLHeadingElement.

Apply this diff:

-const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
+const AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(

41-46: Fix DOM type mismatch for AlertDescription.

Ref/props typed as paragraph, but you render a div. Correct the element types.

Apply this diff:

-const AlertDescription = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => (
-  <div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
+const AlertDescription = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLDivElement>
+>(({ className, ...props }, ref) => (
+  <div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />

45-45: Optional: adopt modern descendant variant
Let’s future-proof this component by swapping [&_p]:leading-relaxed for **:p:leading-relaxed in apps/mail/components/ui/alert.tsx:45. We’ve spotted numerous other [&_*] selectors across apps/mail—consider a global migration once you enable the new Tailwind variants.


30-46: Align forwardRef generics with rendered tags.

  • apps/mail/components/ui/alert.tsx
    • AlertTitle: change the ref generic from HTMLParagraphElement to HTMLHeadingElement.
    • AlertDescription: change the ref generic from HTMLParagraphElement to HTMLDivElement and update props to React.HTMLAttributes (or swap the <div> for a <p>).
  • Add a repo-wide AST-based check (e.g. ast-grep or lint rule) to enforce that forwardRef<T, …> matches the actual JSX tag and prevent future mismatches.
apps/mail/components/ui/resizable.tsx (4)

3-7: Use type-only React imports; don’t rely on the global React namespace

Strict TS setups can choke on React.ComponentProps without an explicit import. Import the type and simplify references.

 'use client';

+import type { ComponentProps } from 'react';
 import * as ResizablePrimitive from 'react-resizable-panels';
 import { GripVertical } from 'lucide-react';

 import { cn } from '@/lib/utils';

-}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
+}: ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (

-}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
+}: ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
   withHandle?: boolean;
 }) => (

Also applies to: 8-12, 24-26


12-15: Wrap long Tailwind strings to meet 100-char limit

Break the long class into multiple cn segments for readability and to honor repo limits.

   <ResizablePrimitive.PanelGroup
-    className={cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', className)}
+    className={cn(
+      'flex h-full w-full',
+      'data-[panel-group-direction=vertical]:flex-col',
+      className,
+    )}
     {...props}
   />

28-31: Tailwind rename + readability: outline-none → outline-hidden; split the mega string

Update deprecated utility per guidelines and split to keep lines sane.

   <ResizablePrimitive.PanelResizeHandle
-    className={cn(
-      'bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90',
-      className,
-    )}
+    className={cn(
+      'bg-border relative flex w-px items-center justify-center',
+      'focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1',
+      'after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2',
+      'data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full',
+      'data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full',
+      'data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0',
+      '[&[data-panel-group-direction=vertical]>div]:rotate-90',
+      className,
+    )}
     {...props}
   >

35-37: Tailwind rename: rounded-sm → rounded-xs

Matches the repo’s utility renames.

-      <div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-sm border">
+      <div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
         <GripVertical className="h-2.5 w-2.5" />
       </div>
apps/mail/components/responsive-modal.tsx (2)

29-35: Fix VisuallyHidden usage — named import shouldn’t be dot-chained.

You imported { VisuallyHidden }, but you’re rendering VisuallyHidden.VisuallyHidden. That will explode at runtime. Render the component directly (or switch to namespace import and use .Root). Let’s keep it simple.

-        <VisuallyHidden.VisuallyHidden>
+        <VisuallyHidden>
           <DialogHeader>
             <DialogTitle>Title</DialogTitle>
             <DialogDescription>Modal content</DialogDescription>
           </DialogHeader>
-        </VisuallyHidden.VisuallyHidden>
+        </VisuallyHidden>

...

-        <VisuallyHidden.VisuallyHidden>
+        <VisuallyHidden>
           <DrawerHeader>
             <DrawerTitle>Title</DrawerTitle>
           </DrawerHeader>
-        </VisuallyHidden.VisuallyHidden>
+        </VisuallyHidden>

Also applies to: 45-50


28-38: Move hidden DialogHeader into DialogContent
Aligns with Radix UI’s requirement that DialogTitle/DialogDescription be descendants of DialogContent for proper context-based a11y wiring. (radix-ui.com)

apps/mail/components/ui/tooltip.tsx (3)

21-21: Outline variant currently renders no border width.

border-border sets color only. Add a width so the outline actually appears.

Apply:

-        outline: 'border-border',
+        outline: 'border border-border',

49-69: Wrap Content in a Portal to avoid clipping and stacking-context gotchas.

Without a Portal, tooltips can be cut off inside overflow/transform containers. Radix ships a Portal for this—use it.

Apply:

-  <TooltipPrimitive.Content
-    ref={ref}
-    sideOffset={sideOffset}
-    className={cn(tooltipVariants({ variant }), className)}
-    {...props}
-  >
+  <TooltipPrimitive.Portal>
+    <TooltipPrimitive.Content
+      ref={ref}
+      sideOffset={sideOffset}
+      className={cn(tooltipVariants({ variant }), className)}
+      {...props}
+    >
       {variant === 'sidebar' && (
         <svg
           width="6"
           height="10"
           viewBox="0 0 6 10"
           fill="none"
           xmlns="http://www.w3.org/2000/svg"
           className="absolute left-[-6px] top-1/2 -translate-y-1/2"
         >
           <path d="M6 0L0 5L6 10V0Z" className="fill-white/80 dark:fill-[#413F3E]" />
         </svg>
       )}
       {props.children}
-  </TooltipPrimitive.Content>
+    </TooltipPrimitive.Content>
+  </TooltipPrimitive.Portal>

55-66: Make the arrow non-interactive to prevent hover flicker.

The SVG can steal pointer events at the edge. Nuke them.

Apply:

-        className="absolute left-[-6px] top-1/2 -translate-y-1/2"
+        className="pointer-events-none absolute left-[-6px] top-1/2 -translate-y-1/2"
apps/mail/components/ui/toggle.tsx (1)

36-36: Minor robustness: don’t rely on cva merging className.

Safer to pass className via cn to avoid depending on cva’s className passthrough semantics.

-    className={cn(toggleVariants({ variant, size, className }))}
+    className={cn(toggleVariants({ variant, size }), className)}
apps/mail/app/globals.css (1)

19-19: Switch to native @variant dark directive (apps/mail/app/globals.css:19)
Tailwind v4 natively supports dark mode via @variant dark, so replace the custom hack:

-/* Custom variant for dark mode */
-@custom-variant dark (&:where(.dark, .dark *));
+/* Use Tailwind v4 dark mode variant */
+@variant dark;

Then wrap your dark-mode rules in @variant dark { … } as needed.

apps/mail/components/ui/progress.tsx (1)

12-21: Forward value to Root (a11y), clamp it, and prefer transition-transform.

Right now value is stripped from Root, so ARIA reads it as indeterminate—bad UX. Also, clamp to [0,100] to avoid overshoot. transition-all is overkill; animate only transforms for perf.

Apply:

   <ProgressPrimitive.Root
     ref={ref}
     className={cn('bg-secondary relative h-4 w-full overflow-hidden rounded-full', className)}
+    value={value}
     {...props}
   >
     <ProgressPrimitive.Indicator
-      className="bg-primary h-full w-full flex-1 transition-all"
-      style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
+      className="bg-primary h-full w-full flex-1 transition-transform"
+      style={{
+        transform: `translateX(-${
+          100 - Math.max(0, Math.min(100, value ?? 0))
+        }%)`,
+      }}
     />
apps/mail/components/ui/radio-group.tsx (1)

25-27: Replace deprecated Tailwind utility and align base border token.

Use outline-hidden per guidelines and avoid primary border for the unchecked state.

-        'border-primary text-primary ring-offset-background focus-visible:ring-ring aspect-square h-4 w-4 rounded-full border focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
+        'border-input data-[state=checked]:border-primary text-primary ring-offset-background focus-visible:ring-ring aspect-square h-4 w-4 rounded-full border focus:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
apps/mail/components/connection/add.tsx (1)

119-119: Tailwind class likely invalid: 'size-6!'

Tailwind’s important modifier is prefix ‘!’, not suffix. Use '!size-6' (or plain 'size-6') to avoid no-op class.

Apply this diff:

-                  <Icon className="size-6!" />
+                  <Icon className="!size-6" />
apps/mail/components/ui/label.tsx (1)

15-18: Drop unused VariantProps to avoid API noise.

labelVariants defines no variants, yet props expose VariantProps — dead weight.

Apply:

-  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
+  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => (
-  <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
+  <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />

If you intend to add variants, wire them through instead of removing.

apps/mail/components/ui/checkbox.tsx (1)

13-24: Tailwind v4 rename fixes: outline + rounding.

Align with the renamed utilities to avoid subtle styling drift.

Apply:

   <CheckboxPrimitive.Root
     ref={ref}
     className={cn(
-      'border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer h-4 w-4 shrink-0 rounded-sm border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
+      'border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer h-4 w-4 shrink-0 rounded-xs border focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
       className,
     )}
apps/mail/components/ui/sidebar.tsx (6)

131-145: Icon size mismatch blows up the trigger button.

Button is 28px; icon is h-12. That’s… suboptimal UI.

Apply:

-      <Menu className="h-12" />
+      <Menu className="size-4" />

291-305: Use outline-hidden (Tailwind v4 rename).

Keep consistency with new utilities.

Apply:

-    <Comp
+    <Comp
       ref={ref}
       data-sidebar="group-label"
       className={cn(
-        'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-none transition-[margin,opacity] duration-200 ease-in-out focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
+        'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-in-out focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
         'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
         className,
       )}

312-329: Same rename here: outline-hidden.

Apply:

-      className={cn(
+      className={cn(
         'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
+        // outline-none -> outline-hidden
+        // (Tailwind v4)

Better as a direct change:

-        'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
+        'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',

569-589: One more outline rename on sub-button.

Apply:

-      className={cn(
+      className={cn(
         'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
+        // outline-none -> outline-hidden

Direct fix:

-        '... rounded-md px-2 outline-none focus-visible:ring-2 ...',
+        '... rounded-md px-2 outline-hidden focus-visible:ring-2 ...',

111-116: shadow → shadow-sm (Tailwind v4 rename).

Prevents silent style drift across themes.

Apply:

-            className="group-data-[variant=floating]:border-sidebar-border bg-sidebar dark:bg-sidebar flex h-full w-full flex-col overflow-hidden duration-300 ease-in-out group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow"
+            className="group-data-[variant=floating]:border-sidebar-border bg-sidebar dark:bg-sidebar flex h-full w-full flex-col overflow-hidden duration-300 ease-in-out group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"

1-617: Replace all remaining outline-none with outline-hidden
No legacy Slot.Slot usages detected. However, numerous outline-none instances remain across UI components (e.g., in calendar.tsx, dropdown-menu.tsx, textarea.tsx, sidebar.tsx, navigation-menu.tsx, etc.). Update them to focus-visible:outline-hidden (or equivalent) to complete the migration.

apps/mail/components/ui/toggle-group.tsx (1)

42-45: Prop precedence is inverted; item-level props should override context.

Right now context wins, making per-item overrides impossible.

Apply:

-        toggleVariants({
-          variant: context.variant || variant,
-          size: context.size || size,
-        }),
+        toggleVariants({
+          variant: variant ?? context.variant,
+          size: size ?? context.size,
+        }),
apps/mail/components/ui/collapsible.tsx (1)

7-9: Bug: wrong Radix members (will break at runtime).

In @radix-ui/react-collapsible the members are Trigger and Content, not CollapsibleTrigger/CollapsibleContent.

Apply:

-const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
-
-const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
+const CollapsibleTrigger = CollapsiblePrimitive.Trigger;
+
+const CollapsibleContent = CollapsiblePrimitive.Content;

Optional simplification (clearer and tree-shakeable):

export {
  Root as Collapsible,
  Trigger as CollapsibleTrigger,
  Content as CollapsibleContent,
} from '@radix-ui/react-collapsible';
apps/mail/components/ui/form.tsx (2)

108-114: Add aria-errormessage for robust a11y.
Makes error association explicit for AT. Minimal change, maximal clarity.

     <Slot
       ref={ref}
       id={formItemId}
       aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
       aria-invalid={!!error}
+      aria-errormessage={error ? formMessageId : undefined}
       {...props}
     />

141-156: Announce errors: add role/aria-live on message.
Make it obvious to screen readers. Accessibility at ludicrous speed.

   return (
     <p
       ref={ref}
       id={formMessageId}
-      className={cn('text-destructive text-sm font-medium', className)}
+      className={cn('text-destructive text-sm font-medium', className)}
+      role={error ? 'alert' : undefined}
+      aria-live={error ? 'polite' : undefined}
       {...props}
     >
       {body}
     </p>
   );
apps/mail/components/ui/accordion.tsx (2)

27-35: Drop transition-all on the trigger to avoid layout/paint churn.

Only color and the child svg rotate. Prefer targeted transitions.

Apply:

-        'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
+        'flex flex-1 items-center justify-between py-4 font-medium hover:underline transition-colors [&[data-state=open]>svg]:rotate-180',

45-49: Avoid transition-all on content; honor reduced motion.

You already have explicit keyframe animations; transition-all is redundant and can jank.

Apply:

-    className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm transition-all"
+    className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down motion-reduce:!animate-none overflow-hidden text-sm"
apps/mail/components/ui/scroll-area.tsx (1)

30-42: Add data-slot for consistency with the new slot-based system

Small but useful for styling hooks.

-  <ScrollAreaPrimitive.ScrollAreaScrollbar
+  <ScrollAreaPrimitive.ScrollAreaScrollbar
+    data-slot="scrollbar"
@@
-    <ScrollAreaPrimitive.ScrollAreaThumb className="bg-border relative flex-1 rounded-full" />
+    <ScrollAreaPrimitive.ScrollAreaThumb
+      data-slot="scrollbar-thumb"
+      className="bg-border relative flex-1 rounded-full"
+    />
apps/mail/components/context/label-sidebar-context.tsx (1)

56-60: Use onSelect instead of onClick on ContextMenuItem for reliable close semantics.

Radix ContextMenu items fire onSelect and close by default; onClick can be less consistent when asChild is used.

-          <ContextMenuItem
-            asChild
-            onClick={() => setDeleteDialogOpen(true)}
+          <ContextMenuItem
+            asChild
+            onSelect={() => setDeleteDialogOpen(true)}
             disabled={false}
             className="gap-2 text-sm"
           >
apps/mail/components/create/template-button.tsx (1)

203-204: Tailwind class: use arbitrary value syntax for z-index.

z-99999 isn’t a standard scale value. Prefer z-[99999] to guarantee it compiles under the Tailwind plugin stack.

-        <DropdownMenuContent className="z-99999 w-60" align="start" sideOffset={6}>
+        <DropdownMenuContent className="z-[99999] w-60" align="start" sideOffset={6}>
@@
-              <DropdownMenuSubContent className="z-99999 w-60">
+              <DropdownMenuSubContent className="z-[99999] w-60">

Also applies to: 218-218

apps/mail/components/create/email-composer.tsx (3)

1008-1025: Fix invalid Tailwind class: use z-[99999], not z-99999.

z-99999 isn’t a default utility; JIT won’t emit it. This can break layering with the new overlay.

-        <DialogContent className="z-99999 sm:max-w-[425px]">
+        <DialogContent className="z-[99999] sm:max-w-[425px]">

1028-1056: Same z-index issue here—apply the bracketed arbitrary value.

Keep stacking consistent across both dialogs.

-        <DialogContent className="z-99999 sm:max-w-[425px]">
+        <DialogContent className="z-[99999] sm:max-w-[425px]">

1008-1056: Consider replacing giant z-index with a tokenized layer.

Hardcoding 99999 is… suboptimal. Define a design-token or Tailwind theme layer (e.g., z-modal) so overlays stay deterministic across portals.

I can draft a Tailwind theme extension and refactor call-sites if you want.

apps/mail/components/ui/context-menu.tsx (1)

83-87: Replace outline-none with outline-hidden.

Consistency matters.

-      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
+      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
apps/mail/components/ui/dropdown-menu.tsx (1)

158-158: Unify separator color with ContextMenu (bg-border).

Minor consistency polish across menus.

-    className={cn('bg-muted -mx-1 my-1 h-px', className)}
+    className={cn('bg-border -mx-1 my-1 h-px', className)}
apps/mail/components/ui/command.tsx (2)

42-51: Rename outline-none to outline-hidden per guidelines.

Aligns with the repo’s Tailwind rename policy.

-        'placeholder:text-muted-foreground flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50',
+        'placeholder:text-muted-foreground flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',

111-118: Unify selected-state selector.

You’re mixing quoted and unquoted data-attr variants; pick one for consistency and to avoid accidental specificity quirks.

-      "data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+      'data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 64c5480 and 501cd82.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • apps/mail/app/(routes)/settings/connections/page.tsx (1 hunks)
  • apps/mail/app/(routes)/settings/danger-zone/page.tsx (1 hunks)
  • apps/mail/app/globals.css (6 hunks)
  • apps/mail/components/connection/add.tsx (1 hunks)
  • apps/mail/components/context/command-palette-context.tsx (5 hunks)
  • apps/mail/components/context/label-sidebar-context.tsx (1 hunks)
  • apps/mail/components/create/email-composer.tsx (2 hunks)
  • apps/mail/components/create/template-button.tsx (1 hunks)
  • apps/mail/components/labels/label-dialog.tsx (1 hunks)
  • apps/mail/components/magicui/file-tree.tsx (1 hunks)
  • apps/mail/components/mail/mail-display.tsx (2 hunks)
  • apps/mail/components/onboarding.tsx (1 hunks)
  • apps/mail/components/responsive-modal.tsx (1 hunks)
  • apps/mail/components/setup-phone.tsx (1 hunks)
  • apps/mail/components/ui/accordion.tsx (3 hunks)
  • apps/mail/components/ui/alert.tsx (1 hunks)
  • apps/mail/components/ui/avatar.tsx (2 hunks)
  • apps/mail/components/ui/badge.tsx (1 hunks)
  • apps/mail/components/ui/button.tsx (2 hunks)
  • apps/mail/components/ui/checkbox.tsx (1 hunks)
  • apps/mail/components/ui/collapsible.tsx (1 hunks)
  • apps/mail/components/ui/command.tsx (8 hunks)
  • apps/mail/components/ui/context-menu.tsx (7 hunks)
  • apps/mail/components/ui/dialog.tsx (1 hunks)
  • apps/mail/components/ui/drawer.tsx (3 hunks)
  • apps/mail/components/ui/dropdown-menu.tsx (7 hunks)
  • apps/mail/components/ui/form.tsx (3 hunks)
  • apps/mail/components/ui/input.tsx (1 hunks)
  • apps/mail/components/ui/label.tsx (1 hunks)
  • apps/mail/components/ui/navigation-menu.tsx (5 hunks)
  • apps/mail/components/ui/popover.tsx (2 hunks)
  • apps/mail/components/ui/pricing-dialog.tsx (1 hunks)
  • apps/mail/components/ui/pricing-switch.tsx (2 hunks)
  • apps/mail/components/ui/progress.tsx (2 hunks)
  • apps/mail/components/ui/prompts-dialog.tsx (1 hunks)
  • apps/mail/components/ui/radio-group.tsx (1 hunks)
  • apps/mail/components/ui/resizable.tsx (1 hunks)
  • apps/mail/components/ui/scroll-area.tsx (1 hunks)
  • apps/mail/components/ui/select.tsx (8 hunks)
  • apps/mail/components/ui/separator.tsx (1 hunks)
  • apps/mail/components/ui/sheet.tsx (1 hunks)
  • apps/mail/components/ui/sidebar.tsx (6 hunks)
  • apps/mail/components/ui/skeleton.tsx (1 hunks)
  • apps/mail/components/ui/switch.tsx (2 hunks)
  • apps/mail/components/ui/tabs.tsx (3 hunks)
  • apps/mail/components/ui/textarea.tsx (1 hunks)
  • apps/mail/components/ui/toggle-group.tsx (1 hunks)
  • apps/mail/components/ui/toggle.tsx (1 hunks)
  • apps/mail/components/ui/tooltip.tsx (1 hunks)
  • apps/mail/package.json (2 hunks)
  • pnpm-workspace.yaml (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{css,js,ts,jsx,tsx,mdx}

📄 CodeRabbit inference engine (.cursor/rules/tailwind-css-v4.mdc)

**/*.{css,js,ts,jsx,tsx,mdx}: Chain variants together for composable variants (e.g., group-has-data-potato:opacity-100).
Use new variants such as starting, not-*, inert, nth-*, in-*, open (for :popover-open), and ** for all descendants.
Do not use deprecated utilities like bg-opacity-*, text-opacity-*, border-opacity-*, and divide-opacity-*; use the new syntax (e.g., bg-black/50).
Use renamed utilities: shadow-sm is now shadow-xs, shadow is now shadow-sm, drop-shadow-sm is now drop-shadow-xs, drop-shadow is now drop-shadow-sm, blur-sm is now blur-xs, blur is now blur-sm, rounded-sm is now rounded-xs, rounded is now rounded-sm, outline-none is now outline-hidden.
Use bg-(--brand-color) syntax for CSS variables in arbitrary values instead of bg-[--brand-color].
Stacked variants now apply left-to-right instead of right-to-left.

Files:

  • apps/mail/components/mail/mail-display.tsx
  • apps/mail/app/(routes)/settings/connections/page.tsx
  • apps/mail/components/context/label-sidebar-context.tsx
  • apps/mail/app/(routes)/settings/danger-zone/page.tsx
  • apps/mail/components/connection/add.tsx
  • apps/mail/components/ui/resizable.tsx
  • apps/mail/components/ui/prompts-dialog.tsx
  • apps/mail/components/ui/alert.tsx
  • apps/mail/components/responsive-modal.tsx
  • apps/mail/components/ui/pricing-dialog.tsx
  • apps/mail/components/labels/label-dialog.tsx
  • apps/mail/components/ui/textarea.tsx
  • apps/mail/components/ui/tooltip.tsx
  • apps/mail/components/magicui/file-tree.tsx
  • apps/mail/components/setup-phone.tsx
  • apps/mail/components/ui/toggle-group.tsx
  • apps/mail/components/ui/checkbox.tsx
  • apps/mail/components/onboarding.tsx
  • apps/mail/components/create/template-button.tsx
  • apps/mail/components/ui/progress.tsx
  • apps/mail/components/create/email-composer.tsx
  • apps/mail/components/ui/drawer.tsx
  • apps/mail/components/ui/label.tsx
  • apps/mail/components/ui/input.tsx
  • apps/mail/components/ui/command.tsx
  • apps/mail/components/ui/tabs.tsx
  • apps/mail/components/ui/sidebar.tsx
  • apps/mail/components/ui/context-menu.tsx
  • apps/mail/components/ui/select.tsx
  • apps/mail/components/ui/skeleton.tsx
  • apps/mail/app/globals.css
  • apps/mail/components/ui/scroll-area.tsx
  • apps/mail/components/ui/separator.tsx
  • apps/mail/components/ui/toggle.tsx
  • apps/mail/components/ui/collapsible.tsx
  • apps/mail/components/ui/radio-group.tsx
  • apps/mail/components/ui/navigation-menu.tsx
  • apps/mail/components/context/command-palette-context.tsx
  • apps/mail/components/ui/dropdown-menu.tsx
  • apps/mail/components/ui/avatar.tsx
  • apps/mail/components/ui/form.tsx
  • apps/mail/components/ui/pricing-switch.tsx
  • apps/mail/components/ui/accordion.tsx
  • apps/mail/components/ui/switch.tsx
  • apps/mail/components/ui/popover.tsx
  • apps/mail/components/ui/button.tsx
  • apps/mail/components/ui/sheet.tsx
  • apps/mail/components/ui/badge.tsx
  • apps/mail/components/ui/dialog.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENT.md)

**/*.{js,jsx,ts,tsx}: Use 2-space indentation
Use single quotes
Limit lines to 100 characters
Semicolons are required

Files:

  • apps/mail/components/mail/mail-display.tsx
  • apps/mail/app/(routes)/settings/connections/page.tsx
  • apps/mail/components/context/label-sidebar-context.tsx
  • apps/mail/app/(routes)/settings/danger-zone/page.tsx
  • apps/mail/components/connection/add.tsx
  • apps/mail/components/ui/resizable.tsx
  • apps/mail/components/ui/prompts-dialog.tsx
  • apps/mail/components/ui/alert.tsx
  • apps/mail/components/responsive-modal.tsx
  • apps/mail/components/ui/pricing-dialog.tsx
  • apps/mail/components/labels/label-dialog.tsx
  • apps/mail/components/ui/textarea.tsx
  • apps/mail/components/ui/tooltip.tsx
  • apps/mail/components/magicui/file-tree.tsx
  • apps/mail/components/setup-phone.tsx
  • apps/mail/components/ui/toggle-group.tsx
  • apps/mail/components/ui/checkbox.tsx
  • apps/mail/components/onboarding.tsx
  • apps/mail/components/create/template-button.tsx
  • apps/mail/components/ui/progress.tsx
  • apps/mail/components/create/email-composer.tsx
  • apps/mail/components/ui/drawer.tsx
  • apps/mail/components/ui/label.tsx
  • apps/mail/components/ui/input.tsx
  • apps/mail/components/ui/command.tsx
  • apps/mail/components/ui/tabs.tsx
  • apps/mail/components/ui/sidebar.tsx
  • apps/mail/components/ui/context-menu.tsx
  • apps/mail/components/ui/select.tsx
  • apps/mail/components/ui/skeleton.tsx
  • apps/mail/components/ui/scroll-area.tsx
  • apps/mail/components/ui/separator.tsx
  • apps/mail/components/ui/toggle.tsx
  • apps/mail/components/ui/collapsible.tsx
  • apps/mail/components/ui/radio-group.tsx
  • apps/mail/components/ui/navigation-menu.tsx
  • apps/mail/components/context/command-palette-context.tsx
  • apps/mail/components/ui/dropdown-menu.tsx
  • apps/mail/components/ui/avatar.tsx
  • apps/mail/components/ui/form.tsx
  • apps/mail/components/ui/pricing-switch.tsx
  • apps/mail/components/ui/accordion.tsx
  • apps/mail/components/ui/switch.tsx
  • apps/mail/components/ui/popover.tsx
  • apps/mail/components/ui/button.tsx
  • apps/mail/components/ui/sheet.tsx
  • apps/mail/components/ui/badge.tsx
  • apps/mail/components/ui/dialog.tsx
**/*.{js,jsx,ts,tsx,css}

📄 CodeRabbit inference engine (AGENT.md)

Use Prettier with sort-imports and Tailwind plugins

Files:

  • apps/mail/components/mail/mail-display.tsx
  • apps/mail/app/(routes)/settings/connections/page.tsx
  • apps/mail/components/context/label-sidebar-context.tsx
  • apps/mail/app/(routes)/settings/danger-zone/page.tsx
  • apps/mail/components/connection/add.tsx
  • apps/mail/components/ui/resizable.tsx
  • apps/mail/components/ui/prompts-dialog.tsx
  • apps/mail/components/ui/alert.tsx
  • apps/mail/components/responsive-modal.tsx
  • apps/mail/components/ui/pricing-dialog.tsx
  • apps/mail/components/labels/label-dialog.tsx
  • apps/mail/components/ui/textarea.tsx
  • apps/mail/components/ui/tooltip.tsx
  • apps/mail/components/magicui/file-tree.tsx
  • apps/mail/components/setup-phone.tsx
  • apps/mail/components/ui/toggle-group.tsx
  • apps/mail/components/ui/checkbox.tsx
  • apps/mail/components/onboarding.tsx
  • apps/mail/components/create/template-button.tsx
  • apps/mail/components/ui/progress.tsx
  • apps/mail/components/create/email-composer.tsx
  • apps/mail/components/ui/drawer.tsx
  • apps/mail/components/ui/label.tsx
  • apps/mail/components/ui/input.tsx
  • apps/mail/components/ui/command.tsx
  • apps/mail/components/ui/tabs.tsx
  • apps/mail/components/ui/sidebar.tsx
  • apps/mail/components/ui/context-menu.tsx
  • apps/mail/components/ui/select.tsx
  • apps/mail/components/ui/skeleton.tsx
  • apps/mail/app/globals.css
  • apps/mail/components/ui/scroll-area.tsx
  • apps/mail/components/ui/separator.tsx
  • apps/mail/components/ui/toggle.tsx
  • apps/mail/components/ui/collapsible.tsx
  • apps/mail/components/ui/radio-group.tsx
  • apps/mail/components/ui/navigation-menu.tsx
  • apps/mail/components/context/command-palette-context.tsx
  • apps/mail/components/ui/dropdown-menu.tsx
  • apps/mail/components/ui/avatar.tsx
  • apps/mail/components/ui/form.tsx
  • apps/mail/components/ui/pricing-switch.tsx
  • apps/mail/components/ui/accordion.tsx
  • apps/mail/components/ui/switch.tsx
  • apps/mail/components/ui/popover.tsx
  • apps/mail/components/ui/button.tsx
  • apps/mail/components/ui/sheet.tsx
  • apps/mail/components/ui/badge.tsx
  • apps/mail/components/ui/dialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENT.md)

Enable TypeScript strict mode

Files:

  • apps/mail/components/mail/mail-display.tsx
  • apps/mail/app/(routes)/settings/connections/page.tsx
  • apps/mail/components/context/label-sidebar-context.tsx
  • apps/mail/app/(routes)/settings/danger-zone/page.tsx
  • apps/mail/components/connection/add.tsx
  • apps/mail/components/ui/resizable.tsx
  • apps/mail/components/ui/prompts-dialog.tsx
  • apps/mail/components/ui/alert.tsx
  • apps/mail/components/responsive-modal.tsx
  • apps/mail/components/ui/pricing-dialog.tsx
  • apps/mail/components/labels/label-dialog.tsx
  • apps/mail/components/ui/textarea.tsx
  • apps/mail/components/ui/tooltip.tsx
  • apps/mail/components/magicui/file-tree.tsx
  • apps/mail/components/setup-phone.tsx
  • apps/mail/components/ui/toggle-group.tsx
  • apps/mail/components/ui/checkbox.tsx
  • apps/mail/components/onboarding.tsx
  • apps/mail/components/create/template-button.tsx
  • apps/mail/components/ui/progress.tsx
  • apps/mail/components/create/email-composer.tsx
  • apps/mail/components/ui/drawer.tsx
  • apps/mail/components/ui/label.tsx
  • apps/mail/components/ui/input.tsx
  • apps/mail/components/ui/command.tsx
  • apps/mail/components/ui/tabs.tsx
  • apps/mail/components/ui/sidebar.tsx
  • apps/mail/components/ui/context-menu.tsx
  • apps/mail/components/ui/select.tsx
  • apps/mail/components/ui/skeleton.tsx
  • apps/mail/components/ui/scroll-area.tsx
  • apps/mail/components/ui/separator.tsx
  • apps/mail/components/ui/toggle.tsx
  • apps/mail/components/ui/collapsible.tsx
  • apps/mail/components/ui/radio-group.tsx
  • apps/mail/components/ui/navigation-menu.tsx
  • apps/mail/components/context/command-palette-context.tsx
  • apps/mail/components/ui/dropdown-menu.tsx
  • apps/mail/components/ui/avatar.tsx
  • apps/mail/components/ui/form.tsx
  • apps/mail/components/ui/pricing-switch.tsx
  • apps/mail/components/ui/accordion.tsx
  • apps/mail/components/ui/switch.tsx
  • apps/mail/components/ui/popover.tsx
  • apps/mail/components/ui/button.tsx
  • apps/mail/components/ui/sheet.tsx
  • apps/mail/components/ui/badge.tsx
  • apps/mail/components/ui/dialog.tsx
pnpm-workspace.yaml

📄 CodeRabbit inference engine (AGENT.md)

Monorepo managed with pnpm workspaces

Files:

  • pnpm-workspace.yaml
**/package.json

📄 CodeRabbit inference engine (AGENT.md)

Workspace packages use catalog versioning for shared dependencies

Files:

  • apps/mail/package.json
**/*.css

📄 CodeRabbit inference engine (.cursor/rules/tailwind-css-v4.mdc)

**/*.css: Use the @theme directive in CSS for Tailwind v4 configuration instead of tailwind.config.js.
Use @import "tailwindcss"; in CSS instead of @tailwind directives.
Legacy tailwind.config.js files can be imported in CSS using the @config directive.
Use native CSS @layer instead of Tailwind's custom implementation.
Access all Tailwind design tokens as CSS variables using the namespace format (e.g., --color-blue-500).
Override entire theme namespaces in CSS using --font-*: initial; or --*: initial;.
Use container query utilities (@container, @sm:, @max-md:, etc.) directly in CSS for responsive design.
Use 3D transform utilities such as transform-3d, rotate-x-*, scale-z-*, and perspective-* in CSS.
Use new gradient utilities like bg-linear-45, bg-conic, and bg-radial-[at_25%_25%] in CSS.
Use new shadow utilities such as inset-shadow-* and inset-ring-* in CSS.
Use new CSS property utilities like field-sizing-content, scheme-light, scheme-dark, and font-stretch-* in CSS.
Define custom utilities using the @utility directive in CSS.
Define custom variants using the @variant directive in CSS.
Use the @plugin directive in CSS to include Tailwind plugins.
Use new default styles: border color is currentColor, ring width is 1px, placeholder text uses current color at 50% opacity, and hover styles only apply on devices that support hover.
Use CSS variables instead of the theme() function.
Use the @import "tailwindcss" prefix(tw); syntax in CSS to add a prefix to all Tailwind classes.
Add sources for Tailwind using the @source directive in CSS.
Disable automatic source detection in Tailwind using @import "tailwindcss" source(none); in CSS.
Import legacy config files in CSS using @config.
Configure dark mode in CSS using the @variant dark directive.
Extend container customization in CSS using the @utility directive.

Files:

  • apps/mail/app/globals.css
🧠 Learnings (8)
📚 Learning: 2025-08-10T08:25:22.063Z
Learnt from: CR
PR: Mail-0/Zero#0
File: AGENT.md:0-0
Timestamp: 2025-08-10T08:25:22.063Z
Learning: Applies to **/package.json : Workspace packages use catalog versioning for shared dependencies

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-08-10T08:25:22.063Z
Learnt from: CR
PR: Mail-0/Zero#0
File: AGENT.md:0-0
Timestamp: 2025-08-10T08:25:22.063Z
Learning: Applies to pnpm-workspace.yaml : Monorepo managed with pnpm workspaces

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.{css,js,ts,jsx,tsx,mdx} : Use `bg-(--brand-color)` syntax for CSS variables in arbitrary values instead of `bg-[--brand-color]`.

Applied to files:

  • apps/mail/app/globals.css
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.css : Use new default styles: border color is `currentColor`, ring width is 1px, placeholder text uses current color at 50% opacity, and hover styles only apply on devices that support hover.

Applied to files:

  • apps/mail/app/globals.css
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.css : Use new CSS property utilities like `field-sizing-content`, `scheme-light`, `scheme-dark`, and `font-stretch-*` in CSS.

Applied to files:

  • apps/mail/app/globals.css
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.css : Configure dark mode in CSS using the `variant dark` directive.

Applied to files:

  • apps/mail/app/globals.css
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.{css,js,ts,jsx,tsx,mdx} : Use renamed utilities: `shadow-sm` is now `shadow-xs`, `shadow` is now `shadow-sm`, `drop-shadow-sm` is now `drop-shadow-xs`, `drop-shadow` is now `drop-shadow-sm`, `blur-sm` is now `blur-xs`, `blur` is now `blur-sm`, `rounded-sm` is now `rounded-xs`, `rounded` is now `rounded-sm`, `outline-none` is now `outline-hidden`.

Applied to files:

  • apps/mail/app/globals.css
  • apps/mail/components/ui/toggle.tsx
📚 Learning: 2025-08-03T20:42:04.207Z
Learnt from: CR
PR: Mail-0/Zero#0
File: .cursor/rules/tailwind-css-v4.mdc:0-0
Timestamp: 2025-08-03T20:42:04.207Z
Learning: Applies to **/*.css : Use native CSS `layer` instead of Tailwind's custom implementation.

Applied to files:

  • apps/mail/app/globals.css
🧬 Code graph analysis (21)
apps/mail/components/mail/mail-display.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/connection/add.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/labels/label-dialog.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/setup-phone.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/onboarding.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/create/template-button.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/create/email-composer.tsx (1)
apps/mail/components/ui/dialog.tsx (1)
  • DialogContent (121-121)
apps/mail/components/ui/drawer.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/input.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/command.tsx (2)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/icons/icons.tsx (1)
  • Search (1651-1666)
apps/mail/components/ui/select.tsx (2)
apps/mail/components/icons/icons.tsx (2)
  • ChevronDown (1200-1216)
  • Check (1634-1649)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/skeleton.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/scroll-area.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/separator.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/context/command-palette-context.tsx (1)
apps/mail/components/ui/dialog.tsx (2)
  • DialogTitle (127-127)
  • DialogDescription (122-122)
apps/mail/components/ui/dropdown-menu.tsx (1)
apps/mail/components/icons/icons.tsx (1)
  • ChevronRight (835-850)
apps/mail/components/ui/avatar.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/switch.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/button.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/sheet.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
apps/mail/components/ui/dialog.tsx (1)
apps/mail/lib/utils.ts (1)
  • cn (56-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer

--subtleBlack: #1F1F1F;
--skyBlue: #0066FF;
--shinyGray: #A1A1A1;
--sidebar: hsl(0 0% 98%);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unify the sidebar token — single source of truth.

You introduced --sidebar but the theme still maps to --sidebar-background. Wire it up or delete the new var to avoid dead config.

Apply this diff to use the new token across the theme:

@@
-  --sidebar: hsl(0 0% 98%);
+  --sidebar: hsl(0 0% 98%);
@@
-  --sidebar: hsl(240 5.9% 10%);
+  --sidebar: hsl(240 5.9% 10%);
@@
-  --color-sidebar: var(--sidebar-background);
+  --color-sidebar: var(--sidebar);

If other files still consume --sidebar-background, keep both and plan a follow-up sweep.

Also applies to: 118-118, 166-174

🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 80 (and also check 118 and 166-174),
you added a new CSS variable --sidebar but the rest of the theme still uses
--sidebar-background, creating a dead/duplicate token; either wire the theme to
use --sidebar everywhere or remove --sidebar. Fix by updating all occurrences of
--sidebar-background in this file (and any other project files) to reference
--sidebar (or add --sidebar-background as an alias that references --sidebar) so
there is a single source of truth, and if you choose to keep both add a TODO to
sweep other files later.

Comment on lines +480 to +487
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Kill the duplication: two base layers fight; last one wins.

You now have two @layer base blocks for * and body. This creates conflicting cascade. Pick one baseline and delete the other. If the intent is the simplified body, remove the earlier block.

Apply this diff to consolidate:

@@
-@layer base {
-  * {
-    @apply border-border;
-  }
-  body {
-    @apply bg-background text-foreground font-sans overscroll-none style-scrollbar;
-  }
-}

Optionally, if you still want the scrollbar styling and font family, fold them into the final block instead:

 @layer base {
   * {
-    @apply border-border outline-ring/50;
+    @apply border-border outline-ring/50;
   }
   body {
-    @apply bg-background text-foreground;
+    @apply bg-background text-foreground font-sans style-scrollbar;
+    /* If you intended to drop overscroll-none, omit it; else add it back explicitly */
+    /* @apply overscroll-none; */
   }
 }

Also sanity-check outline-ring/50 on * — global outline color is fine, but unnecessary work. Consider scoping to focusable elements if perf or specificity becomes noisy.


🏁 Script executed:

#!/bin/bash
echo "Occurrences of '@layer base' in apps/mail/app/globals.css:"
rg -n '@layer base' apps/mail/app/globals.css
echo ""
echo "Snippets around each occurrence:"
for L in $(rg -n '@layer base' apps/mail/app/globals.css | cut -d: -f1); do
  echo "=== Snippet around line $L ==="
  sed -n "$((L-5)),$((L+10))p" apps/mail/app/globals.css
  echo ""
done

Length of output: 1199


Consolidate duplicate @layer base blocks (lines 278 & 480). Only the last block wins, so remove the one at 278 or merge its unique body styles (font-sans overscroll-none style-scrollbar) into the block at 480. Consider scoping outline-ring/50 to focusable elements for better performance.

🤖 Prompt for AI Agents
In apps/mail/app/globals.css around lines 480 (and duplicate at 278), there are
two @layer base blocks where only the last one wins; remove the earlier block at
line 278 or merge its unique declarations (font-sans, overscroll-none,
style-scrollbar) into the block at 480 so all base styles are consolidated; also
scope the universal outline-rule (outline-ring/50) to focusable elements (e.g.,
links, buttons, inputs, textareas, selects, and elements using tabindex) instead
of applying to * to reduce styling cost and avoid unintended outlines.

Comment on lines +75 to 76
<DialogContent className="bg-panelLight mx-auto w-full max-w-[90%] rounded-xl border p-0 sm:max-w-[690px] dark:bg-[#111111]">
<div className="flex flex-col gap-4 p-4">
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Place DialogTitle inside DialogContent (or add a header) for proper a11y semantics.

Right now DialogTitle sits outside Content. Move it inside so aria attributes resolve correctly.

-    <Dialog open={open} onOpenChange={onOpenChange}>
-      <DialogTitle></DialogTitle>
-      <DialogContent className="bg-panelLight mx-auto w-full max-w-[90%] rounded-xl border p-0 sm:max-w-[690px] dark:bg-[#111111]">
+    <Dialog open={open} onOpenChange={onOpenChange}>
+      <DialogContent className="bg-panelLight mx-auto w-full max-w-[90%] rounded-xl border p-0 sm:max-w-[690px] dark:bg-[#111111]">
+        {/* Optional header for a11y */}
+        {/* <DialogHeader><DialogTitle>Welcome to Zero Email</DialogTitle></DialogHeader> */}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<DialogContent className="bg-panelLight mx-auto w-full max-w-[90%] rounded-xl border p-0 sm:max-w-[690px] dark:bg-[#111111]">
<div className="flex flex-col gap-4 p-4">
<Dialog open={open} onOpenChange={onOpenChange}>
{/* Removed the standalone <DialogTitle> so that it lives inside the content for proper aria semantics */}
<DialogContent className="bg-panelLight mx-auto w-full max-w-[90%] rounded-xl border p-0 sm:max-w-[690px] dark:bg-[#111111]">
{/* Optional header for a11y */}
{/* <DialogHeader>
<DialogTitle>Welcome to Zero Email</DialogTitle>
</DialogHeader> */}
<div className="flex flex-col gap-4 p-4">
{/* …the rest of your onboarding UI… */}
</div>
</DialogContent>
</Dialog>
🤖 Prompt for AI Agents
In apps/mail/components/onboarding.tsx around lines 75-76, DialogTitle is
currently placed outside of DialogContent which breaks accessibility semantics;
move the existing DialogTitle element so it is nested directly inside the
DialogContent (or add a header wrapper inside DialogContent and place
DialogTitle there) ensuring the title is rendered before the main content and
that aria-labelledby/aria-describedby references resolve correctly; update any
classNames/layout as needed so visual layout is unchanged after moving the
title.

import { Drawer, DrawerContent, DrawerHeader, DrawerTitle } from '@/components/ui/drawer';
import { VisuallyHidden } from 'radix-ui';
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
import { type ReactElement } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Type import nit — import ReactNode directly.

Cleaner and consistent with TS strict prefs.

-import { type ReactElement } from 'react';
+import type { ReactElement, ReactNode } from 'react';

 ...

 type ResponsiveModalProps = {
-  children: React.ReactNode;
+  children: ReactNode;
   open: boolean;
   onOpenChange: (open: boolean) => void;
 };

Also applies to: 13-17

🤖 Prompt for AI Agents
In apps/mail/components/responsive-modal.tsx around line 10 (and similarly lines
13–17), the import uses "type ReactElement"; change the import to pull ReactNode
instead (e.g. import { type ReactNode } from 'react') and update any related
prop/type annotations that currently reference ReactElement to use ReactNode for
consistency with TS strict preferences.

import { VisuallyHidden } from 'radix-ui';
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
import { type ReactElement } from 'react';
// import { useMedia } from 'react-use';
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

isDesktop hardcoded to true — mobile Drawer path is dead. Intended?

If not deliberate, re-enable a media query so this actually responds. Speed is key; correctness too.

-// import { useMedia } from 'react-use';
+import { useMedia } from 'react-use';

 ...

-  const isDesktop = true;
+  const isDesktop = useMedia('(min-width: 768px)', true);

Verify no hydration warnings and that Drawer renders below 768px.

Also applies to: 24-27

🤖 Prompt for AI Agents
In apps/mail/components/responsive-modal.tsx around line 11 (and also affecting
lines 24–27) you hardcoded isDesktop = true which disables the mobile Drawer —
re-enable a media query (e.g., import useMedia from 'react-use' and call
useMedia('(min-width: 768px)')) to derive isDesktop; to avoid hydration
warnings, compute isDesktop only on the client (use a mounted flag with
useEffect or useMedia's initialValue tied to typeof window check) or render a
placeholder until mounted so server vs client markup matches; finally ensure the
Drawer branch is returned when isDesktop is false (width < 768px) so the Drawer
renders on mobile.

Comment on lines 21 to 23
className={cn(
'bg-background pointer-events-none relative block h-3 w-3 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:right-1 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 dark:bg-white',
'bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
)}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Split long Thumb class for ≤100 chars.
Tiny readability win; zero functional change.

-        'bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
+        'bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0',
+        'transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className={cn(
'bg-background pointer-events-none relative block h-3 w-3 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:right-1 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 dark:bg-white',
'bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
)}
className={cn(
'bg-background pointer-events-none block h-5 w-5 rounded-full shadow-lg ring-0',
'transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0',
)}
🤖 Prompt for AI Agents
In apps/mail/components/ui/switch.tsx around lines 21 to 23, the long single
string of classes passed to cn for the thumb exceeds 100 chars and should be
split for readability; break the long class string into multiple shorter strings
(either as multiple arguments to cn or an array of strings) so each line is ≤100
chars, keep the exact class tokens and their order unchanged, and ensure the
data-[state=checked] and data-[state=unchecked] utilities remain attached to the
correct token so behavior is preserved.

Comment on lines 31 to 35
className={cn(
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-2 py-px text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
className,
)}
{...props}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

Adopt Tailwind’s renamed utilities and keep lines ≤100 chars.

Use new tokens: rounded-smrounded-xs, outline-noneoutline-hidden, shadow-smshadow-xs. Also split the long class string and avoid transition-all (scope transitions explicitly).

-    className={cn(
-      'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
-      className,
-    )}
+    className={cn(
+      'ring-offset-background focus-visible:ring-ring',
+      'inline-flex items-center justify-center whitespace-nowrap rounded-xs px-3 py-1.5 text-sm font-medium',
+      'transition-[color,box-shadow] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2',
+      'disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs',
+      className,
+    )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className={cn(
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-2 py-px text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
'ring-offset-background focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm',
className,
)}
{...props}
className={cn(
'ring-offset-background focus-visible:ring-ring',
'inline-flex items-center justify-center whitespace-nowrap rounded-xs px-3 py-1.5 text-sm font-medium',
'transition-[color,box-shadow] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs',
className,
)}
{...props}
🤖 Prompt for AI Agents
In apps/mail/components/ui/tabs.tsx around lines 31 to 35, update the long
className string to use Tailwind’s renamed utilities and keep lines under 100
chars: replace rounded-sm with rounded-xs, focus-visible:outline-none with
focus-visible:outline-hidden, and data-[state=active]:shadow-sm with
data-[state=active]:shadow-xs; remove transition-all and instead add explicit
transition classes (e.g., transition-colors transition-shadow duration-150) to
scope transitions; and split the class string into multiple concatenated strings
or array entries passed to cn so no line exceeds 100 characters.

<textarea
className={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename deprecated utility: outline-none → outline-hidden.

Aligns with the new utilities in the guidelines.

Apply this diff:

-          'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
+          'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-base focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
🤖 Prompt for AI Agents
In apps/mail/components/ui/textarea.tsx around line 10, replace the deprecated
utility "outline-none" with the new "outline-hidden" inside the Tailwind class
string; update the class list to use "focus-visible:outline-hidden" (preserving
all other classes and spacing) so the component aligns with the new utility
naming.


const toggleVariants = cva(
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

⚠️ Potential issue

Tailwind v4 rename: use outline-hidden, not outline-none.

Per repo guidelines, outline-noneoutline-hidden. Also, this line likely exceeds the 100-char limit—consider factoring base classes to keep lint green.

Apply:

-  'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',
+  'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',

Nit: if the linter complains about line length, split the base classes into a const base = '...' and pass base to cva, or move some stable pieces into cn() alongside toggleVariants(...).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2',
🤖 Prompt for AI Agents
In apps/mail/components/ui/toggle.tsx around line 10, the Tailwind class list
uses the deprecated outline-none and the string exceeds the 100‑char lint limit;
replace outline-none with outline-hidden and refactor the long class string by
extracting stable base classes into a const (e.g. const base = '...') and pass
that base into cva or combine via cn(toggleVariants(...), base) so the file
stays under the line-length limit and follows repo guidelines.

Comment on lines +15 to 16
outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground',
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Outline variant: drop the border when active for cleaner “on” state.

Prevents the outline border from clashing with the bg-accent on toggle.

-        outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground',
+        outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground data-[state=on]:border-transparent',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground',
},
outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground data-[state=on]:border-transparent',
},
🤖 Prompt for AI Agents
In apps/mail/components/ui/toggle.tsx around lines 15 to 16, the "outline"
variant currently applies a visible border even when the toggle is active;
update the variant so the border is removed in the active/on state (e.g. replace
the active state classes that include 'border' with 'border-0' or
'border-transparent' and keep bg-accent and hover styles), ensuring the toggle
shows a clean filled "on" appearance while hover behavior remains unchanged.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review continued from previous batch...

} from '@/components/ui/dialog';
'use client';

import { type DialogProps } from '@radix-ui/react-dialog';
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Decouple from Radix types; derive from local Dialog instead.

Prevents tight coupling and future Radix changes from leaking through.

-import { type DialogProps } from '@radix-ui/react-dialog';
+// (type-only) derive from local Dialog to avoid coupling to Radix types

-const CommandDialog = ({ children, ...props }: DialogProps) => {
+const CommandDialog = (
+  { children, ...props }: React.ComponentProps<typeof Dialog>,
+) => {

Also applies to: 26-26

🤖 Prompt for AI Agents
In apps/mail/components/ui/command.tsx around lines 3 and 26, remove the import
of DialogProps from '@radix-ui/react-dialog' and instead derive the dialog prop
type from our local Dialog component to avoid coupling to Radix; either import a
DialogProps type exported by the local Dialog module or define the type as
React.ComponentPropsWithoutRef<typeof Dialog> (or the appropriate ComponentProps
helper) and update the function/component signatures to use that local-derived
type.

Comment on lines +29 to 33
@@ -46,12 +39,12 @@ const CommandInput = React.forwardRef<
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Hide the close button inside CommandDialog.

The new DialogContent renders a close button by default; it overlaps the command UI. Turn it off here.

-      <DialogContent className="overflow-hidden p-0 shadow-lg">
+      <DialogContent showCloseButton={false} className="overflow-hidden p-0 shadow-lg">

Hide default close button in CommandDialog via CSS utility. The built-in close button in DialogContent overlaps the command UI—hide it with a Tailwind selector:

-      <DialogContent className="overflow-hidden p-0 shadow-lg">
+      <DialogContent className="overflow-hidden p-0 shadow-lg [&_[data-radix-dialog-close]]:hidden">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<DialogContent className="overflow-hidden p-0 shadow-lg">
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
<DialogContent className="overflow-hidden p-0 shadow-lg [&_[data-radix-dialog-close]]:hidden">
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
{children}
</Command>
</DialogContent>
🤖 Prompt for AI Agents
apps/mail/components/ui/command.tsx around lines 29-33: the DialogContent
contains the built-in close button which overlaps the command UI; update the
DialogContent className to include a Tailwind selector that hides the default
close button element inside it (e.g. add a utility targeting the close button
selector and set it hidden) and ensure other dialog close mechanisms
(ESC/overlay) remain enabled.

Comment on lines +49 to 51
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-md border p-1 shadow-lg',
className,
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix transform-origin CSS var syntax.

Use var(...); current value is invalid.

-      'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-md border p-1 shadow-lg',
+      'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-hidden rounded-md border p-1 shadow-lg',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-md border p-1 shadow-lg',
className,
)}
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-hidden rounded-md border p-1 shadow-lg',
className,
)}
🤖 Prompt for AI Agents
In apps/mail/components/ui/dropdown-menu.tsx around lines 49 to 51, the Tailwind
utility used for transform-origin is currently using the raw CSS variable name
"origin-[--radix-dropdown-menu-content-transform-origin]" which is invalid;
update it to use the CSS var() syntax by replacing that token with
"origin-[var(--radix-dropdown-menu-content-transform-origin)]" so Tailwind emits
a valid transform-origin value.

Comment on lines +66 to 68
className,
)}
{...props}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Also fix transform-origin var on Content.

Align with SubContent and Radix docs.

-        'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md',
+        'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md',
className,
)}
'bg-popover text-popover-foreground \
data-[state=open]:animate-in data-[state=closed]:animate-out \
data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 \
data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 \
data-[side=bottom]:slide-in-from-top-2 \
data-[side=left]:slide-in-from-right-2 \
data-[side=right]:slide-in-from-left-2 \
data-[side=top]:slide-in-from-bottom-2 \
z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] \
min-w-[8rem] \
origin-[var(--radix-dropdown-menu-content-transform-origin)] \
overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md',
className,
)}
🤖 Prompt for AI Agents
In apps/mail/components/ui/dropdown-menu.tsx around lines 66 to 68, the Content
class uses origin-[--radix-dropdown-menu-content-transform-origin] which is
incorrect; change it to use the CSS var() form
origin-[var(--radix-dropdown-menu-content-transform-origin)] so the
transform-origin matches SubContent and Radix docs, and ensure the className
concatenation remains unchanged.

Comment on lines +84 to 87
@@ -94,7 +97,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace outline-none with outline-hidden (item).

-      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
+      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8',
className,
)}
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8',
className,
)}
🤖 Prompt for AI Agents
In apps/mail/components/ui/dropdown-menu.tsx around lines 84 to 87, the class
string currently includes "outline-none" but the review requests using
"outline-hidden (item)"; replace the "outline-none" token with "outline-hidden"
(and annotate or mark it as the item variant if your style system expects the
literal "(item)" marker), ensuring the resulting class list contains
"outline-hidden" in place of "outline-none" so the component uses the correct
outline utility.

Comment on lines +123 to 125
className,
)}
{...props}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace outline-none with outline-hidden (radio item).

-      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
+      'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
🤖 Prompt for AI Agents
In apps/mail/components/ui/dropdown-menu.tsx around lines 123 to 125, the radio
item classes include 'outline-none' but the design system expects
'outline-hidden'; update the class string to replace 'outline-none' with
'outline-hidden' so the radio item uses the correct utility, keeping the rest of
the className merge intact and preserving surrounding spacing and commas.

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 52 files

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

isLoading?: boolean;
loadingText?: string;
}
function Button({
Copy link
Contributor

Choose a reason for hiding this comment

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

Button no longer forwards its ref, which can break focus management and integrations expecting a ref (common in shadcn/radix patterns). Reintroduce React.forwardRef for parity and accessibility.

(Based on your team's feedback about keeping component APIs consistent while migrating to modern imports.)

Prompt for AI agents
Address the following comment on apps/mail/components/ui/button.tsx at line 40:

<comment>Button no longer forwards its ref, which can break focus management and integrations expecting a ref (common in shadcn/radix patterns). Reintroduce React.forwardRef for parity and accessibility.

(Based on your team&#39;s feedback about keeping component APIs consistent while migrating to modern imports.)</comment>

<file context>
@@ -36,50 +37,25 @@ const buttonVariants = cva(
-  isLoading?: boolean;
-  loadingText?: string;
-}
+function Button({
+  className,
+  variant,
</file context>

"@fontsource-variable/geist-mono": "5.2.6",
"@hookform/resolvers": "4.1.2",
"@intercom/messenger-js-sdk": "0.0.14",
"@radix-ui/react-accordion": "1.2.12",
Copy link
Contributor

Choose a reason for hiding this comment

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

Multiple @radix-ui/react-* dependencies were added but no code references to "@radix-ui" exist in the repo, indicating these are currently unused and add install size and maintenance surface. Consider removing unused deps or include the related code changes that use them.

(Based on your PR’s goal of migrating to modern @radix-ui imports and removing the legacy package, I verified actual usage across the codebase.)

Prompt for AI agents
Address the following comment on apps/mail/package.json at line 28:

<comment>Multiple @radix-ui/react-* dependencies were added but no code references to &quot;@radix-ui&quot; exist in the repo, indicating these are currently unused and add install size and maintenance surface. Consider removing unused deps or include the related code changes that use them.

(Based on your PR’s goal of migrating to modern @radix-ui imports and removing the legacy package, I verified actual usage across the codebase.)</comment>

<file context>
@@ -25,6 +25,28 @@
     &quot;@fontsource-variable/geist-mono&quot;: &quot;5.2.6&quot;,
     &quot;@hookform/resolvers&quot;: &quot;4.1.2&quot;,
     &quot;@intercom/messenger-js-sdk&quot;: &quot;0.0.14&quot;,
+    &quot;@radix-ui/react-accordion&quot;: &quot;1.2.12&quot;,
+    &quot;@radix-ui/react-avatar&quot;: &quot;1.1.10&quot;,
+    &quot;@radix-ui/react-checkbox&quot;: &quot;1.3.3&quot;,
</file context>

className={cn(
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
Copy link
Contributor

Choose a reason for hiding this comment

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

aria-invalid sets a ring color without a ring width, so the error ring won’t be visible. Add a ring size (e.g., aria-invalid:ring-[3px]) so the invalid state is apparent.

(Based on your team's feedback about ensuring consistent focus rings and a11y across components.)

Prompt for AI agents
Address the following comment on apps/mail/components/ui/input.tsx at line 13:

<comment>aria-invalid sets a ring color without a ring width, so the error ring won’t be visible. Add a ring size (e.g., aria-invalid:ring-[3px]) so the invalid state is apparent.

(Based on your team&#39;s feedback about ensuring consistent focus rings and a11y across components.)</comment>

<file context>
@@ -2,21 +2,20 @@ import * as React from &#39;react&#39;;
+      className={cn(
+        &#39;file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm&#39;,
+        &#39;focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]&#39;,
+        &#39;aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive&#39;,
+        className,
+      )}
</file context>
Suggested change
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
'aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',

},
);
Input.displayName = 'Input';
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ref is no longer forwarded to the underlying input, which will break react-hook-form registration and any ref-based focus/validation. Restore React.forwardRef and pass ref to the native to maintain behavior.

(Based on your team's goal of keeping all existing functionality intact during the migration.)

Prompt for AI agents
Address the following comment on apps/mail/components/ui/input.tsx at line 5:

<comment>Ref is no longer forwarded to the underlying input, which will break react-hook-form registration and any ref-based focus/validation. Restore React.forwardRef and pass ref to the native &lt;input&gt; to maintain behavior.

(Based on your team&#39;s goal of keeping all existing functionality intact during the migration.)</comment>

<file context>
@@ -2,21 +2,20 @@ import * as React from &#39;react&#39;;
-  },
-);
-Input.displayName = &#39;Input&#39;;
+function Input({ className, type, ...props }: React.ComponentProps&lt;&#39;input&#39;&gt;) {
+  return (
+    &lt;input
</file context>

},
},
);
function SheetContent({
Copy link
Contributor

Choose a reason for hiding this comment

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

Ref is not forwarded for SheetContent; use React.forwardRef to preserve ref support and match Radix/shadcn conventions.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/sheet.tsx at line 41:

<comment>Ref is not forwarded for SheetContent; use React.forwardRef to preserve ref support and match Radix/shadcn conventions.</comment>

<file context>
@@ -1,110 +1,125 @@
-    },
-  },
-);
+function SheetContent({
+  className,
+  children,
</file context>

<NavigationMenuPrimitive.Viewport
className={cn(
'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow md:w-(--radix-navigation-menu-viewport-width)',
'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]',
Copy link
Contributor

Choose a reason for hiding this comment

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

origin-top-center is not a valid Tailwind utility by default; the transform-origin will be ignored. Use origin-top (equivalent to top center) or define a custom transformOrigin in Tailwind config.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/navigation-menu.tsx at line 84:

<comment>origin-top-center is not a valid Tailwind utility by default; the transform-origin will be ignored. Use origin-top (equivalent to top center) or define a custom transformOrigin in Tailwind config.</comment>

<file context>
@@ -80,7 +81,7 @@ const NavigationMenuViewport = React.forwardRef&lt;
     &lt;NavigationMenuPrimitive.Viewport
       className={cn(
-        &#39;origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-md border shadow md:w-(--radix-navigation-menu-viewport-width)&#39;,
+        &#39;origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]&#39;,
         className,
       )}
</file context>
Suggested change
'origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]',
'origin-top bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg md:w-[var(--radix-navigation-menu-viewport-width)]',

<TooltipContent>Prompts</TooltipContent>
</Tooltip>
<DialogContent className="max-w-(--breakpoint-lg)" showOverlay={true}>
<DialogContent className="max-w-(--breakpoint-lg)">
Copy link
Contributor

Choose a reason for hiding this comment

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

Invalid Tailwind class syntax for arbitrary value; use square brackets with var() so the max-width is applied.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/prompts-dialog.tsx at line 135:

<comment>Invalid Tailwind class syntax for arbitrary value; use square brackets with var() so the max-width is applied.</comment>

<file context>
@@ -132,7 +132,7 @@ export function PromptsDialog() {
           &lt;TooltipContent&gt;Prompts&lt;/TooltipContent&gt;
         &lt;/Tooltip&gt;
-        &lt;DialogContent className=&quot;max-w-(--breakpoint-lg)&quot; showOverlay={true}&gt;
+        &lt;DialogContent className=&quot;max-w-(--breakpoint-lg)&quot;&gt;
           &lt;DialogHeader&gt;
             &lt;DialogTitle className=&quot;flex items-center gap-2&quot;&gt;
</file context>
Suggested change
<DialogContent className="max-w-(--breakpoint-lg)">
<DialogContent className="max-w-[var(--breakpoint-lg)]">

ref={ref}
className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border shadow-md',
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md',
Copy link
Contributor

Choose a reason for hiding this comment

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

Tailwind arbitrary values using CSS variables should be wrapped in var(...); use max-h-[var(--radix-select-content-available-height)] and origin-[var(--radix-select-content-transform-origin)] to ensure valid CSS.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/select.tsx at line 71:

<comment>Tailwind arbitrary values using CSS variables should be wrapped in var(...); use max-h-[var(--radix-select-content-available-height)] and origin-[var(--radix-select-content-transform-origin)] to ensure valid CSS.</comment>

<file context>
@@ -63,7 +68,7 @@ const SelectContent = React.forwardRef&lt;
       ref={ref}
       className={cn(
-        &#39;bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border shadow-md&#39;,
+        &#39;bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md&#39;,
         position === &#39;popper&#39; &amp;&amp;
           &#39;data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1&#39;,
</file context>
Suggested change
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border shadow-md',
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-[var(--radix-select-content-available-height)] min-w-[8rem] origin-[var(--radix-select-content-transform-origin)] overflow-y-auto overflow-x-hidden rounded-md border shadow-md',

orientation = 'horizontal',
decorative = true,
...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Props type includes ref even though this component does not support refs; use ComponentPropsWithoutRef to align types and avoid confusion.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/separator.tsx at line 13:

<comment>Props type includes ref even though this component does not support refs; use ComponentPropsWithoutRef to align types and avoid confusion.</comment>

<file context>
@@ -1,24 +1,28 @@
+  orientation = &#39;horizontal&#39;,
+  decorative = true,
+  ...props
+}: React.ComponentProps&lt;typeof SeparatorPrimitive.Root&gt;) {
+  return (
+    &lt;SeparatorPrimitive.Root
</file context>
Suggested change
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
}: React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>) {

orientation={orientation}
className={cn(
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px',
className,
Copy link
Contributor

Choose a reason for hiding this comment

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

data-[orientation] size classes have higher specificity than consumer classes (e.g., h-6), blocking overrides; vertical separators will ignore custom heights like in create/toolbar.

Prompt for AI agents
Address the following comment on apps/mail/components/ui/separator.tsx at line 20:

<comment>data-[orientation] size classes have higher specificity than consumer classes (e.g., h-6), blocking overrides; vertical separators will ignore custom heights like in create/toolbar.</comment>

<file context>
@@ -1,24 +1,28 @@
+      decorative={decorative}
+      orientation={orientation}
+      className={cn(
+        &#39;bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px&#39;,
+        className,
+      )}
</file context>

@ArjunCodess ArjunCodess changed the title Migrate shadcn/ui components to latest version with modern imports Update shadcn/ui components to latest version and fix dialog backdrop blur Sep 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

design Improvements & changes to design & UX High Priority High Priority Work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Background blur inconsistency between email compose modal and search dropdown

1 participant