Skip to content

Comments

Splash screen adding#172

Open
rabden wants to merge 33 commits intomainfrom
splash-screen-adding
Open

Splash screen adding#172
rabden wants to merge 33 commits intomainfrom
splash-screen-adding

Conversation

@rabden
Copy link
Collaborator

@rabden rabden commented Feb 1, 2026

Important

Adds splash screen with animations, structured data enhancements, and updates dependencies and CSS styles.

  • Splash Screen:
    • Adds SplashScreen component in splash-screen.tsx with video animation and reduced motion handling.
    • Uses usePrefersReducedMotion hook for motion preference detection.
  • Structured Data:
    • Adds breadcrumb schema to blog/[slug]/page.tsx, blog/page.tsx, privacy/page.tsx, products/[slug]/page.tsx, products/page.tsx, team/[slug]/page.tsx, team/page.tsx, terms/page.tsx.
    • Adds generateBreadcrumbSchema, generateSoftwareApplicationSchema, generatePersonSchema to structured-data.ts.
  • CSS and Animation:
    • Adds custom-fade-in animation in globals.css.
    • Updates AnimatedBackground in animated-background.tsx with new particle configurations.
  • Dependencies:
    • Updates framer-motion to ^12.33.0 in package.json.
    • Updates @types/node to ^25.2.1 and @types/react to ^19.2.13 in package.json.

This description was created by Ellipsis for 151d943. You can customize this summary. It will automatically update as commits are pushed.

@vercel
Copy link

vercel bot commented Feb 1, 2026

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

Project Deployment Actions Updated (UTC)
volvox-website Ready Ready Preview, Comment Feb 7, 2026 6:34am

Request Review

@claude
Copy link
Contributor

claude bot commented Feb 1, 2026

Claude finished @rabden's task in 2m 38s —— View job


PR Review Summary

I've reviewed this PR which adds a splash screen, intro section, structured data enhancements, and animations. I fixed one critical issue and identified several areas that need attention.


✅ Fixed Issues

Critical syntax error fixed (src/components/splash-screen.tsx):

  • Removed duplicate motion.div element on lines 118-123 that would cause a React render error. This was likely a merge artifact.
  • Commit: 151d943

⚠️ Issues Requiring Attention

I've left inline comments on the most critical issues. Here's a summary:

1. LCP/Core Web Vitals Impact (High Priority)

  • The .animate-fade-in CSS animation uses 3s delay + 3s duration (6s total invisible content)
  • Google's LCP target is < 2.5s; this will significantly hurt SEO scores
  • Recommendation: Reduce to ~1.5s delay, 1.5s duration, and reduce translateY(200px) to 30-50px

2. Dead Code - Unused Scroll Transforms (intro-section.tsx:115-127)

  • useScroll/useTransform hooks create values that never activate
  • Body scroll is locked during the intro; by the time it unlocks, the component unmounts
  • Recommendation: Remove unused hooks or implement proper scroll-away exit animation

3. Cross-Browser Video Compatibility (splash-screen.tsx:137)

  • WebM format not supported in Safari < 16.4
  • Recommendation: Use <source> elements with both WebM and MP4 formats

4. Potential Double Navigation (homepage-client.tsx:82-110)

  • The pendingHash state in the effect dependency array may cause double navigation
  • Recommendation: Use a ref instead of state for pendingHash

👍 What's Working Well

  1. Accessibility: Excellent reduced-motion handling in both IntroSection and SplashScreen
  2. Bot Detection: Smart skip logic for crawlers (lines 77-79 in intro-section.tsx)
  3. Safety Timeout: 10s fallback timer prevents infinite scroll lock (line 105-109)
  4. Error Handling: reportError integration for video failures
  5. Structured Data: Well-implemented JSON-LD schemas with proper organization
  6. Hydration Handling: useSyncExternalStore for reduced motion detection avoids hydration mismatches
  7. Proper Cleanup: Timer refs and cleanup functions in effects are well-managed

