Skip to content

Redesign landing page with scroll navigation and animations#62

Merged
Ross1116 merged 5 commits into
mainfrom
staging
May 28, 2026
Merged

Redesign landing page with scroll navigation and animations#62
Ross1116 merged 5 commits into
mainfrom
staging

Conversation

@Ross1116

@Ross1116 Ross1116 commented May 27, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • New responsive landing header with mobile menu and updated hero layouts
    • Redesigned hero and product preview cards with visual/background effects
    • Tabbed showcase with desktop/mobile/combined views
    • New animated background and ripple effects; deferred below‑fold client bundle for faster load
    • Contact CTA now opens the demo modal and forwards optional click handlers
  • Style / Content

    • Updated site title and description copy across metadata and social previews
  • Removed

    • ROI calculator and its interactive controls removed from the landing page

Review Change Stack

@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
sitespace-app Ready Ready Preview, Comment May 28, 2026 3:19am

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0475c9d-ab3d-4371-8a63-9259c8b3d338

📥 Commits

Reviewing files that changed from the base of the PR and between cf88f0b and f2379e0.

📒 Files selected for processing (9)
  • src/components/landing/ContactModal.tsx
  • src/components/landing/LandingHeader.tsx
  • src/components/landing/LandingPage.tsx
  • src/components/landing/LandingPageBelowFoldClient.tsx
  • src/components/landing/LandingPageOneToOne.module.css
  • src/components/landing/ShowcaseSection.tsx
  • src/components/landing/TopBar.tsx
  • src/components/ui/aurora-background.tsx
  • tailwind.config.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/components/landing/ContactModal.tsx
  • tailwind.config.ts
  • src/components/ui/aurora-background.tsx
  • src/components/landing/ShowcaseSection.tsx
  • src/components/landing/TopBar.tsx
  • src/components/landing/LandingHeader.tsx
  • src/components/landing/LandingPage.tsx
  • src/components/landing/LandingPageOneToOne.module.css

📝 Walkthrough

Walkthrough

This PR replaces the legacy LandingPage with LandingPageOneToOne, updates app metadata, adds Tailwind theming and visual primitives (Aurora, ripple grid), implements a scroll-aware header and modal keyframe animations, introduces idle-gated hero/grid components, and adds a dynamic below-the-fold client bundle with reveal/progress animations and a tabbed showcase.

Changes

Landing Page Redesign

Layer / File(s) Summary
App-level routing and metadata
src/app/layout.tsx, src/app/page.tsx
App metadata (title/default/template, description, Open Graph, Twitter) updated and HomePage now renders LandingPageOneToOne.
Design system foundation: Tailwind plugin and UI primitives
tailwind.config.ts, src/components/ui/aurora-background.tsx, src/components/ui/background-ripple-effect.tsx
Adds Tailwind plugin to expose flattened color CSS variables; AuroraBackground for layered gradient animations; BackgroundRippleEffect renders distance-based ripple grid used by hero visuals.
Navigation and modal styling
src/components/landing/LandingHeader.tsx, src/components/landing/TopBar.tsx, src/components/landing/ContactModal.tsx
New LandingHeader (scroll-aware, mobile menu with body-lock); TopBar refactored to fixed nav links and light/dark switching; ContactModal/DemoModalProvider use class/keyframe animations and updated CTA onClick forwarding.
Hero section components
src/components/landing/LandingHero.tsx, src/components/landing/LandingHeroGrid.tsx, src/components/landing/DashboardHero.tsx
LandingHero composes Aurora and hero copy/CTA; LandingHeroGrid defers render until idle and renders the ripple grid; DashboardHero simplified to static preview with ProductPeek cards.
Landing page structure, styles, and exports
src/components/landing/LandingPageOneToOne.module.css, src/components/landing/LandingPageOneToOne.tsx, src/components/landing/LandingPage.tsx, src/components/landing/LandingPage.css
Adds comprehensive CSS module and LandingPageOneToOne server page (with dynamic below-fold loading); refactors LandingPage into new section-based layout and exports reusable frame components and styling constants; removes obsolete CSS rules.
Below-the-fold content and showcase
src/components/landing/LandingPageBelowFoldClient.tsx, src/components/landing/ShowcaseSection.tsx
Adds client-side below-fold bundle with IntersectionObserver-driven fade-in and progress-fill animations, helper presentational components, and a tabbed ShowcaseSection (desktop/mobile/combined).

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

