Skip to content

Toast PoC#1285

Open
kotAPI wants to merge 6 commits intomainfrom
kotapi/toast-poc-try-1
Open

Toast PoC#1285
kotAPI wants to merge 6 commits intomainfrom
kotapi/toast-poc-try-1

Conversation

@kotAPI
Copy link
Collaborator

@kotAPI kotAPI commented Aug 2, 2025

Summary by CodeRabbit

  • New Features

    • Added a toast notification system with provider, root, and animated toaster.
    • Centralized API for triggering info, success, and error toasts.
    • Supports stacking, animations, auto-dismiss with pause/resume on hover, and manual dismissal.
    • Storybook examples demonstrating toast usage.
  • Chores

    • Added framer-motion as a development dependency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 2, 2025

Walkthrough

Adds a new toast notification system: UI components (Toast, ToastProvider, ToastRoot, Toaster), a toast store with subscription/timer controls, a toast API, a Storybook example, and adds framer-motion to devDependencies.

Changes

Cohort / File(s) Change Summary
Dependency
package.json
Added devDependency framer-motion (^12.23.12).
Toast entry
src/components/ui/Toast/Toast.tsx
New default Toast component that warns against direct use and re-exports Toast.Provider (ToastProvider), Toast.Toaster (Toaster), and named toast API.
Store
src/components/ui/Toast/contexts/toastStore.tsx
New toast store: Toast type, subscribe, addToast, removeToast, pauseAllToasts, resumeAllToasts with timers and listener notifications.
Provider
src/components/ui/Toast/fragments/ToastProvider.tsx
New client-side ToastProvider component that wraps children.
Root
src/components/ui/Toast/fragments/ToastRoot.tsx
New ToastRoot component rendering a structural Primitive.div wrapper.
Toaster UI
src/components/ui/Toast/fragments/ToastToaster.tsx
New Toaster component: subscribes to store, animates stacked toasts with framer-motion, supports hover pause/resume, positioning, and dismissal.
API
src/components/ui/Toast/toastAPI.ts
New toast object with create, success, error, info methods calling store addToast.
Storybook
src/components/ui/Toast/stories/Toast.stories.tsx
New Storybook story ToastExample demonstrating Toast.Provider, Toast.Toaster, and toast.success usage.

Sequence Diagram(s)

sequenceDiagram
  participant UI as User Interface
  participant ToastAPI as toastAPI
  participant Store as ToastStore
  participant Toaster as Toaster

  UI->>ToastAPI: toast.success("Message")
  ToastAPI->>Store: addToast({ type: "success", message: "Message" })
  Store->>Toaster: notify subscribers (new toast list)
  Toaster->>UI: render animated toast
  UI-->>Store: (user clicks close) removeToast(id)
  Store->>Toaster: notify subscribers (toast removed)
  Toaster->>UI: remove toast from display
  Toaster->>Store: on hover -> pauseAllToasts()
  Toaster-->>Store: on leave -> resumeAllToasts()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • GoldGroove06

Poem

🥕
I nibble on code and nudge a small bell,
Toasts pop and dance where the notifications dwell.
Stack, pause, and close—watch them gracefully glide,
A rabbit’s quick cheer for UI far and wide.
🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kotapi/toast-poc-try-1

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

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: 7

🔭 Outside diff range comments (1)
package.json (1)

7-45: Missing Toast component export in package.json.

The new Toast component is not included in the exports section, but other UI components are exported. This inconsistency means consumers won't be able to import the Toast component.

Add the Toast export to the exports section:

    "./ToggleGroup": "./dist/components/ToggleGroup.js",
+   "./Toast": "./dist/components/Toast.js",
    "./Tooltip": "./dist/components/Tooltip.js",
🧹 Nitpick comments (6)
src/components/ui/Toast/stories/Toast.stories.tsx (4)

6-6: Remove commented import.

The commented import should be removed entirely rather than left as a comment for cleaner code.

-// import { TabProps } from '../fragments/TabContent'; // Removed - not exported

24-26: Remove unused ArrowIcon component.

The ArrowIcon component is defined but never used in the story. Consider removing it to keep the code clean.

