Skip to content

feat: add shiny text, confetti, shipfox loader components#236

Merged
kylengn merged 6 commits intomainfrom
feat/add-effect-components
Dec 15, 2025
Merged

feat: add shiny text, confetti, shipfox loader components#236
kylengn merged 6 commits intomainfrom
feat/add-effect-components

Conversation

@kylengn
Copy link
Contributor

@kylengn kylengn commented Dec 15, 2025

Summary by CodeRabbit

  • New Features

    • Added shiny text animation effect with adjustable speed
    • Introduced confetti celebration animations triggered via button clicks
    • Enhanced loading state visualization with animated loader
    • Added automatic celebration animation on task completion in modals
  • Documentation

    • Added Storybook examples demonstrating confetti animations with default and custom configuration options

✏️ Tip: You can customize this high-level summary in your review settings.

@kylengn kylengn self-assigned this Dec 15, 2025
@kylengn kylengn requested a review from noe-charmet as a code owner December 15, 2025 03:55
Copilot AI review requested due to automatic review settings December 15, 2025 03:55
@vercel
Copy link

vercel bot commented Dec 15, 2025

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

Project Deployment Review Updated (UTC)
react-ui Ready Ready Preview, Comment Dec 15, 2025 5:14am

@coderabbitai
Copy link

coderabbitai bot commented Dec 15, 2025

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.

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.

Walkthrough

This PR introduces two new decorative UI components to the React library: ShinyText (CSS-based text animation with gradient fill and shine effect) and Confetti (canvas-based celebration component with context-based API). Supporting dependencies are added, and both components are integrated into existing stories and components for demonstrating their usage.

Changes

Cohort / File(s) Summary
CSS Animation Styling
libs/react/ui/index.css
Adds .shiny-text class with gradient background and shine animation (@keyframes shine) that shifts background position from 100% to -100%, plus .shiny-text.disabled to disable animation
ShinyText Component
libs/react/ui/src/components/shiny-text/shiny-text.tsx, libs/react/ui/src/components/shiny-text/index.ts
New React component with props for text, optional disabled state, speed control, and className; derives animation duration from speed prop and renders div with shiny-text styling
Confetti Component
libs/react/ui/src/components/confetti/confetti.tsx, libs/react/ui/src/components/confetti/confetti.stories.tsx, libs/react/ui/src/components/confetti/index.ts
New canvas-based confetti module using canvas-confetti library; exports Confetti component (with imperative ref API via context), ConfettiButton helper, and ConfettiProps/ConfettiButtonProps types; includes default stories for basic and custom options usage
Dependencies
libs/react/ui/package.json
Adds runtime dependencies: canvas-confetti (^1.9.4), shipfox-loader-react (^1.0.1); adds devDependency: @types/canvas-confetti (^1.9.0)
Public API Exports
libs/react/ui/src/components/index.ts, libs/react/ui/src/index.ts
Exposes Confetti and ShinyText modules via barrel exports in components/index.ts; re-exports ShipfoxLoader from shipfox-loader-react in main index.ts
Component Integrations
libs/react/ui/src/components/code-block/code-block-footer.tsx, libs/react/ui/src/components/modal/modal.stories.tsx
CodeBlockFooter replaces static shipfox icon with ShipfoxLoader component when running; Modal GithubActions story adds Confetti integration with state-driven timing (running → done after 3s) and triggers confetti.fire() on completion

Sequence Diagram

sequenceDiagram
    participant User
    participant ConfettiButton
    participant ConfettiContext
    participant Confetti
    participant CanvasConfetti as canvas-confetti lib

    User->>ConfettiButton: Click button
    ConfettiButton->>ConfettiButton: Compute button position & origin
    ConfettiButton->>ConfettiContext: Call context.fire(options)
    ConfettiContext->>Confetti: Invoke ref.current.fire(mergedOptions)
    Confetti->>Confetti: Merge call options with component options
    Confetti->>CanvasConfetti: confetti(mergedOptions)
    CanvasConfetti-->>Confetti: Animation renders on canvas
    Confetti-->>ConfettiButton: Returns Promise
    ConfettiButton-->>User: Confetti animation plays
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Confetti component logic: Context-based imperative API with ref forwarding, option merging, and error handling warrant close attention to ensure pattern correctness
  • Modal story state management: Verify useEffect hooks correctly orchestrate footerState transitions (running → done) and confetti timing
  • CodeBlockFooter integration: Confirm ShipfoxLoader placement and message rendering conditional logic work as intended
  • Dependency compatibility: Verify canvas-confetti (^1.9.4) and shipfox-loader-react (^1.0.1) versions align with project requirements

Possibly related PRs

Suggested reviewers

  • EnzalRad
  • dvxam
  • noe-charmet

Poem

🐰 With shimmer and shine, the text now gleams,
And confetti blooms in pixel dreams,
A button click, the canvas springs to life,
Loading spinners twirl through celebration strife,
This UI hops brighter—hooray, hooray! 🎉✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main changes: addition of three new components (shiny text, confetti, and shipfox loader), which aligns with the file changes and objectives.

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

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

Copy link

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

🧹 Nitpick comments (5)
libs/react/ui/src/components/shiny-text/shiny-text.tsx (2)

1-6: Export ShinyTextProps for consumer type safety.

The ShinyTextProps interface should be exported so consumers can properly type their props when wrapping or extending this component.

-interface ShinyTextProps {
+export interface ShinyTextProps {
   text: string;
   disabled?: boolean;
   speed?: number;
   className?: string;
 }

11-17: Consider using cn() utility for className composition.

The codebase uses the cn() utility (from utils/cn) for className merging elsewhere. Using it here would provide consistent behavior and better handling of conditional classes.

+import {cn} from 'utils/cn';
+
 function ShinyText({text, disabled = false, speed = 5, className = ''}: ShinyTextProps) {
   const animationDuration = `${speed}s`;

   return (
     <div
-      className={`shiny-text ${disabled ? 'disabled' : ''} ${className}`}
+      className={cn('shiny-text', disabled && 'disabled', className)}
       style={{animationDuration}}
     >
       {text}
     </div>
   );
 }
libs/react/ui/src/components/modal/modal.stories.tsx (1)

221-231: Effect dependency may cause unintended timer restarts.

The first effect has [open, footerState] as dependencies, but the timer should only start when the modal opens with footerState === 'running'. If footerState changes while the modal is open, this won't cause issues due to the condition check, but consider removing footerState from the dependency array if the intent is only to react to open changes.

The current implementation is functionally correct due to the condition guard, but you could simplify by only depending on open if that's the sole trigger:

   useEffect(() => {
     if (open && footerState === 'running') {
       const timer = setTimeout(() => {
         setFooterState('done');
       }, 3000);

       return () => {
         clearTimeout(timer);
       };
     }
-  }, [open, footerState]);
+  }, [open]); // footerState check is inside the condition

However, keeping footerState in deps is also valid if re-evaluation on state change is desired.

libs/react/ui/src/components/confetti/confetti.tsx (2)

23-25: Unused ConfettiContext and related type.

ConfettiContext is created and provided but never consumed. If this is intended for future use, consider adding a TODO comment; otherwise, remove the dead code.

If not needed:

-type ConfettiContextValue = ConfettiApi;
-
-const ConfettiContext = createContext<ConfettiContextValue | null>(null);

If intended for future use, export a hook:

export function useConfetti() {
  const context = useContext(ConfettiContext);
  if (!context) {
    throw new Error('useConfetti must be used within a Confetti component');
  }
  return context;
}

107-109: ConfettiButtonProps type includes canvas option that may cause confusion.

The type allows canvas?: HTMLCanvasElement in options, but the global confetti() function handles this differently than expected. If passed, it would override where confetti renders, which might not be the intended use case for this button component.

Consider omitting canvas from the allowed options:

-export type ConfettiButtonProps = ComponentProps<'button'> & {
-  options?: ConfettiOptions & ConfettiGlobalOptions & {canvas?: HTMLCanvasElement};
-};
+export type ConfettiButtonProps = ComponentProps<'button'> & {
+  options?: Omit<ConfettiOptions & ConfettiGlobalOptions, 'canvas'>;
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4c75fc and 5c367d3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • libs/react/ui/index.css (1 hunks)
  • libs/react/ui/package.json (3 hunks)
  • libs/react/ui/src/components/code-block/code-block-footer.tsx (3 hunks)
  • libs/react/ui/src/components/confetti/confetti.stories.tsx (1 hunks)
  • libs/react/ui/src/components/confetti/confetti.tsx (1 hunks)
  • libs/react/ui/src/components/confetti/index.ts (1 hunks)
  • libs/react/ui/src/components/index.ts (2 hunks)
  • libs/react/ui/src/components/modal/modal.stories.tsx (4 hunks)
  • libs/react/ui/src/components/shiny-text/index.ts (1 hunks)
  • libs/react/ui/src/components/shiny-text/shiny-text.tsx (1 hunks)
  • libs/react/ui/src/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*

⚙️ CodeRabbit configuration file

We handle errors at the edge of our applications in most cases. Do not recommend to add error handling around every single function. We prefer them to bubble up and be handled at upper layers.

Files:

  • libs/react/ui/src/index.ts
  • libs/react/ui/src/components/confetti/index.ts
  • libs/react/ui/src/components/shiny-text/index.ts
  • libs/react/ui/index.css
  • libs/react/ui/src/components/index.ts
  • libs/react/ui/src/components/confetti/confetti.stories.tsx
  • libs/react/ui/src/components/confetti/confetti.tsx
  • libs/react/ui/package.json
  • libs/react/ui/src/components/modal/modal.stories.tsx
  • libs/react/ui/src/components/code-block/code-block-footer.tsx
  • libs/react/ui/src/components/shiny-text/shiny-text.tsx
🧬 Code graph analysis (4)
libs/react/ui/src/components/confetti/confetti.stories.tsx (1)
libs/react/ui/src/components/confetti/confetti.tsx (2)
  • Confetti (105-105)
  • ConfettiButton (111-135)
libs/react/ui/src/components/confetti/confetti.tsx (1)
libs/react/ui/src/components/button/button.tsx (1)
  • Button (50-91)
libs/react/ui/src/components/modal/modal.stories.tsx (1)
libs/react/ui/src/components/confetti/confetti.tsx (2)
  • ConfettiRef (27-27)
  • Confetti (105-105)
libs/react/ui/src/components/code-block/code-block-footer.tsx (2)
libs/react/ui/src/index.ts (1)
  • ShipfoxLoader (4-4)
libs/react/ui/src/components/shiny-text/shiny-text.tsx (1)
  • ShinyText (21-21)
🔇 Additional comments (12)
libs/react/ui/src/index.ts (1)

4-4: LGTM!

The re-export of ShipfoxLoader from shipfox-loader-react is clean and the dependency is properly declared in package.json.

libs/react/ui/src/components/confetti/index.ts (1)

1-1: LGTM!

Standard barrel export pattern for the confetti module.

libs/react/ui/index.css (1)

941-970: LGTM!

The shiny-text CSS implementation is well-structured with proper vendor prefixes for Safari compatibility (-webkit-background-clip, -webkit-text-fill-color). The hardcoded 5s duration in CSS serves as a sensible default and is correctly overridden by the component's inline animationDuration style when the speed prop is provided.

libs/react/ui/src/components/confetti/confetti.stories.tsx (1)

1-38: LGTM!

The Storybook stories provide good coverage of both default and customized confetti usage patterns. The WithOptions story effectively demonstrates the configurable nature of the component.

libs/react/ui/src/components/modal/modal.stories.tsx (1)

255-265: LGTM!

The Confetti component integration is well-implemented with manualstart to prevent auto-firing, proper ref handling, and appropriate z-index/pointer-events styling to avoid blocking interactions.

libs/react/ui/src/components/confetti/confetti.tsx (1)

111-135: ConfettiButton uses global confetti instead of canvas instance.

Unlike the Confetti component which uses a dedicated canvas, ConfettiButton calls the global confetti() function directly. This creates confetti on a default canvas that may overlay the entire viewport. This is likely intentional for standalone use, but documenting this behavior difference would help consumers.

Is this intentional? If ConfettiButton should use the same canvas as a parent Confetti component, it could consume the ConfettiContext instead:

// Alternative: Use context if available, fallback to global
const context = useContext(ConfettiContext);
const fireConfetti = context?.fire ?? confetti;
libs/react/ui/package.json (2)

48-48: The shipfox-loader-react package is published and publicly accessible on npm. No action needed.


39-39: canvas-confetti@^1.9.4 is verified as secure and legitimate.

Version 1.9.4 is the current latest stable release with no known security vulnerabilities. The package is actively maintained, has zero dependencies, and is published by a verified maintainer.

libs/react/ui/src/components/index.ts (1)

8-8: LGTM! Barrel exports follow the established pattern.

The new component exports are correctly positioned alphabetically and follow the existing barrel export pattern in this index file.

Also applies to: 23-23

libs/react/ui/src/components/code-block/code-block-footer.tsx (2)

3-3: LGTM! Imports are correctly structured.

The new imports for ShinyText and ShipfoxLoader are properly added and follow the project's import conventions.

Also applies to: 6-6


32-32: Manual verification required for ShipfoxLoader props.

The shipfox-loader-react package is not publicly available, preventing verification of the props API. Access to the package documentation or source code is needed to confirm whether size, animation, color, and background are valid props and whether the provided values are acceptable.

libs/react/ui/src/components/shiny-text/index.ts (1)

1-1: LGTM! Standard barrel export.

The barrel export follows the established pattern for component modules in this library.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds three new UI components to the React component library: a ShinyText component for animated text effects, a Confetti component for celebratory animations, and integrates the ShipfoxLoader from an external package. The changes enhance the visual experience by replacing static icons with animated loaders and adding visual feedback for completion states.

Key Changes:

  • Adds ShinyText component with customizable animation speed for text shimmer effects
  • Adds Confetti component with canvas-based particle animations and a ConfettiButton wrapper
  • Integrates shipfox-loader-react external package and exports it from the main library index
  • Updates CodeBlockFooter to use ShipfoxLoader and ShinyText for enhanced visual feedback
  • Adds interactive demo in modal stories showing confetti trigger on completion state

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds canvas-confetti@1.9.4, @types/canvas-confetti@1.9.0, and shipfox-loader-react@1.0.1 dependencies
libs/react/ui/package.json Declares new dependencies in dependencies and devDependencies sections
libs/react/ui/src/index.ts Exports ShipfoxLoader from external package at library root level
libs/react/ui/src/components/index.ts Exports new confetti and shiny-text components
libs/react/ui/src/components/shiny-text/* Implements ShinyText component with text, disabled, speed, and className props
libs/react/ui/src/components/confetti/* Implements Confetti canvas component with ref API and ConfettiButton wrapper, includes Storybook stories
libs/react/ui/src/components/code-block/code-block-footer.tsx Replaces static shipfox icon with ShipfoxLoader, wraps running state messages with ShinyText
libs/react/ui/src/components/modal/modal.stories.tsx Adds GithubActions story demo with state management, confetti trigger on completion
libs/react/ui/index.css Adds CSS for shiny-text animation with linear gradient and keyframe animation
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@argos-ci
Copy link

argos-ci bot commented Dec 15, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) 👍 Changes approved 6 changed, 2 added Dec 15, 2025, 5:16 AM

@kylengn kylengn merged commit 18f4ab3 into main Dec 15, 2025
5 checks passed
@kylengn kylengn deleted the feat/add-effect-components branch December 15, 2025 08:29
@coderabbitai coderabbitai bot mentioned this pull request Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants