refactor: README for clarity, enhance theme toggle accessibility, and…#5
Conversation
… improve particle canvas performance with reduced motion support.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary by CodeRabbit
WalkthroughAdds CSS variables and canvas theming, injects a pre-hydration theme script, expands README content, makes Hero respect reduced-motion with an overlay, makes ThemeToggle hydration-safe, and rewrites ParticleCanvas to be theme-, DPR-, and motion-aware. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant B as Browser
participant RL as RootLayout (pre-hydrate script)
participant CSS as CSS Vars
participant TT as ThemeToggle
participant PC as ParticleCanvas
participant H as Hero
U->>B: Request page
B->>RL: Run pre-hydration script (apply preferred color / dark class)
B->>CSS: Resolve theme CSS vars (`--primary-rgb`, `--canvas-*`)
B->>TT: Mount ThemeToggle
TT-->>TT: set mounted = true (defer ARIA/animations)
B->>PC: Mount ParticleCanvas
alt Prefers Reduced Motion
PC-->>PC: Render single static frame (no listeners/loop)
else Full Motion
PC-->>PC: Read CSS vars, apply canvas styles, start animation loop + listeners (DPR-aware)
end
B->>H: Mount Hero
alt Prefers Reduced Motion
H-->>H: Disable motion (durations=0, no arrow anim)
else Full Motion
H-->>H: Run motion transitions and arrow animation
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Cache: Disabled due to data retention organization setting Knowledge base: Disabled due to data retention organization setting 📒 Files selected for processing (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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
README.md (1)
14-18: Optional: Consider markdown linter preferences.The 4-space indentation for nested list items is valid Markdown and improves readability. However, markdownlint expects 2-space indentation by default. You can either:
- Keep the current 4-space indentation (recommended for clarity)
- Adjust to 2-space indentation to satisfy the linter
- Configure markdownlint to accept 4-space indentation via
.markdownlint.jsonBased on static analysis hints.
Also applies to: 29-32
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (5)
README.md(1 hunks)app/globals.css(2 hunks)components/sections/hero.tsx(8 hunks)components/theme/theme-toggle.tsx(1 hunks)components/ui/particle-canvas.tsx(7 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
README.md
14-14: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
15-15: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
16-16: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
17-17: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
18-18: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
29-29: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
30-30: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
31-31: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
32-32: Unordered list indentation
Expected: 2; Actual: 4
(MD007, ul-indent)
🔇 Additional comments (10)
app/globals.css (1)
23-27: LGTM!The new canvas-specific CSS variables follow the existing naming conventions and provide appropriate theme-aware values. The opacity difference (1.0 for light, 0.95 for dark) is subtle and appropriate. The inline comments clearly document the purpose.
Also applies to: 56-58
README.md (1)
13-20: LGTM!The expanded tech stack descriptions significantly improve clarity and professionalism. The structured bullet points with emoji indicators make the content more scannable, and the addition of the Tailwind CSS entry completes the technology overview.
components/theme/theme-toggle.tsx (2)
11-21: LGTM!The hydration-safe pattern correctly addresses SSR/CSR mismatches. The
mountedstate ensures theme-dependent attributes and animations only apply after hydration, preventing console errors and visual flickers.
29-43: Excellent accessibility improvements!The mounted-gated ARIA attributes ensure screen readers receive accurate state information after hydration. The conditional animations prevent hydration mismatches while maintaining the polished UX. The use of
aria-pressedis semantically correct for a toggle button.Also applies to: 46-62
components/sections/hero.tsx (2)
14-14: Excellent accessibility implementation!The reduced-motion support comprehensively addresses accessibility concerns. By checking
useReducedMotion()and conditionally setting durations to 0 and removing delays, you ensure users who prefer reduced motion experience an instant, non-animated interface while preserving the polished UX for others.Also applies to: 64-66, 78-81, 93-96, 108-111, 128-147
46-55: LGTM!The overlay layer improves text contrast without affecting accessibility or interactivity. The
aria-hiddenandpointer-events-noneattributes are correctly applied, and the responsive approach (gradient on desktop, stronger layer on mobile) is appropriate for different screen sizes.components/ui/particle-canvas.tsx (4)
29-33: LGTM!The reduced-motion implementation correctly detects user preferences and renders a single static frame when motion is reduced, bypassing the animation loop and event listeners. This provides an accessible experience while maintaining visual presence.
Also applies to: 175-190
42-55: Excellent hydration-safe styling!The
applyCanvasStylesfunction correctly reads CSS variables client-side to prevent SSR/CSR mismatches. The fallback logic aligns with the values defined inapp/globals.css, ensuring consistent behavior even if variables are not available.Also applies to: 180-181
92-124: LGTM!The theme-aware color computation robustly reads CSS variables with sensible fallbacks. The
trim()checks and theme-based defaults ensure the canvas renders correctly across themes and handles cases where CSS variables might not be available.
138-164: LGTM!The theme-aware rendering logic provides appropriate visual contrast across themes. The differential line widths and alpha values ensure connections are visible but not overwhelming in both light and dark modes. The use of primary accent for mouse connections provides good affordance.
Also applies to: 210-227
Docstrings generation was requested by @nabobery. * #5 (comment) The following files were modified: * `components/sections/hero.tsx` * `components/theme/theme-toggle.tsx` * `components/ui/particle-canvas.tsx`
|
Note Generated docstrings for this pull request at #6 |
… performance with improved CSS variable handling
Docstrings generation was requested by @nabobery. * #5 (comment) The following files were modified: * `app/layout.tsx` * `components/sections/hero.tsx` * `components/theme/theme-toggle.tsx` * `components/ui/particle-canvas.tsx`
|
Note Generated docstrings for this pull request at #7 |
📝 Add docstrings to `feat/v1-improvements`
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 (3)
components/sections/hero.tsx (1)
23-43: Honor reduced-motion for the Typed.js animation.
useReducedMotiondisables the Framer Motion transitions, but the Typed.js effect still runs, so users who prefer reduced motion see continuous typing animation. That contradicts the stated goal and accessibility preference. Make the effect conditional (or render static text) whenshouldReduceMotionis true.- useEffect(() => { - if (typedRef.current) { + useEffect(() => { + if (shouldReduceMotion) { + return + } + if (typedRef.current) { const options = { @@ - return () => { - typedInstance.current?.destroy() - } - }, []) + return () => { + typedInstance.current?.destroy() + } + }, [shouldReduceMotion])components/ui/particle-canvas.tsx (2)
24-71: Re-run effect whenresolvedThemechanges.When the user runs in
"system"mode and the OS theme flips,themestays"system"so this effect never re-executes. The cached CSS variables (andcanvasParticleRgbfallback) remain stuck on the old theme, so the canvas colors don’t update. AddresolvedThemeto the dependency list (and ensurereadCssVarsruns) so system-driven theme changes refresh colors.- const { theme, resolvedTheme } = useTheme() + const { theme, resolvedTheme } = useTheme() @@ - }, [theme]) + }, [theme, resolvedTheme])Also make sure any theme-conditioned fallbacks inside the effect use
resolvedTheme || themeafter this change.
288-312: Fix boundary handling to prevent teleporting particles.When a particle crosses the left/top boundary,
this.x/this.yget clamped to 0 and the subsequent ternary always picks the “else” branch, snapping the particle to the far edge. Becausevx/vyare never inverted, particles stick to borders. Switchvx/vytoletand reflect them when bounds are hit instead of reassigning position after clamping.- const vx = (Math.random() - 0.5) * 0.2 - const vy = (Math.random() - 0.5) * 0.2 + let vx = (Math.random() - 0.5) * 0.2 + let vy = (Math.random() - 0.5) * 0.2 @@ - // Bounce off edges - if (this.x < 0 || this.x > cssWidth) { - this.x = Math.max(0, Math.min(this.x, cssWidth)) - this.x = this.x < 0 ? radius : cssWidth - radius - } - - if (this.y < 0 || this.y > cssHeight) { - this.y = Math.max(0, Math.min(this.y, cssHeight)) - this.y = this.y < 0 ? radius : cssHeight - radius - } + // Bounce off edges + if (this.x - radius < 0 || this.x + radius > cssWidth) { + this.x = Math.max(radius, Math.min(this.x, cssWidth - radius)) + vx = -vx + } + + if (this.y - radius < 0 || this.y + radius > cssHeight) { + this.y = Math.max(radius, Math.min(this.y, cssHeight - radius)) + vy = -vy + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (4)
app/layout.tsx(3 hunks)components/sections/hero.tsx(7 hunks)components/theme/theme-toggle.tsx(1 hunks)components/ui/particle-canvas.tsx(7 hunks)
🧰 Additional context used
🪛 ast-grep (0.39.5)
app/layout.tsx
[warning] 111-111: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
app/layout.tsx
[error] 112-112: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
…rate theme representation
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (1)
components/theme/theme-toggle.tsx(1 hunks)
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
components/ui/particle-canvas.tsx (2)
147-194: Use the resolved theme for light/dark heuristics.When the stored theme is
"system"and the OS is dark,theme !== 'dark'evaluates totrue, soisLightstays true: connection opacity and line widths use light-mode tuning against a dark canvas. Base this decision on the effective theme.- const primaryRgb = cssVarsRef.current.primaryRgb - const canvasLineRgb = cssVarsRef.current.canvasLineRgb - const isLight = theme !== 'dark' + const primaryRgb = cssVarsRef.current.primaryRgb + const canvasLineRgb = cssVarsRef.current.canvasLineRgb + const effectiveTheme = resolvedTheme ?? theme ?? 'light' + const isLight = effectiveTheme !== 'dark'
285-312: Invert particle velocity on wall collisions.After hitting an edge the particle is clamped to the boundary but its velocity never flips, so it sticks in place. Make the velocities mutable and multiply by -1 when you hit a wall.
- const vx = (Math.random() - 0.5) * 0.2 - const vy = (Math.random() - 0.5) * 0.2 + let vx = (Math.random() - 0.5) * 0.2 + let vy = (Math.random() - 0.5) * 0.2 @@ - if (this.x < 0 || this.x > cssWidth) { - this.x = Math.max(0, Math.min(this.x, cssWidth)) - this.x = this.x < 0 ? radius : cssWidth - radius - } + if (this.x - radius <= 0 || this.x + radius >= cssWidth) { + this.x = Math.max(radius, Math.min(this.x, cssWidth - radius)) + vx *= -1 + } @@ - if (this.y < 0 || this.y > cssHeight) { - this.y = Math.max(0, Math.min(this.y, cssHeight)) - this.y = this.y < 0 ? radius : cssHeight - radius - } + if (this.y - radius <= 0 || this.y + radius >= cssHeight) { + this.y = Math.max(radius, Math.min(this.y, cssHeight - radius)) + vy *= -1 + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (4)
app/layout.tsx(2 hunks)components/sections/hero.tsx(7 hunks)components/theme/theme-toggle.tsx(1 hunks)components/ui/particle-canvas.tsx(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- components/sections/hero.tsx
🧰 Additional context used
🪛 ast-grep (0.39.5)
app/layout.tsx
[warning] 111-111: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
app/layout.tsx
[error] 112-112: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
…epresentation based on resolved theme and user preferences
… improve particle canvas performance with reduced motion support.