feat(ui): add DotGrid component with GSAP animations#148
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughIntroduces 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
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. Comment |
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
There was a problem hiding this comment.
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.
| 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; | ||
| }; |
There was a problem hiding this comment.
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.
| if (!canvas) return; | ||
| const ctx = canvas.getContext('2d'); | ||
| if (!ctx) return; | ||
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
There was a problem hiding this comment.
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.
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1)); |
There was a problem hiding this comment.
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 correctlyUsing
DotGridas a masked background decoration here fits well with the onboarding layout and Storybook/Argos flow.One detail:
baseColor="#F00"is a 3‑digit hex, whilehexToRgbindot-grid.tsxcurrently only accepts 6‑digit hex codes. That meansbaseRgbwill fall back to black, so proximity-based blending will interpolate from black instead of red. Either updatehexToRgbto 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 nothingThe drawing effect bails out when
circlePathis null, which happens ifwindow.Path2Dis 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.arcdirectly whenPath2Disn’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 instancesAttaching throttled
mousemoveandclicklisteners towindowworks 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 owndotsRefon 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.tslibs/react/ui/src/components/dot-grid/index.tslibs/react/ui/src/components/dot-grid/dot-grid.tsxlibs/react/ui/package.jsonlibs/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 usageAdding
gsapas 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 setupThe added Vitest browser/coverage packages plus
playwrightalign with thevitest-runtest 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-runwired tovitest@^4.0.8and 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 correctRe-exporting from
./dot-gridkeeps 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 senseAdding
export * from './dot-grid';here cleanly exposes DotGrid through the existing components entry point and matches the pattern used by other components.
| 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), | ||
| }; | ||
| } |
There was a problem hiding this comment.
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.
| 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.
…ling improvements
Summary by CodeRabbit
Release Notes