Skip to content

feat(ui): add DotGrid component with GSAP animations#148

Merged
kylengn merged 3 commits intomainfrom
feat/add-dot-grid
Nov 18, 2025
Merged

feat(ui): add DotGrid component with GSAP animations#148
kylengn merged 3 commits intomainfrom
feat/add-dot-grid

Conversation

@kylengn
Copy link
Contributor

@kylengn kylengn commented Nov 18, 2025

Summary by CodeRabbit

Release Notes

  • New Features
    • Introduced an interactive dot grid component with responsive animations that respond to pointer movements, hover effects, and click interactions.
    • Integrated the dot grid component into the sign-in interface for enhanced visual experience.

@kylengn kylengn self-assigned this Nov 18, 2025
Copilot AI review requested due to automatic review settings November 18, 2025 03:39
@vercel
Copy link

vercel bot commented Nov 18, 2025

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

Project Deployment Preview Comments Updated (UTC)
react-ui Ready Ready Preview Comment Nov 18, 2025 5:40am

@coderabbitai
Copy link

coderabbitai bot commented Nov 18, 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

Introduces a new interactive canvas-based DotGrid component with GSAP animations and pointer tracking, updates package dependencies to include gsap and Vitest testing tools, and integrates the component into the sign-in story as a decorative background.

Changes

Cohort / File(s) Summary
Dependencies & Testing Setup
libs/react/ui/package.json
Added gsap and playwright to dependencies; introduced Vitest integration tooling (\@vitest/browser-playwright, \@vitest/coverage-v8) and playwright to devDependencies. Reorganized test tooling references.
DotGrid Component
libs/react/ui/src/components/dot-grid/dot-grid.tsx
New React component implementing a responsive canvas-based dot grid with pointer tracking, velocity-based GSAP animations, color blending, proximity-based interactions, and click-triggered shock waves. Features device-pixel-ratio handling, ResizeObserver integration, and Path2D optimization.
Component Exports
libs/react/ui/src/components/dot-grid/index.ts, libs/react/ui/src/components/index.ts
Added barrel re-exports exposing DotGrid component through public entry points.
Story Integration
libs/react/ui/src/onboarding/sign-in.stories.tsx
Imported DotGrid and replaced static radial gradient background with interactive dot grid component configured with animation and interaction props; updated container styling for CSS masking.

Sequence Diagram

sequenceDiagram
    participant User
    participant Canvas as DotGrid Canvas
    participant PointerTracker as Pointer Tracker
    participant GSAP as GSAP Animator
    participant Grid as Dot Grid State

    User->>Canvas: Move pointer / Click
    Canvas->>PointerTracker: Capture pointer position & velocity
    PointerTracker->>Grid: Update dots in proximity range
    alt Velocity exceeds threshold OR Click event
        Grid->>GSAP: Trigger inertia/shock animation
        GSAP->>Canvas: Apply tween to dot displacement
        Canvas->>Canvas: Render dots with color blend
        GSAP->>Grid: Return dots to original position (elastic)
    else Hover without threshold
        Grid->>Canvas: Blend colors for nearby dots
        Canvas->>Canvas: Render with color interpolation
    end
    Canvas->>User: Display animated dot grid
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • dot-grid.tsx: High-density logic including canvas rendering with device pixel ratio handling, grid layout computation, RGB color precomputation and blending, pointer velocity tracking, GSAP inertia animation setup, shock wave mechanics, and ResizeObserver integration. Requires careful review of animation timing, color blending calculations, and event handler debouncing.
  • Animation mechanics: Verify GSAP inertia configuration, threshold-based triggering logic, elastic return animations, and performance implications of frequent canvas redraws.
  • Canvas optimization: Review Path2D usage and device pixel ratio scaling correctness.

Suggested reviewers

  • EnzalRad
  • dvxam
  • noe-charmet

Poem

🐰 A grid of dots now dances bright,
With pointer magic, smooth and light!
GSAP whispers, canvas gleams,
Inertia flows through playful dreams!
✨🎨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): add DotGrid component with GSAP animations' accurately and directly describes the main change: introducing a new DotGrid component that uses GSAP for animations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

@argos-ci
Copy link

argos-ci bot commented Nov 18, 2025

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