"🐰 I bounced in with a brand new page,
Aurora glows and ripples engage,
Menus slide and modals spring,
Tabs reveal the features we bring,
Hooray — the landing hops on stage!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch staging

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.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Redesign landing page with scroll navigation and animations May 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
src/components/landing/TopBar.tsx (1)

173-176: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Close the mobile drawer when “Book a Demo” is tapped.

On Line 173, the CTA opens the modal but leaves the mobile menu open. Close the drawer on the same interaction to avoid stacked overlays and stale menu state.

Suggested fix
-        <DemoRequestCTA
-          label="Book a Demo"
-          className="cursor-pointer block mt-5 w-full text-center bg-amber-500 text-black rounded-full px-6 py-3 text-lg font-semibold hover:bg-amber-400 transition-colors"
-        />
+        <div onClick={closeMenu}>
+          <DemoRequestCTA
+            label="Book a Demo"
+            className="cursor-pointer block mt-5 w-full text-center bg-amber-500 text-black rounded-full px-6 py-3 text-lg font-semibold hover:bg-amber-400 transition-colors"
+          />
+        </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/TopBar.tsx` around lines 173 - 176, The "Book a Demo"
DemoRequestCTA currently opens the modal but leaves the mobile drawer open;
update the TopBar where DemoRequestCTA is rendered to provide an onClick handler
that both opens the demo modal and closes the mobile drawer (e.g., call
openDemoModal() or showDemoModal() then setIsMobileMenuOpen(false) /
closeMobileDrawer()). Specifically, modify the DemoRequestCTA invocation in
TopBar.tsx to pass a handler that calls the existing modal-opening function and
then the drawer-close state updater (or invokes the drawer close function) so a
single tap closes the menu and opens the demo modal.
🧹 Nitpick comments (4)
src/components/ui/aurora-background.tsx (1)

18-67: ⚡ Quick win

Use a neutral wrapper instead of <main> in this reusable component.

Keeping <main> inside a shared UI primitive can produce multiple main landmarks when composed into pages.

Suggested fix
-      <main>
+      <div>
         <div
           className={cn(
             "relative flex h-[100vh] w-full flex-col items-center justify-center overflow-hidden bg-zinc-50 text-slate-950 transition-bg",
             className
           )}
@@
-      </main>
+      </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/aurora-background.tsx` around lines 18 - 67, This component
file (aurora-background.tsx) uses a <main> wrapper which can create multiple
main landmarks when reused; replace the root <main> with a neutral container
(e.g., <div> or <> fragment) so the reusable AuroraBackground component does not
introduce a main landmark, and ensure you move the existing
props/className/style spreading to that neutral wrapper (look for the JSX block
starting with <main> and the inner div using cn(...) to apply styles).
src/components/landing/LandingPageBelowFoldClient.tsx (2)

32-72: ⚡ Quick win

Consider unobserving elements after animation triggers to improve performance.

The IntersectionObserver continues tracking elements even after they've animated in. When users scroll up and down, the callbacks fire repeatedly:

  • Fade-in elements harmlessly re-add the visible class
  • Progress bars could re-animate on every re-entry (lines 60-66)

This creates unnecessary callback overhead and potential visual issues.

⚡ Suggested optimization
 const fadeObserver = new IntersectionObserver(
   (entries) => {
     entries.forEach((entry) => {
       if (!entry.isIntersecting) return;
       (entry.target as HTMLElement).classList.add(styles.visible);
+      fadeObserver.unobserve(entry.target);
     });
   },
   { threshold: 0.1, rootMargin: "0px 0px -100px 0px" },
 );
 const progressObserver = new IntersectionObserver(
   (entries) => {
     entries.forEach((entry) => {
       if (!entry.isIntersecting) return;

       const fills = Array.from(
         (entry.target as HTMLElement).querySelectorAll<HTMLElement>(
           "[data-progress-fill]",
         ),
       );

       fills.forEach((fill) => {
         const width = fill.getAttribute("data-width") ?? "0%";
         fill.style.width = "0%";
         window.setTimeout(() => {
           fill.style.width = width;
         }, 100);
       });
+      progressObserver.unobserve(entry.target);
     });
   },
   { threshold: 0.5 },
 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/LandingPageBelowFoldClient.tsx` around lines 32 - 72,
The observers keep tracking elements after their animations run; update the
IntersectionObserver callbacks for fadeObserver and progressObserver to
unobserve the entry.target once its animation has been triggered: in the
fadeObserver callback (where you add styles.visible) call
fadeObserver.unobserve(entry.target) after adding the class, and in the
progressObserver callback (after setting widths / scheduling the timeout for
fills) call progressObserver.unobserve(entry.target) so fills won’t re-animate
on repeated intersections; refer to the existing fadeObserver, progressObserver,
fadeTargets, progressTargets and fills identifiers to locate and modify the
callbacks.

652-869: 💤 Low value

Consider reusing the ShowcaseSection component to reduce duplication.

This inline showcase implementation (lines 652-869) provides similar functionality to the standalone ShowcaseSection.tsx component:

  • Both offer tabbed desktop/mobile/combined views
  • Both display the same screenshot assets
  • ShowcaseSection already uses next/image optimization

If the styling differences are the only distinction, consider:

  1. Extracting common logic to a shared hook
  2. Making ShowcaseSection accept style props/className overrides
  3. Using ShowcaseSection here and applying CSS module classes as needed

This would centralize the showcase logic and benefit from the image optimizations already implemented in ShowcaseSection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/LandingPageBelowFoldClient.tsx` around lines 652 -
869, This inline showcase duplicates logic from the existing ShowcaseSection
component; replace the block rendering the tabbed showcase (the JSX that manages
showcaseView and setShowcaseView and the three role="tabpanel" panes) by reusing
ShowcaseSection: import ShowcaseSection, pass in className/style overrides (e.g.
styles.sectionSpacing, styles.showcaseContent, styles.desktopFrame,
styles.mobileFrame) and any text props needed, or alternatively extract the
local state/logic into a shared hook (e.g. useShowcaseView) used by both files;
ensure you use ShowcaseSection's next/image usage (image optimization) instead
of raw <img> and forward any aria/tab props through the component so behavior
and accessibility remain identical.
src/components/landing/LandingPage.tsx (1)

20-38: 💤 Low value

Unused constant MEDIUM can be removed.

The MEDIUM constant is defined on line 29 but is never used anywhere in the file. Consider removing it to avoid dead code.

♻️ Proposed fix
 const LARGE =
   "text-[clamp(2rem,6vw,5rem)] font-bold leading-[1.2] tracking-tight";

-const MEDIUM = "text-[clamp(1.5rem,4vw,3rem)] font-semibold leading-[1.3]";
-
 const APPLE =
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/LandingPage.tsx` around lines 20 - 38, Remove the
unused MEDIUM constant definition to eliminate dead code: delete the MEDIUM
constant declaration (the string assigned to the MEDIUM identifier) from
LandingPage.tsx and ensure no references to MEDIUM remain elsewhere in the
component (leave other constants like GIANT, LARGE, APPLE, BADGE,
CHECK_ICON_CLS, and FADE intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/landing/LandingHeader.tsx`:
- Around line 150-153: The mobile menu container in LandingHeader.tsx (the div
with className using styles.mobileMenu and styles.active, controlled by
isMenuOpen) currently only sets aria-hidden which doesn't prevent keyboard focus
when closed; update the component to apply inert (or otherwise remove
focusability) to that container when isMenuOpen is false—either add the inert
attribute (with an inert polyfill if needed) or programmatically set tabIndex=-1
/ disable focusable children while closed; apply the same change to the other
mobile menu instance covering the 154-195 block so links/buttons inside the
off-canvas drawer cannot be tabbed when closed.

In `@src/components/landing/LandingPage.tsx`:
- Around line 483-509: The Tailwind decimal utility classes in MobileFrame
(e.g., max-w-93.75, rounded-12.5, h-6.25, rounded-b-5, rounded-9.5) may not be
generated; replace them with bracketed explicit values (for example
max-w-[375px], rounded-[50px], h-[25px], rounded-b-[20px], rounded-[38px]) in
the JSX returned by the MobileFrame component so the intended pixel sizes are
applied reliably while preserving the existing class order and merging with
cn(..., APPLE, "shine", className).

In `@src/components/landing/LandingPageBelowFoldClient.tsx`:
- Around line 171-176: Replace the plain <img> tags in
LandingPageBelowFoldClient (e.g., the image at lines showing src
"/static/images/bookingspage.png" and the other occurrences noted: feature
section images, showcase images, PhoneShot helper) with Next.js Image from
'next/image': add an import for Image at the top of the component, swap each
<img> to <Image> using the same src and alt, remove HTML-only props like loading
and decoding, and provide either explicit width and height or use
layout="responsive"/fill with appropriate sizes/aspect ratio classes to avoid
layout shift; preserve any className and styling and apply responsive sizes
(sizes prop) where relevant. Ensure every replaced image uses the Image
component (including occurrences referenced: 200-205, 749-754, 787-791, 830-835,
849-854, and the PhoneShot helper) so Next.js automatic optimizations are
enabled.

In `@src/components/landing/LandingPageOneToOne.module.css`:
- Around line 168-173: Rename the non-kebab keyframe identifier `@keyframes`
fadeInUp to kebab-case (e.g., `@keyframes` fade-in-up) and update every
animation/animation-name reference in this stylesheet that points to "fadeInUp"
(including any uses in classes around the blocks originally at lines ~165-166,
~743-744 and ~750-759) to the new kebab-case name so linting passes; ensure both
the `@keyframes` declaration and all animation/animation-name/animation shorthand
usages (and any vendor-prefixed variants) are changed consistently to the new
identifier.
- Around line 706-707: The CSS rule `.active` is empty and triggers stylelint's
block-no-empty; either remove the empty `.active {}` entirely or keep the class
but add a harmless declaration (e.g., `content: ""` or `display: inline-block`)
so the class remains exportable; update the rule named `.active` accordingly and
run stylelint to confirm the violation is resolved.

In `@src/components/landing/ShowcaseSection.tsx`:
- Line 36: The data-fade-in attributes on the ShowcaseSection's root and the
other div are unused (FADE constant uses data-[visible] not data-fade-in) so
either remove the data-fade-in attributes from the JSX in ShowcaseSection or, if
these attributes are intentionally read by a parent observer (e.g.,
LandingPageBelowFoldClient), add a one-line inline comment above each div
stating that data-fade-in is consumed by the parent observer; update the
component accordingly and ensure tests/docs reflect the chosen approach.
- Line 41: In the ShowcaseSection component update all occurrences of the
inconsistent brand capitalization from "Sitespace" to the official "SiteSpace":
change the heading text ("Explore Sitespace") and all image alt attributes that
reference the brand (the alt strings in the ShowcaseSection JSX around the
images) so they consistently use "SiteSpace" throughout the component.

In `@tailwind.config.ts`:
- Line 2: The code imports the internal function flattenColorPalette from
tailwindcss/lib/util/flattenColorPalette which is unstable; remove that import
and instead derive palettes from theme('colors') inside your Tailwind plugin (or
implement a small local flatten helper), e.g., access colors via the plugin
callback using theme('colors') and then walk/flatten the returned object to
produce the palette values you need; update any usage sites referencing
flattenColorPalette to call your new local helper or the flattened
theme('colors') object (search for flattenColorPalette references in
tailwind.config.ts and replace them).

---

Outside diff comments:
In `@src/components/landing/TopBar.tsx`:
- Around line 173-176: The "Book a Demo" DemoRequestCTA currently opens the
modal but leaves the mobile drawer open; update the TopBar where DemoRequestCTA
is rendered to provide an onClick handler that both opens the demo modal and
closes the mobile drawer (e.g., call openDemoModal() or showDemoModal() then
setIsMobileMenuOpen(false) / closeMobileDrawer()). Specifically, modify the
DemoRequestCTA invocation in TopBar.tsx to pass a handler that calls the
existing modal-opening function and then the drawer-close state updater (or
invokes the drawer close function) so a single tap closes the menu and opens the
demo modal.

---

Nitpick comments:
In `@src/components/landing/LandingPage.tsx`:
- Around line 20-38: Remove the unused MEDIUM constant definition to eliminate
dead code: delete the MEDIUM constant declaration (the string assigned to the
MEDIUM identifier) from LandingPage.tsx and ensure no references to MEDIUM
remain elsewhere in the component (leave other constants like GIANT, LARGE,
APPLE, BADGE, CHECK_ICON_CLS, and FADE intact).

In `@src/components/landing/LandingPageBelowFoldClient.tsx`:
- Around line 32-72: The observers keep tracking elements after their animations
run; update the IntersectionObserver callbacks for fadeObserver and
progressObserver to unobserve the entry.target once its animation has been
triggered: in the fadeObserver callback (where you add styles.visible) call
fadeObserver.unobserve(entry.target) after adding the class, and in the
progressObserver callback (after setting widths / scheduling the timeout for
fills) call progressObserver.unobserve(entry.target) so fills won’t re-animate
on repeated intersections; refer to the existing fadeObserver, progressObserver,
fadeTargets, progressTargets and fills identifiers to locate and modify the
callbacks.
- Around line 652-869: This inline showcase duplicates logic from the existing
ShowcaseSection component; replace the block rendering the tabbed showcase (the
JSX that manages showcaseView and setShowcaseView and the three role="tabpanel"
panes) by reusing ShowcaseSection: import ShowcaseSection, pass in
className/style overrides (e.g. styles.sectionSpacing, styles.showcaseContent,
styles.desktopFrame, styles.mobileFrame) and any text props needed, or
alternatively extract the local state/logic into a shared hook (e.g.
useShowcaseView) used by both files; ensure you use ShowcaseSection's next/image
usage (image optimization) instead of raw <img> and forward any aria/tab props
through the component so behavior and accessibility remain identical.

In `@src/components/ui/aurora-background.tsx`:
- Around line 18-67: This component file (aurora-background.tsx) uses a <main>
wrapper which can create multiple main landmarks when reused; replace the root
<main> with a neutral container (e.g., <div> or <> fragment) so the reusable
AuroraBackground component does not introduce a main landmark, and ensure you
move the existing props/className/style spreading to that neutral wrapper (look
for the JSX block starting with <main> and the inner div using cn(...) to apply
styles).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 862b34cb-2512-4ee3-a4c3-25d706763846

📥 Commits

Reviewing files that changed from the base of the PR and between b6a23f1 and cf88f0b.

📒 Files selected for processing (18)
  • src/app/layout.tsx
  • src/app/page.tsx
  • src/components/landing/ContactModal.tsx
  • src/components/landing/DashboardHero.tsx
  • src/components/landing/LandingHeader.tsx
  • src/components/landing/LandingHero.tsx
  • src/components/landing/LandingHeroGrid.tsx
  • src/components/landing/LandingPage.css
  • src/components/landing/LandingPage.tsx
  • src/components/landing/LandingPageBelowFoldClient.tsx
  • src/components/landing/LandingPageOneToOne.module.css
  • src/components/landing/LandingPageOneToOne.tsx
  • src/components/landing/ROICalculator.tsx
  • src/components/landing/ShowcaseSection.tsx
  • src/components/landing/TopBar.tsx
  • src/components/ui/aurora-background.tsx
  • src/components/ui/background-ripple-effect.tsx
  • tailwind.config.ts
💤 Files with no reviewable changes (1)
  • src/components/landing/ROICalculator.tsx

Comment thread src/components/landing/LandingHeader.tsx
Comment thread src/components/landing/LandingPage.tsx
Comment thread src/components/landing/LandingPageBelowFoldClient.tsx Outdated
Comment thread src/components/landing/LandingPageOneToOne.module.css Outdated
Comment thread src/components/landing/LandingPageOneToOne.module.css
Comment thread src/components/landing/ShowcaseSection.tsx Outdated
Comment thread src/components/landing/ShowcaseSection.tsx Outdated
Comment thread tailwind.config.ts Outdated
@Ross1116 Ross1116 merged commit 671704a into main May 28, 2026
3 checks passed
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.

1 participant