📋 Minor Suggestions

  1. structured-data.ts: Consider exporting the input interfaces (SoftwareApplicationSchemaInput, PersonSchemaInput, ArticleSchemaInput) so consumers can explicitly type their objects

  2. intro-section.tsx:125: The dark mode video uses WebM which may fail on older browsers. Same recommendation as splash screen - use multiple sources

  3. Consider adding preload hints: Add <link rel="preload"> for the LCP-critical assets (logo images, fonts) in layout.tsx


🔍 SEO Considerations

This PR has been extensively discussed regarding SEO impact. Key mitigations already in place:

  • ✅ Bot detection skips intro for crawlers
  • prefers-reduced-motion respected
  • ✅ Server-side data fetching preserved
  • ✅ Proper metadata and structured data

Remaining concerns (documented in inline comments):

  • Animation timing is too long for Core Web Vitals
  • No <noscript> fallback for JS-disabled users (low priority)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added animated intro sequence to homepage with logo animation and interactive phase
    • Introduced splash screen with video display and automatic dismissal handling
    • Enhanced scrollbar styling with improved visual appearance
  • Improvements

    • Updated visual design with new font variables and refined background gradients
    • Improved accessibility with reduced motion preference detection and support
    • Added structured data (breadcrumbs, schema markup) to enhance SEO across pages
    • Enhanced particle system rendering
  • Chores

    • Updated framework dependencies for stability

Walkthrough

Adds two client-side intro experiences (IntroSection, SplashScreen) with reduced‑motion support and scroll‑locking; defers homepage navigation until intro completion. Updates global CSS (fonts, gradients, scrollbars, animate‑fade‑in), removes local fade‑in CSS from animated‑background, injects expanded JSON‑LD generators/scripts across pages (including SITE_URL and website schema), and bumps a few dependencies.

Changes

Cohort / File(s) Summary
Global styles
src/app/globals.css
Added --font-volvox, SairaStencilOne font variable, updated background gradient tokens, added .animate-fade-in keyframes/class with reduced‑motion fallback, and custom scrollbar styling.
Animated background
src/components/animated-background.tsx
Increased particle params (maxParticles→175, spawnWidth→1000, spawnHeight→500). Removed in-file .animate-fade-in keyframes/styles (class left in markup).
Intro & Splash components
src/components/intro-section.tsx, src/components/splash-screen.tsx
Added IntroSection (two‑phase intro with video/image fallback, framer‑motion, reduced‑motion handling, scroll‑lock, onComplete) and SplashScreen (timers, video lifecycle, reduced‑motion handling, scroll‑lock, error reporting and dismissal).
Homepage integration / flow wiring
src/components/homepage-client.tsx
Renders IntroSection, tracks pendingHash and introFinished, defers/queues navigation until intro completes, updates offset usage to window.scrollY, and wires intro completion to navigation.
Hero cleanup
src/components/hero/hero-section.tsx
Removed decorative background glow element and its inline radial‑gradient; other hero logic unchanged.
Structured data / schemas & constants
src/lib/structured-data.ts, src/lib/constants
Expanded structured‑data utilities (added WebSite, Breadcrumb, SoftwareApplication, Person, Article, WebPage generators), re‑exported additional social URL constants, and added SITE_URL.
Page injections (JSON‑LD scripts)
src/app/layout.tsx, src/app/blog/.../page.tsx, src/app/blog/page.tsx, src/app/products/.../page.tsx, src/app/products/page.tsx, src/app/team/.../page.tsx, src/app/team/page.tsx, src/app/privacy/page.tsx, src/app/terms/page.tsx
Injected breadcrumb and other JSON‑LD scripts (rendered beforeInteractive) across layout and multiple pages; added generateWebSiteSchema in layout; small metadata title/canonical tweaks.
Package bumps
package.json
Bumped framer-motion and some @types/* packages.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Homepage as "Homepage (client)"
  participant Intro as "IntroSection"
  participant Splash as "SplashScreen"
  participant Router

  User->>Homepage: load
  Homepage->>Intro: render IntroSection (may lock scroll)
  Homepage->>Splash: render SplashScreen (may lock scroll)
  Note over Intro,Splash: both observe prefers-reduced-motion and manage timers/visibility
  User->>Homepage: attempt navigation (hash/link)
  Homepage->>Homepage: is introFinished?
  alt intro not finished
    Homepage->>Homepage: store pendingHash (defer navigation)
    Homepage-->>User: navigation deferred
  else intro finished
    Homepage->>Router: navigate immediately
  end
  Intro->>Homepage: onComplete -> set introFinished
  Homepage->>Router: if pendingHash -> navigate and clear pendingHash
Loading

Suggested labels

feature, frontend, accessibility, performance, dependencies

Suggested reviewers

  • BillChirico
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Splash screen adding' is vague and uses incomplete grammar; it does not clearly summarize the comprehensive scope of changes including structured data enhancements, CSS updates, and dependency bumps. Revise title to be more descriptive and complete, such as 'Add splash screen with intro animations and structured data enhancements' to better reflect the main changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is related to the changeset and provides detailed information about splash screen, structured data, CSS, and dependency updates that align with the actual file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch splash-screen-adding

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.

@qltysh
Copy link
Contributor

qltysh bot commented Feb 1, 2026

4 new issues

Tool Category Rule Count
qlty Duplication Found 70 lines of similar code in 2 locations (mass = 181) 2
qlty Structure Function with many returns (count = 10): AnimatedBackground 2

…reen

- Add prefers-reduced-motion check to skip splash for users who prefer reduced motion
- Add onError handler to gracefully hide splash if video fails to load
- Add aria-hidden="true" for screen reader accessibility
- Remove unused videoRef
- Fix comment to match actual 1.5s delay

Co-authored-by: Md Rabiul Hossain Jahed <undefined@users.noreply.github.com>
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: 2

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 7-39: The SplashScreen component can get stuck because
handleVideoEnd sets a timeout (setTimeout in handleVideoEnd) without
storing/clearing it and there is no fallback if the video never fires ended;
update SplashScreen to track timer IDs (useRef for initialDelay and endDelay),
clear any active timers in the cleanup of useEffect and when dismissing, add an
onError handler on the video element and a max-duration fallback timer (e.g.,
start a maxTimeout when showVideo becomes true or when videoRef.current loads)
that calls the same dismiss logic, and ensure document.body.style.overflow is
always reset when clearing/discarding the splash to avoid locking scroll.
- Around line 44-49: The splash overlay's motion.div in splash-screen.tsx uses
the className containing "pointer-events-none", allowing clicks to pass through;
update the className on the motion.div (the splash overlay element) to remove
"pointer-events-none" or replace it with "pointer-events-auto" so the overlay
blocks pointer events while visible (ensure the change is applied to the
motion.div that has initial/exit/transition props).

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

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 44-54: Modify the splash screen error handler to report video load
failures via the existing reportError function before hiding the splash; update
handleVideoError to accept the event/error and call reportError(errorOrEvent, {
component: 'SplashScreen', handler: 'handleVideoError' }) and then call
setIsVisible(false). Ensure you import reportError from src/lib/logger.ts and
keep handleVideoEnd unchanged.
- Around line 31-42: The effect that toggles scrolling in useEffect currently
forces document.body.style.overflow = "unset" which clobbers any pre-existing
scroll locks; capture the previous overflow (e.g., const prev =
document.body.style.overflow) when isVisible becomes true, set overflow to
"hidden", and on both the isVisible false branch and the cleanup return restore
document.body.style.overflow = prev so the original value is preserved; update
the useEffect surrounding isVisible to store and restore the prior overflow
instead of unconditionally setting "unset".
- Around line 7-8: The exported React component SplashScreen currently lacks an
explicit return type; update its signature to include an explicit type that
allows null (e.g., change export function SplashScreen() to export function
SplashScreen(): JSX.Element | null or React.ReactNode) so the public API is
typed correctly given the conditional return of null in the component; adjust
any imports/types if needed to reference React.ReactNode.
- Around line 11-29: Derive the reduced-motion state and initial visibility
before the effect to avoid the setState-in-effect flash: compute
prefersReducedMotion once (e.g., a top-level constant or a lazy state
initializer) and use it to set the initial values for isVisible and showVideo
instead of calling setIsVisible inside useEffect; then keep the useEffect for
the timed showVideo logic only with an empty dependency array and clear the
timeout (replace/remove the setState-for-reduced-motion and the suggested
[prefersReducedMotion] dependency). Ensure you reference the existing symbols
useEffect, prefersReducedMotion, setIsVisible, setShowVideo, and initialDelay
when making the changes.
- Around line 56-57: Remove the early return that returns null when isVisible is
false so AnimatePresence can handle exit animations; instead keep rendering the
AnimatePresence wrapper and conditionally render the motion.div inside it based
on isVisible (referencing isVisible, AnimatePresence, and the motion.div used
for exit animation in the SplashScreen component) so the exit transition can run
before unmounting.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 2, 2026
- Add explicit return type (JSX.Element | null) for public API
- Use lazy state initializer to avoid setState in useEffect flash
- Use reportError from lib/logger for proper Sentry error tracking
- Preserve and restore original overflow value instead of setting 'unset'
- Remove early return to allow AnimatePresence exit animations

Co-authored-by: Md Rabiul Hossain Jahed <undefined@users.noreply.github.com>
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

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 86-92: The splash overlay motion.div is missing a data-testid
which the test-suite requires; update the JSX element (the motion.div in the
SplashScreen component) to include a descriptive data-testid attribute (e.g.
data-testid="splash-overlay") so tests can reliably select it while retaining
existing props like initial, exit, transition, className and aria-hidden.

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

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 60-75: The effect in useEffect (in SplashScreen component)
unconditionally restores document.body.style.overflow from
originalOverflowRef.current even when it never changed it; update the effect to
only store/modify overflow when you actually lock scrolling (when isVisible
becomes true) and only restore on cleanup or when isVisible becomes false if and
only if you previously changed it—use the existing originalOverflowRef (or add a
didLockRef flag) to record that you performed the lock, set
document.body.style.overflow = "hidden" only when locking, and on cleanup/when
hiding only restore document.body.style.overflow from
originalOverflowRef.current if that flag indicates you changed it.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Md Rabiul Hossain Jahed <163085056+rabden@users.noreply.github.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 2, 2026
Avoid clobbering existing scroll state when splash is hidden (e.g., for
users with prefers-reduced-motion). Now properly captures and restores
the previous overflow value only when the splash actually locks scroll.

Co-authored-by: Md Rabiul Hossain Jahed <undefined@users.noreply.github.com>
coderabbitai[bot]
coderabbitai bot previously requested changes Feb 2, 2026
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

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 12-21: The server/client mismatch comes from
getPrefersReducedMotion() returning true on SSR but different on the client;
replace that initializer in SplashScreen with a useSyncExternalStore-based hook
that returns a server snapshot of true and on the client subscribes to
window.matchMedia("(prefers-reduced-motion: reduce)") to provide the live value;
use that hook (e.g., usePrefersReducedMotion) to derive the initial isVisible
state (const [isVisible, setIsVisible] = useState(() =>
!usePrefersReducedMotion())) or simply compute isVisible from the hook, ensure
the subscription callback updates subscribers and you clean up the media query
listener on unmount, and keep references to getPrefersReducedMotion only for
legacy fallback if needed.

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

🤖 Fix all issues with AI agents
In `@src/components/intro-section.tsx`:
- Around line 62-70: handleComplete currently captures the onComplete prop
directly which defeats the purpose of onCompleteRef; update handleComplete to
call onCompleteRef.current() instead of onComplete() and change its dependency
array to reference onCompleteRef (and keep unlockScroll) so the callback no
longer re-creates when onComplete identity changes — ensure it still uses
isSkippingRef.current guard, sets isSkippingRef.current = true, calls
unlockScroll(), setPhase("interactive"), setIsVisible(false), and then invokes
onCompleteRef.current() if present.

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Signed-off-by: Md Rabiul Hossain Jahed <163085056+rabden@users.noreply.github.com>
- Fixed critical syntax error in homepage-client.tsx where checkAndScroll
  function was duplicated/malformed causing application crash
- Updated handleComplete in intro-section.tsx to use onCompleteRef.current
  instead of onComplete directly to prevent unnecessary effect re-runs
  when onComplete callback identity changes

Co-authored-by: Md Rabiul Hossain Jahed <undefined@users.noreply.github.com>
coderabbitai[bot]
coderabbitai bot previously requested changes Feb 7, 2026
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

Caution

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

⚠️ Outside diff range comments (1)
src/components/homepage-client.tsx (1)

82-120: ⚠️ Potential issue | 🔴 Critical

Critical: duplicated/nested checkAndScroll causes double navigation and broken hash handling.

The effect body contains two overlapping definitions of checkAndScroll — the inner one (lines 95-102) is nested inside the outer's if (element) branch (line 93). When the target element exists on the first frame:

  1. Outer calls handleNavigate(hash) (line 94).
  2. Execution falls into the inner definition + requestAnimationFrame(checkAndScroll) (line 106), which starts a second polling loop that will call handleNavigate again.
  3. Line 112 also kicks off the outer loop.

This results in double navigation, redundant rAF loops, and setPendingHash(null) only firing inside the success branch instead of unconditionally before the loop.

This looks like a bad merge or copy-paste artifact. The intended logic is a single checkAndScroll with the pendingHash clearing moved outside.

🐛 Proposed fix — flatten to a single checkAndScroll
   useEffect(() => {
     if (!introFinished) return;
 
     const hash = pendingHash || window.location.hash.slice(1);
+    // Clear pendingHash synchronously to prevent re-entry
+    if (pendingHash) setPendingHash(null);
+
     if (hash) {
       const startTime = performance.now();
       const timeout = 2000;
       let animationFrameId: number | undefined;
 
       const checkAndScroll = () => {
         const element = document.getElementById(hash);
         if (element) {
           handleNavigate(hash);
-      const checkAndScroll = () => {
-        const element = document.getElementById(hash);
-        if (element) {
-          handleNavigate(hash);
-        } else if (performance.now() - startTime < timeout) {
-          animationFrameId = requestAnimationFrame(checkAndScroll);
-        }
-      };
-
-      // Clear pendingHash synchronously to prevent re-entry
-      if (pendingHash) setPendingHash(null);
-      animationFrameId = requestAnimationFrame(checkAndScroll);
         } else if (performance.now() - startTime < timeout) {
           animationFrameId = requestAnimationFrame(checkAndScroll);
         }
       };
 
       animationFrameId = requestAnimationFrame(checkAndScroll);
 
       return () => {
         if (animationFrameId !== undefined) {
           cancelAnimationFrame(animationFrameId);
         }
       };
     }
   }, [introFinished, pendingHash, handleNavigate]);
🤖 Fix all issues with AI agents
In `@src/components/homepage-client.tsx`:
- Around line 163-166: IntroSection currently renders JS-only content and
there's no <noscript> fallback or LCP preloads; add a simple non-JS fallback and
preload hints in the root layout. In the IntroSection component (and where it's
used with handleIntroComplete) add a <noscript> element that either renders
essential static markup or an accessible link/button that jumps to `#main-content`
so non-JS users can reach the main app; ensure the fallback duplicates only the
minimal, meaningful content/CTA. In the app root layout (the default exported
layout component) add <link rel="preload" as="image" href="..."> or <link
rel="preconnect"> entries for the Largest Contentful Paint assets referenced by
IntroSection/Hero (images/fonts) so crawlers and real users get LCP hints; keep
the hrefs matching the actual asset imports used by IntroSection and Hero.

coderabbitai[bot]
coderabbitai bot previously requested changes Feb 7, 2026
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

Caution

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

⚠️ Outside diff range comments (1)
src/components/homepage-client.tsx (1)

82-110: ⚠️ Potential issue | 🟠 Major

Effect can trigger double navigation when pendingHash is cleared.

When pendingHash is truthy, the effect clears it synchronously (Line 101), which triggers a re-run. On the second run, pendingHash is null but window.location.hash still contains the section (set by handleNavigate in the first run's rAF), so checkAndScroll fires a second time for the same target — causing a redundant smooth scroll.

Additionally, calling setPendingHash(null) directly in the effect body is flagged by ESLint (react-hooks/set-state-in-effect) as it causes cascading renders.

Fix both by moving the clear into the rAF callback (after navigation succeeds) and tracking whether this effect already handled a hash:

Proposed fix
   useEffect(() => {
     if (!introFinished) return;

     const hash = pendingHash || window.location.hash.slice(1);
     if (hash) {
       const startTime = performance.now();
       const timeout = 2000;
       let animationFrameId: number | undefined;
+      let handled = false;

       const checkAndScroll = () => {
         const element = document.getElementById(hash);
         if (element) {
+          handled = true;
           handleNavigate(hash);
+          if (pendingHash) setPendingHash(null);
         } else if (performance.now() - startTime < timeout) {
           animationFrameId = requestAnimationFrame(checkAndScroll);
         }
       };

-      // Clear pendingHash synchronously to prevent re-entry
-      if (pendingHash) setPendingHash(null);
       animationFrameId = requestAnimationFrame(checkAndScroll);

       return () => {
         if (animationFrameId !== undefined) {
           cancelAnimationFrame(animationFrameId);
         }
+        // Clear pendingHash on cleanup if not yet handled
+        if (!handled && pendingHash) setPendingHash(null);
       };
     }
   }, [introFinished, pendingHash, handleNavigate]);
🤖 Fix all issues with AI agents
In `@src/components/intro-section.tsx`:
- Around line 114-127: The scroll-based transforms created with
useScroll/useTransform (scrollYProgress, scale, opacity, blur, y tied to
containerRef) never run because body scroll is locked during the "text" phase
and when phase becomes "interactive" the component immediately returns null via
the isVisible check; either remove these unused transforms or postpone
unmounting so the exit-parallax can run: update the logic around isVisible/phase
(the place that calls setIsVisible(false) / handleComplete and the conditional
if (!isVisible && phase === "interactive") return null) to delay setting
isVisible to false (or delay returning null) until the scroll unlock occurs and
the scrollYProgress-based animations reach their end (or a short timeout after
unlock), ensuring the useScroll/useTransform values have time to animate before
the component unmounts.

Comment on lines +114 to +127
// Scroll animations for exit
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start start", "end start"],
});

const scale = useTransform(scrollYProgress, [0, 1], [1, 0.9]);
const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0]);
const blur = useTransform(scrollYProgress, [0, 0.4], ["0px", "10px"]);
const y = useTransform(scrollYProgress, [0, 1], ["0%", "-10%"]);

const videoSrc = isDark ? "/animated-logo.webm" : "/animated-logo-white.mp4";

if (!isVisible && phase === "interactive") 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 | 🟡 Minor

Scroll-based exit transforms won't activate while scroll is locked.

useScroll / useTransform (Lines 115-123) compute parallax values based on scroll position, but body scroll is locked (overflow: hidden) during the entire "text" phase. These transforms only take effect after handleComplete unlocks scroll and transitions to "interactive" — at which point the component returns null (Line 127: !isVisible && phase === "interactive"). This means the parallax exit effect is effectively dead code.

If the parallax exit is intended as a scroll-away effect after the intro completes, the component unmounts before the user can scroll. Either remove the unused scroll transforms or delay setIsVisible(false) / return null to allow the scroll-away animation to play.

🤖 Prompt for AI Agents
In `@src/components/intro-section.tsx` around lines 114 - 127, The scroll-based
transforms created with useScroll/useTransform (scrollYProgress, scale, opacity,
blur, y tied to containerRef) never run because body scroll is locked during the
"text" phase and when phase becomes "interactive" the component immediately
returns null via the isVisible check; either remove these unused transforms or
postpone unmounting so the exit-parallax can run: update the logic around
isVisible/phase (the place that calls setIsVisible(false) / handleComplete and
the conditional if (!isVisible && phase === "interactive") return null) to delay
setting isVisible to false (or delay returning null) until the scroll unlock
occurs and the scrollYProgress-based animations reach their end (or a short
timeout after unlock), ensuring the useScroll/useTransform values have time to
animate before the component unmounts.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Md Rabiul Hossain Jahed <163085056+rabden@users.noreply.github.com>
The splash-screen.tsx file had a duplicate motion.div element on lines
118-123 which would cause a React render error. This was likely a merge
artifact that duplicated the opening motion.div tag.

Co-authored-by: Md Rabiul Hossain Jahed <undefined@users.noreply.github.com>
}

.animate-fade-in {
animation: custom-fade-in 3s 3s ease-out forwards;
Copy link
Contributor

Choose a reason for hiding this comment

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

LCP Performance Impact: The .animate-fade-in animation uses a 3s delay + 3s duration (total 6s of invisible content). Any above-the-fold content using this class will significantly harm Largest Contentful Paint (LCP) metrics — Google's target is < 2.5s.

Consider:

  1. Reducing the delay to sync with intro completion (~1.5s max)
  2. Reducing translateY(200px) to a smaller value (30-50px) for smaller viewports
  3. Triggering via JS after the intro completes rather than a fixed CSS delay
Suggested change
animation: custom-fade-in 3s 3s ease-out forwards;
.animate-fade-in {
animation: custom-fade-in 1.5s 1.5s ease-out forwards;
opacity: 0;
}


const videoSrc = isDark ? "/animated-logo.webm" : "/animated-logo-white.mp4";

if (!isVisible && phase === "interactive") 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.

Dead Code - Unused scroll transforms: The scroll-based transforms (lines 115-123: scale, opacity, blur, y) will never activate. The body scroll is locked during the "text" phase, and when phase becomes "interactive", the component returns null on this line.

These transforms are effectively dead code since users can't scroll while the intro is visible. Consider either:

  1. Removing the unused useScroll/useTransform hooks to reduce bundle size
  2. Implementing a scroll-away exit animation after intro completes (delaying unmount)

coderabbitai[bot]
coderabbitai bot previously requested changes Feb 7, 2026
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: 2

🤖 Fix all issues with AI agents
In `@src/components/splash-screen.tsx`:
- Around line 56-62: The dismiss function is duplicated — remove the local
`dismiss` declaration and replace its usages with the existing `dismissSplash`
to avoid redundant code; ensure `dismissSplash` (which calls `setIsDismissed`
and clears `initialTimerRef.current`, `dismissTimerRef.current`, and
`fallbackTimerRef.current`) remains exported/accessible in the component where
`dismiss` was used and delete the now-unused `dismiss` symbol to prevent linter
warnings.
- Around line 132-145: The video element used with videoRef and event handlers
handleVideoEnd/handleVideoError should include preload="none" so the browser
won't eagerly download /animated-logo.webm before showVideo mounts it; add
preload="none" to the <video> attributes (next to autoPlay, muted, playsInline)
to defer fetching until the element is actually inserted/played.

@morph-subagents
Copy link

🤖 Morph Preview Test

Preview URL: https://volvox-website-8crg663ew-volvox-llc.vercel.app

AI Summary

Issues:

  • None found

Verified:

  • Homepage intro animation completes, revealing a scrollable hero section with a custom blue-ish scrollbar.
  • Background particle animations are active and visible on the homepage.
  • Blog, Product, and Team detail pages (including individual profiles) load correctly without errors.
  • Privacy Policy and Terms of Service pages are functional and display correct content.

Recording

Preview Recording

View full session →


Automated testing by Morph

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