Build Status Details Updated (UTC)
default (Inspect) 👍 Changes approved 3 changed Nov 18, 2025, 5:43 AM

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 introduces a new DotGrid component that creates an animated grid of dots with interactive mouse-based physics effects using GSAP animations. The component is integrated into the sign-in story to replace the previous static radial gradient background.

Key Changes:

  • Added GSAP 3.13.0 as a dependency for animation capabilities
  • Implemented DotGrid component with canvas-based rendering and mouse interaction effects
  • Integrated DotGrid into the sign-in story with a radial mask for visual effect

Reviewed Changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
pnpm-lock.yaml Added GSAP 3.13.0 dependency and related package resolution metadata
libs/react/ui/package.json Added GSAP to dependencies; reordered some devDependencies
libs/react/ui/src/components/index.ts Exported the new DotGrid component
libs/react/ui/src/components/dot-grid/index.ts Barrel export for DotGrid component
libs/react/ui/src/components/dot-grid/dot-grid.tsx New interactive dot grid component with canvas rendering and GSAP-powered physics
libs/react/ui/src/onboarding/sign-in.stories.tsx Replaced static gradient background with animated DotGrid component
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

Comment on lines +10 to +19
const throttle = <T extends (...args: never[]) => void>(func: T, limit: number): T => {
let lastCall = 0;
return ((...args: Parameters<T>) => {
const now = performance.now();
if (now - lastCall >= limit) {
lastCall = now;
func(...args);
}
}) as T;
};
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Using performance.now() in the throttle function but comparing against a timestamp that starts at 0. The first call will always execute, but subsequent calls within the throttle limit will be skipped even if they're the first real call. Consider initializing lastCall to -Infinity or performance.now() - limit to ensure consistent behavior.

Copilot uses AI. Check for mistakes.
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The canvas is cleared using logical dimensions (canvas.width, canvas.height) but should use physical dimensions. After scaling the context by dpr, you should clear using the logical dimensions:

ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1));

or store the logical width/height and use those instead.

Suggested change
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1));

Copilot uses AI. Check for mistakes.
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: 1

Caution

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

⚠️ Outside diff range comments (1)
libs/react/ui/src/onboarding/sign-in.stories.tsx (1)

5-46: DotGrid integration looks good, but 3‑digit hex baseColor won’t be parsed correctly

Using DotGrid as a masked background decoration here fits well with the onboarding layout and Storybook/Argos flow.

One detail: baseColor="#F00" is a 3‑digit hex, while hexToRgb in dot-grid.tsx currently only accepts 6‑digit hex codes. That means baseRgb will fall back to black, so proximity-based blending will interpolate from black instead of red. Either update hexToRgb to support 3‑digit hex (preferred) or change this story to use a 6‑digit value like #FF0000.

🧹 Nitpick comments (2)
libs/react/ui/src/components/dot-grid/dot-grid.tsx (2)

93-199: Path2D‑only rendering means older environments render nothing

The drawing effect bails out when circlePath is null, which happens if window.Path2D is unavailable. That’s fine for modern browsers, but in less capable environments you’ll still pay the cost of building the grid and wiring listeners while rendering nothing.

Optional: add a fallback that uses ctx.arc directly when Path2D isn’t supported instead of returning early, so the component degrades gracefully in older or constrained browsers.


201-291: Global window listeners are correct but may be heavy for multiple instances

Attaching throttled mousemove and click listeners to window works well for a single background DotGrid and you clean them up correctly. If you ever end up mounting multiple DotGrid instances on the same page, each will add its own listeners and iterate its own dotsRef on every event.

Not a blocker, but if multi-instance usage becomes a thing, consider centralizing the listeners (or scoping them to the wrapper’s bounding rect) to avoid redundant work.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17e6197 and 95db5fb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • libs/react/ui/package.json (2 hunks)
  • libs/react/ui/src/components/dot-grid/dot-grid.tsx (1 hunks)
  • libs/react/ui/src/components/dot-grid/index.ts (1 hunks)
  • libs/react/ui/src/components/index.ts (1 hunks)
  • libs/react/ui/src/onboarding/sign-in.stories.tsx (2 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/components/index.ts
  • libs/react/ui/src/components/dot-grid/index.ts
  • libs/react/ui/src/components/dot-grid/dot-grid.tsx
  • libs/react/ui/package.json
  • libs/react/ui/src/onboarding/sign-in.stories.tsx
🧬 Code graph analysis (1)
libs/react/ui/src/onboarding/sign-in.stories.tsx (1)
libs/react/ui/src/components/dot-grid/dot-grid.tsx (1)
  • DotGrid (61-303)
⏰ 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). (2)
  • GitHub Check: Agent
  • GitHub Check: Continuous integration