-const ArrowIcon = ({ className }: { className: string }) => {
-    return <svg className={className} width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.14645 3.14645C8.34171 2.95118 8.65829 2.95118 8.85355 3.14645L12.8536 7.14645C13.0488 7.34171 13.0488 7.65829 12.8536 7.85355L8.85355 11.8536C8.65829 12.0488 8.34171 12.0488 8.14645 11.8536C7.95118 11.6583 7.95118 11.3417 8.14645 11.1464L11.2929 8H2.5C2.22386 8 2 7.77614 2 7.5C2 7.22386 2.22386 7 2.5 7H11.2929L8.14645 3.85355C7.95118 3.65829 7.95118 3.34171 8.14645 3.14645Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>;
-};

32-37: Remove unused state and handler.

The activeTab state and handleTabChange function are unused and appear to be leftover from a Tabs component template. Remove them to clean up the story.

-    const [activeTab, setActiveTab] = useState('meteora');
-
-    const handleTabChange = (value: string) => {
-        // console.log('tab', value);
-        setActiveTab(value);
-    };

47-51: Remove commented-out code.

The commented-out Toast.Root and Toast.Title code should be removed unless it's needed for immediate future development.

-                    {/* <Toast.Root>
-                        <Toast.Title>
-                            <h1>Hello</h1>
-                        </Toast.Title>
-                    </Toast.Root> */}
src/components/ui/Toast/fragments/ToastToaster.tsx (1)

30-30: Consider making toast height configurable.

The hardcoded 72px toast height could become incorrect if toast styling changes. Consider making this configurable or calculating it dynamically.

-    const expandedHeight = toasts.length * 72; // (72px is approx toast height + margin)
+    const toastHeight = 72; // Could be made configurable via props
+    const expandedHeight = toasts.length * toastHeight;

And update the component interface:

 interface ToasterProps {
     visibleToasts?: number;
     position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
+    toastHeight?: number;
 }

Also applies to: 37-37

src/components/ui/Toast/contexts/toastStore.tsx (1)

49-50: Improve readability of remaining time calculation.

The remaining time calculation is correct but could be more readable. Consider extracting it to a helper function or adding clearer variable names.

-            toast.remainingTime =
-          (toast.remainingTime ?? 3000) - (Date.now() - (toast.startTime ?? Date.now()));
+            const elapsedTime = Date.now() - (toast.startTime ?? Date.now());
+            toast.remainingTime = Math.max(0, (toast.remainingTime ?? 3000) - elapsedTime);

Also consider adding a minimum remaining time check to prevent negative values.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d4bf11 and 9327851.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • package.json (2 hunks)
  • src/components/ui/Toast/Toast.tsx (1 hunks)
  • src/components/ui/Toast/contexts/toastStore.tsx (1 hunks)
  • src/components/ui/Toast/fragments/ToastProvider.tsx (1 hunks)
  • src/components/ui/Toast/fragments/ToastRoot.tsx (1 hunks)
  • src/components/ui/Toast/fragments/ToastToaster.tsx (1 hunks)
  • src/components/ui/Toast/stories/Toast.stories.tsx (1 hunks)
  • src/components/ui/Toast/toastAPI.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: the `dropdown.trigger` component is customizable and needs to be used with `dropdown.root`....
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.tsx:0-0
Timestamp: 2024-12-12T08:22:59.375Z
Learning: The `Dropdown.Trigger` component is customizable and needs to be used with `Dropdown.Root`.

Applied to files:

  • src/components/ui/Toast/fragments/ToastRoot.tsx
📚 Learning: ensure to verify existing aria attributes in components before suggesting additions during code revi...
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.

Applied to files:

  • src/components/ui/Toast/stories/Toast.stories.tsx
📚 Learning: in the storybook files, variations in the title prefixes like 'components/', 'wip/', 'tools/', and '...
Learnt from: kotAPI
PR: rad-ui/ui#484
File: src/components/ui/Kbd/stories/Kbd.stories.js:6-6
Timestamp: 2024-10-27T07:20:10.457Z
Learning: In the Storybook files, variations in the title prefixes like 'Components/', 'WIP/', 'Tools/', and 'Examples/' are intentional. These naming patterns are acceptable and should not be flagged as inconsistencies in future code reviews.

Applied to files:

  • src/components/ui/Toast/stories/Toast.stories.tsx
🧬 Code Graph Analysis (3)
src/components/ui/Toast/toastAPI.ts (1)
src/components/ui/Toast/contexts/toastStore.tsx (1)
  • addToast (20-36)
src/components/ui/Toast/stories/Toast.stories.tsx (2)
src/components/ui/Toast/contexts/toastStore.tsx (1)
  • Toast (1-9)
src/components/ui/Toast/toastAPI.ts (1)
  • toast (5-12)
src/components/ui/Toast/contexts/toastStore.tsx (2)
src/components/ui/Toast/Toast.tsx (1)
  • toast (15-15)
src/components/ui/Toast/toastAPI.ts (1)
  • toast (5-12)
⏰ 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: build
🔇 Additional comments (9)
package.json (1)

122-122: LGTM! framer-motion addition supports toast animations.

The addition of framer-motion as a devDependency is appropriate for the toast notification system's animation requirements.

src/components/ui/Toast/toastAPI.ts (1)

5-12: LGTM! Clean and consistent toast API design.

The API provides both a generic create method and convenient type-specific methods (success, error, info). The implementation correctly delegates to the addToast function from the store, maintaining good separation of concerns.

src/components/ui/Toast/Toast.tsx (1)

12-17: LGTM! Good component composition pattern.

The static property attachment pattern and separate export of the toast API follows established UI library conventions and provides a clean interface for consumers.

src/components/ui/Toast/stories/Toast.stories.tsx (1)

41-46: LGTM! Good demonstration of toast architecture.

The story correctly demonstrates the toast system with proper component placement - Toast.Toaster rendered globally and Toast.Provider wrapping the interaction area. The usage of toast.success API is appropriate.

Also applies to: 52-52

src/components/ui/Toast/fragments/ToastToaster.tsx (3)

20-27: LGTM! Comprehensive position support.

The position classes cover all common toast placement options with correct Tailwind CSS classes.


49-77: LGTM! Excellent animation and stacking implementation.

The animation logic is well-crafted with smooth transitions, proper stacking effects using scale/opacity, and correct z-index layering. The compressed vs expanded offset calculation creates a polished user experience.


79-98: LGTM! Clean toast rendering with proper styling.

The toast rendering implementation is well-structured with appropriate color coding by type, clean layout, and functional close button integration.

src/components/ui/Toast/contexts/toastStore.tsx (2)

1-9: LGTM! Comprehensive Toast type definition.

The Toast type is well-structured with appropriate optional fields and proper typing for all properties.


11-18: LGTM! Clean observer pattern implementation.

The subscription mechanism is well-implemented with immediate state delivery and proper cleanup via the returned unsubscribe function.