🔇 Additional comments (4)
libs/react/ui/package.json (2)

42-42: GSAP runtime dependency aligns with DotGrid usage

Adding gsap as a runtime dependency here matches the new DotGrid component’s animation needs and is appropriately scoped to the UI package.

Please double-check that this version matches what you expect to use elsewhere in the monorepo (if any other packages pull in GSAP) to avoid duplicate versions.


79-90: Vitest/Playwright devDependencies look consistent with the test setup

The added Vitest browser/coverage packages plus playwright align with the vitest-run test script and Argos/Storybook usage; this looks like the right place for them as devDependencies.

Confirm that your CI pipeline is configured to use these versions (e.g., vitest-run wired to vitest@^4.0.8 and Playwright installed in the test image) so test runs stay reproducible.

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

1-1: Barrel export is straightforward and correct

Re-exporting from ./dot-grid keeps the component’s public API tidy and consistent with the other component folders.

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

7-7: DotGrid re-export in main components barrel makes sense

Adding export * from './dot-grid'; here cleanly exposes DotGrid through the existing components entry point and matches the pattern used by other components.

Comment on lines +51 to +59
function hexToRgb(hex: string): RgbColor {
const m = hex.match(HEX_COLOR_REGEX);
if (!m) return {r: 0, g: 0, b: 0};
return {
r: parseInt(m[1], 16),
g: parseInt(m[2], 16),
b: parseInt(m[3], 16),
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

hexToRgb silently treats non‑6‑digit hex (like #F00) as black

hexToRgb only matches 6‑digit hex via HEX_COLOR_REGEX, returning {r: 0, g: 0, b: 0} for anything else. Since the sign‑in story passes baseColor="#F00", the base RGB used for proximity blending becomes black instead of the intended red, which subtly changes the effect.

Recommend normalizing 3‑digit hex to 6‑digit before matching, so both forms work:

 function hexToRgb(hex: string): RgbColor {
-  const m = hex.match(HEX_COLOR_REGEX);
+  let value = hex.trim();
+
+  // Expand 3-digit #rgb to 6-digit #rrggbb for convenience.
+  if (value.length === 4 && value[0] === '#') {
+    value = `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`;
+  }
+
+  const m = value.match(HEX_COLOR_REGEX);
   if (!m) return {r: 0, g: 0, b: 0};
   return {
     r: parseInt(m[1], 16),
     g: parseInt(m[2], 16),
     b: parseInt(m[3], 16),
   };
 }

This keeps the existing regex and behavior while making the API friendlier to callers.

📝 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
function hexToRgb(hex: string): RgbColor {
const m = hex.match(HEX_COLOR_REGEX);
if (!m) return {r: 0, g: 0, b: 0};
return {
r: parseInt(m[1], 16),
g: parseInt(m[2], 16),
b: parseInt(m[3], 16),
};
}
function hexToRgb(hex: string): RgbColor {
let value = hex.trim();
// Expand 3-digit #rgb to 6-digit #rrggbb for convenience.
if (value.length === 4 && value[0] === '#') {
value = `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`;
}
const m = value.match(HEX_COLOR_REGEX);
if (!m) return {r: 0, g: 0, b: 0};
return {
r: parseInt(m[1], 16),
g: parseInt(m[2], 16),
b: parseInt(m[3], 16),
};
}
🤖 Prompt for AI Agents
In libs/react/ui/src/components/dot-grid/dot-grid.tsx around lines 51 to 59,
hexToRgb currently only accepts 6‑digit hex and returns black for 3‑digit inputs
like "#F00"; normalize 3‑digit shorthand to 6‑digit before applying the existing
HEX_COLOR_REGEX (e.g. expand "#F00" to "#FF0000"), then run the same match/parse
logic and keep the current fallback of {r:0,g:0,b:0} for invalid values.

@kylengn kylengn merged commit 6054e39 into main Nov 18, 2025
5 checks passed
@kylengn kylengn deleted the feat/add-dot-grid branch November 18, 2025 05:48
@coderabbitai coderabbitai bot mentioned this pull request Nov 18, 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