export function addToast(toast: Omit<Toast, 'id'>) {
const newToast: Toast = {
id: crypto.randomUUID(),
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

Verify browser compatibility for crypto.randomUUID().

crypto.randomUUID() requires modern browser support. Consider adding a fallback for older browsers or document the minimum browser requirements.


🏁 Script executed:

#!/bin/bash
# Check if there are any existing polyfills or compatibility considerations in the codebase
rg -i "crypto\.randomUUID|uuid|polyfill" --type js --type ts -A 2 -B 2

Length of output: 515


Add a fallback for crypto.randomUUID()

crypto.randomUUID() isn’t available in some older browsers (e.g. Safari < 15.4, IE). To prevent runtime errors, provide a fallback or clearly document your minimum supported browsers.

• File: src/components/ui/Toast/contexts/toastStore.tsx (around line 22)
• Suggested change:

// import or implement a fallback generator:
import { v4 as uuidV4 } from 'uuid'; // or your own helper

function generateId() {
  return crypto.randomUUID?.() ?? uuidV4();
}

export function addToast(toast: Omit<Toast, 'id'>) {
  const newToast: Toast = {
    id: generateId(),
    duration: toast.duration ?? 3000,
    startTime: Date.now(),
    // …
  };
  // …
}

– Or, if you prefer not to bundle a UUID library, write a small fallback using Math.random().
– Alternatively, in your project’s README, call out that you require only modern browsers supporting crypto.randomUUID().

🤖 Prompt for AI Agents
In src/components/ui/Toast/contexts/toastStore.tsx at line 22, the use of
crypto.randomUUID() lacks a fallback for browsers that do not support it,
causing potential runtime errors. Fix this by creating a helper function that
returns crypto.randomUUID() if available, otherwise falls back to a UUID
generator like the 'uuid' library's v4 function or a custom Math.random()-based
fallback. Replace direct calls to crypto.randomUUID() with this helper to ensure
compatibility across browsers.

Comment on lines +38 to +41
export function removeToast(id: string) {
toasts = toasts.filter((t) => t.id !== id);
listeners.forEach((l) => l(toasts));
}
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

Clear timeout when removing toast to prevent memory leaks.

The removeToast function should clear the timeout of the removed toast to prevent memory leaks and unnecessary timer callbacks.

 export function removeToast(id: string) {
+    const toastToRemove = toasts.find(t => t.id === id);
+    if (toastToRemove?.timeoutId) {
+        clearTimeout(toastToRemove.timeoutId);
+    }
     toasts = toasts.filter((t) => t.id !== id);
     listeners.forEach((l) => l(toasts));
 }
📝 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
export function removeToast(id: string) {
toasts = toasts.filter((t) => t.id !== id);
listeners.forEach((l) => l(toasts));
}
export function removeToast(id: string) {
const toastToRemove = toasts.find(t => t.id === id);
if (toastToRemove?.timeoutId) {
clearTimeout(toastToRemove.timeoutId);
}
toasts = toasts.filter((t) => t.id !== id);
listeners.forEach((l) => l(toasts));
}
🤖 Prompt for AI Agents
In src/components/ui/Toast/contexts/toastStore.tsx around lines 38 to 41, the
removeToast function currently filters out the toast by id but does not clear
its associated timeout, which can cause memory leaks. Modify the function to
first find the toast with the given id, clear its timeout using clearTimeout,
and then proceed to filter it out and notify listeners. This ensures that any
active timers for the removed toast are properly cancelled.

Comment on lines +4 to +6
const ToastProvider = ({ children }: { children: React.ReactNode }) => {
return <div>{children}</div>;
};
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

Misleading component name - ToastProvider doesn't provide context.

The component name suggests it's a React context provider, but it only wraps children in a div without providing any context or state management. This could confuse developers expecting typical provider patterns.

Consider either:

  1. Implementing actual provider functionality with React context
  2. Renaming to something more accurate like ToastWrapper or ToastContainer
-const ToastProvider = ({ children }: { children: React.ReactNode }) => {
-    return <div>{children}</div>;
-};
+const ToastWrapper = ({ children }: { children: React.ReactNode }) => {
+    return <div>{children}</div>;
+};
📝 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
const ToastProvider = ({ children }: { children: React.ReactNode }) => {
return <div>{children}</div>;
};
const ToastWrapper = ({ children }: { children: React.ReactNode }) => {
return <div>{children}</div>;
};
🤖 Prompt for AI Agents
In src/components/ui/Toast/fragments/ToastProvider.tsx around lines 4 to 6, the
component named ToastProvider does not provide any React context or state
management, which is misleading. To fix this, either implement the expected
React context provider functionality inside this component or rename the
component to a more accurate name such as ToastWrapper or ToastContainer to
reflect its actual behavior of just wrapping children in a div.

Comment on lines +7 to +9
const ToastRoot = ({ children, className, asChild }: { children: React.ReactNode, className: string, asChild: boolean }) => {
return <Primitive.div className={className}>{children}</Primitive.div>;
};
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

Unused asChild prop and required className.

Two issues with the component props:

  1. The asChild prop is accepted but never used in the implementation
  2. className is required but should likely be optional

Apply this diff to fix the issues:

-const ToastRoot = ({ children, className, asChild }: { children: React.ReactNode, className: string, asChild: boolean }) => {
-    return <Primitive.div className={className}>{children}</Primitive.div>;
+const ToastRoot = ({ children, className, asChild }: { children: React.ReactNode, className?: string, asChild?: boolean }) => {
+    return <Primitive.div className={className}>{children}</Primitive.div>;

Alternatively, if asChild functionality is intended, implement the pattern or remove the unused prop entirely.

🤖 Prompt for AI Agents
In src/components/ui/Toast/fragments/ToastRoot.tsx around lines 7 to 9, the
asChild prop is accepted but not used, and className is required though it
should be optional. Fix this by either removing the asChild prop if not needed
or implementing its intended functionality, and make className optional by
updating the prop type accordingly.

import ToastRoot from '../fragments/ToastRoot';
import { AnimatePresence, motion } from 'framer-motion';

export function Toaster({ visibleToasts = 3, position = 'bottom-right' }) {
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

Add TypeScript interface for props.

The component lacks proper TypeScript typing for its props, which reduces type safety and developer experience.

+interface ToasterProps {
+    visibleToasts?: number;
+    position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
+}
+
-export function Toaster({ visibleToasts = 3, position = 'bottom-right' }) {
+export function Toaster({ visibleToasts = 3, position = 'bottom-right' }: ToasterProps) {
📝 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
export function Toaster({ visibleToasts = 3, position = 'bottom-right' }) {
interface ToasterProps {
visibleToasts?: number;
position?: 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center';
}
export function Toaster({ visibleToasts = 3, position = 'bottom-right' }: ToasterProps) {
🤖 Prompt for AI Agents
In src/components/ui/Toast/fragments/ToastToaster.tsx at line 14, the Toaster
component's props are not typed, reducing type safety. Define a TypeScript
interface for the props specifying the types of visibleToasts (number) and
position (string with allowed values if applicable), then update the function
signature to use this interface for its props.

Comment on lines +91 to +96
<button
className="ml-2 text-sm opacity-70 hover:opacity-100"
onClick={() => removeToast(toast.id)}
>
</button>
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

Add accessibility features for better user experience.

The toast component lacks important accessibility features. Consider adding ARIA labels, keyboard support, and screen reader announcements.

                                    <button
+                                        aria-label="Close notification"
                                        className="ml-2 text-sm opacity-70 hover:opacity-100"
                                        onClick={() => removeToast(toast.id)}
+                                        onKeyDown={(e) => {
+                                            if (e.key === 'Enter' || e.key === ' ') {
+                                                e.preventDefault();
+                                                removeToast(toast.id);
+                                            }
+                                        }}
                                    >
                    ✕
                                    </button>

Also consider adding role="alert" or role="status" to the toast container and aria-live regions for screen reader announcements.

📝 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
<button
className="ml-2 text-sm opacity-70 hover:opacity-100"
onClick={() => removeToast(toast.id)}
>
</button>
<button
aria-label="Close notification"
className="ml-2 text-sm opacity-70 hover:opacity-100"
onClick={() => removeToast(toast.id)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
removeToast(toast.id);
}
}}
>
</button>
🤖 Prompt for AI Agents
In src/components/ui/Toast/fragments/ToastToaster.tsx around lines 91 to 96, the
toast component's close button lacks accessibility features. Add an aria-label
attribute to the button describing its action, ensure it is keyboard accessible
by default, and add role="alert" or role="status" with aria-live attributes to
the toast container to enable screen reader announcements for better
accessibility.

Comment on lines +7 to +10
const Toast = () => {
console.warn('Direct usage of Toast is not supported. Please use Toast.Root etc. instead.');
return null;
};
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

Inconsistent warning message - Toast.Root not attached.

The warning message mentions using "Toast.Root" but ToastRoot is not attached as a static property on the Toast component. Only Toast.Provider and Toast.Toaster are available.

Either attach ToastRoot as a static property:

+import ToastRoot from './fragments/ToastRoot';

 Toast.Provider = ToastProvider;
 Toast.Toaster = Toaster;
+Toast.Root = ToastRoot;

Or update the warning message to reflect actual available components:

-    console.warn('Direct usage of Toast is not supported. Please use Toast.Root etc. instead.');
+    console.warn('Direct usage of Toast is not supported. Please use Toast.Provider and Toast.Toaster instead.');

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/ui/Toast/Toast.tsx around lines 7 to 10, the warning message
references "Toast.Root" which is not attached as a static property on the Toast
component, causing inconsistency. To fix this, either attach ToastRoot as a
static property on Toast alongside Toast.Provider and Toast.Toaster, or update
the warning message to mention only the available components like Toast.Provider
and Toast.Toaster to accurately guide users.

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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
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 9327851 and 7ff8095.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json (1 hunks)
⏰ 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: build

"eslint-plugin-promise": "6.6.0",
"eslint-plugin-react": "7.34.0",
"eslint-plugin-react-hooks": "4.6.0",
"framer-motion": "^12.23.12",
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

framer-motion should not be only a devDependency if used by published components

If Toast components import framer-motion at runtime, consumers of @radui/ui won’t get it installed because devDependencies aren’t installed for dependents. This will cause runtime/module resolution errors for consumers. Prefer declaring it as a peerDependency (and keep as a devDependency for local development), or explicitly bundle it.

Action:

  • Recommended: Move framer-motion to peerDependencies with a compatible range and keep it in devDependencies for development.
  • Alternative: If intentionally bundled, ensure Rollup does not mark it external and confirm bundle size implications.

To confirm usage and externalization, run:

Apply within this hunk (remove from devDependencies):

-    "framer-motion": "^12.23.12",

Then add to peerDependencies (outside this hunk; illustrative snippet):

{
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "framer-motion": "^12.23.12"
  }
}

Optionally pin a broader peer range to reduce churn:

"framer-motion": "^12.0.0"

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Verify code imports framer-motion =="
rg -nP -C2 --type=ts --type=tsx '\bfrom\s+[\'"]framer-motion[\'"]' || true

echo
echo "== Check Rollup externals for framer-motion =="
fd -a 'rollup*.cjs' | xargs -I{} sh -c 'echo "--- {} ---"; rg -nP -C2 "external|exclude|globals" "{}" || true'

Length of output: 225


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Repo root: $(pwd) =="
echo "== Find all occurrences of 'framer-motion' =="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden -S --glob '!node_modules' 'framer-motion' || true
else
  grep -RIn --exclude-dir=node_modules --exclude-dir=dist 'framer-motion' . || true
fi

echo
echo "== package.json dependency sections =="
if [ -f package.json ]; then
  if command -v jq >/dev/null 2>&1; then
    jq '{name,version,dependencies:.dependencies,devDependencies:.devDependencies,peerDependencies:.peerDependencies}' package.json || true
  else
    echo "jq not available — showing lines around occurrences in package.json"
    rg -n --hidden -C2 'framer-motion' package.json || true
    rg -n --hidden '"(dependencies|devDependencies|peerDependencies)"' package.json || true
  fi
else
  echo "package.json not found"
fi

echo
echo "== Detect bundler/tooling mentions (rollup / vite / tsup / esbuild / webpack) =="
for t in rollup vite tsup esbuild webpack; do
  echo "---- $t ----"
  if command -v rg >/dev/null 2>&1; then
    rg -n --hidden -S --glob '!node_modules' "$t" || true
  else
    grep -RIn --exclude-dir=node_modules --exclude-dir=dist "$t" . || true
  fi
done

echo
echo "== Search for bundler 'external' / 'externals' / 'globals' / 'exclude' in configs =="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden -S --glob '!node_modules' '\bexternal\b|\bexternals\b|\bglobals\b|\bexclude\b' || true
else
  grep -RIn --exclude-dir=node_modules --exclude-dir=dist -E '\bexternal\b|\bexternals\b|\bglobals\b|\bexclude\b' . || true
fi

Length of output: 68483


framer-motion must be a peerDependency (or be explicitly bundled)

Short reason: framer-motion is imported by a runtime component but is only listed in devDependencies — consumers will break unless it's bundled into the published package.

Files I verified:

  • package.json — framer-motion is in devDependencies ("framer-motion": "^12.23.12").
  • src/components/ui/Toast/fragments/ToastToaster.tsx — runtime import: import { AnimatePresence, motion } from 'framer-motion';
  • rollup.config.cjs / build-components.cjs — externals list only react/react-dom/react/jsx-runtime (framer-motion is not listed as external, so if you do not bundle it intentionally, it will be missing for consumers).

Action (recommended):

  • Move framer-motion to peerDependencies with a compatible range and keep it in devDependencies for local dev/testing.

Suggested changes:

Remove from devDependencies:

-    "framer-motion": "^12.23.12",

Add to peerDependencies (example):

"peerDependencies": {
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "framer-motion": "^12.23.12"
}

Alternative: if you intentionally bundle framer-motion into the distributed build, ensure your Rollup/esbuild config does not mark it external and verify bundle size and licensing implications.

🤖 Prompt for AI Agents
In package.json around line 93, framer-motion is currently only in
devDependencies while runtime components import it; move "framer-motion" into
peerDependencies with a compatible semver range (e.g. same range as
devDependency) and keep it in devDependencies for local testing, or
alternatively change the build config to bundle framer-motion (remove it from
externals in rollup/build config) if you intend to ship it; update package.json
accordingly and ensure consumers are informed via peerDependencies so installs
won't break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant