Skip to content

doc: refactor testimonials section with dual scrolling marquees#1374

Open
franky47 wants to merge 5 commits intonextfrom
chore/refactor-testimonials-marquee
Open

doc: refactor testimonials section with dual scrolling marquees#1374
franky47 wants to merge 5 commits intonextfrom
chore/refactor-testimonials-marquee

Conversation

@franky47
Copy link
Copy Markdown
Member

@franky47 franky47 commented Mar 21, 2026

Tasks

Summary

Replaces the static CSS grid layout of the testimonials/quotes section on the landing page with an animated dual-marquee scrolling layout, inspired by @ncdai's testimonials-marquee component.

Changes

  • New dependency: Added react-fast-marquee to the docs package
  • New Marquee wrapper component (packages/docs/src/components/marquee.tsx): Reusable building blocks (Marquee, MarqueeContent, MarqueeFade, MarqueeItem) wrapping react-fast-marquee with gradient fade overlays and pause-on-hover behavior
  • New Testimonial compound component (packages/docs/src/components/testimonial.tsx): Semantic <figure>/<blockquote>/<figcaption> structure following @ncdai's pattern with avatar ring overlays and flexible layout
  • Refactored QuotesSection: Quotes are split into two rows scrolling in opposite directions (left and right), each with gradient fades on the edges. Quotes are rendered as linked cards pointing to their original tweets/posts

Visual behavior

  • Two horizontally scrolling marquee rows of testimonial cards
  • First row scrolls left, second row scrolls right
  • Both rows pause on hover so users can read individual quotes
  • Gradient fade overlays on left/right edges for a polished look
  • Each card links to the original tweet/source

Notes

  • The existing quote.tsx component is intentionally kept as-is in case it is used elsewhere
  • All 33 original quotes are preserved

Replace the static grid layout of the quotes/testimonials section
with an animated dual-marquee layout inspired by @ncdai's
testimonials-marquee component (chanhdai.com/components/testimonials-marquee).

- Add react-fast-marquee dependency
- Create reusable Marquee wrapper components (Marquee, MarqueeContent,
  MarqueeFade, MarqueeItem)
- Create Testimonial compound component following @ncdai's pattern
  (Testimonial, TestimonialQuote, TestimonialAuthor, etc.)
- Split the 33 quotes into two rows scrolling in opposite directions
- Add gradient fade overlays on marquee edges
- Marquees pause on hover for readability
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 21, 2026

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

Project Deployment Actions Updated (UTC)
nuqs Ready Ready Preview, Comment Mar 21, 2026 2:26pm

Request Review

@franky47 franky47 changed the title Refactor testimonials section with dual scrolling marquees doc: refactor testimonials section with dual scrolling marquees Mar 21, 2026
The Tailwind `[&_...]` selectors on the Marquee className prop were not
reliably propagating flex height through react-fast-marquee's internal
wrapper elements (.rfm-marquee-container, .rfm-marquee, .rfm-child).
Move the overrides to a global CSS layer with !important to ensure all
internal layers use align-items: stretch and flex display.
Copy link
Copy Markdown
Contributor

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

Refactors the docs landing page testimonials (“QuotesSection”) from a static grid into a dual-row, animated marquee presentation, adding reusable marquee/testimonial building blocks and the required dependency.

Changes:

  • Added react-fast-marquee and introduced a Marquee wrapper component with fade overlays and pause-on-hover.
  • Added a Testimonial compound component for semantic quote card structure.
  • Reworked QuotesSection to render two opposite-direction marquee rows and added global CSS tweaks for equal-height marquee children.

Reviewed changes

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

Show a summary per file
File Description
pnpm-lock.yaml Locks react-fast-marquee addition (and incidental lockfile updates).
packages/docs/package.json Adds the new marquee dependency to the docs package.
packages/docs/src/components/marquee.tsx New wrapper components around react-fast-marquee plus fade overlays/items.
packages/docs/src/components/testimonial.tsx New semantic testimonial compound components used by quote cards.
packages/docs/src/app/globals.css Adds react-fast-marquee class overrides to enforce equal-height cards.
packages/docs/src/app/(pages)/_landing/quotes/quotes-section.tsx Replaces the grid layout with two marquee rows and new quote card rendering.
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 +52 to +55
type QuoteData = {
text: React.ReactNode
author: {
name: string
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

QuoteData and getTextLength use React.ReactNode, but this module doesn’t import React (or ReactNode) anywhere. This will fail type-checking in TS. Import React/ReactNode (e.g., import type React from 'react' or import type { ReactNode } from 'react') or change the types to avoid the React. namespace.

Copilot uses AI. Check for mistakes.
Comment on lines +356 to +370
{firstRow.map((quote, i) => (
<MarqueeItem key={i} className="px-2">
<QuoteCard quote={quote} />
</MarqueeItem>
))}
</MarqueeContent>
<MarqueeFadeRight />
</Marquee>
<Marquee>
<MarqueeFadeLeft />
<MarqueeContent direction="right">
{secondRow.map((quote, i) => (
<MarqueeItem key={i} className="px-2">
<QuoteCard quote={quote} />
</MarqueeItem>
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

Using the array index as the React key here is unstable because the list is derived from a sorted array; any change to quote contents/order will remount many items and can cause marquee animation/state resets. Prefer a stable key such as quote.url (or a derived id from author+url).

Copilot uses AI. Check for mistakes.
Comment on lines +278 to +285
{
text: 'Big thanks to nuqs for making URL state management actually enjoyable! useState but synced with the URL? Type-safe? Works everywhere (Next.js, Remix, React Router)? Only 6kb? Happy to support such a well-crafted library',
author: {
name: 'Ajay Patel',
avatar: { service: 'x', handle: 'ajaypatel_aj' }
},
url: 'https://x.com/ajaypatel_aj/status/2004082719047778362'
},
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

The PR description says “All 33 original quotes are preserved”, but several quotes have been materially transformed from their original rich content (e.g., the Ajay Patel quote previously contained line breaks, a list, and emojis, and is now collapsed into a single plain string). Either keep the original ReactNode content for these entries, or update the PR description to match the new behavior/content normalization.

Copilot uses AI. Check for mistakes.
Comment on lines 61 to 64
"react": "catalog:react19",
"react-dom": "catalog:react19",
"react-fast-marquee": "^1.6.5",
"recharts": "3.8.0",
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

react-fast-marquee@1.6.5 declares peer deps for React/ReactDOM up to ^18, but this repo uses React 19 (catalog:react19). This will produce unmet peer dependency warnings (and may break installs in stricter CI setups) and is a compatibility risk. Please verify React 19 support (or upgrade to a version that declares React 19), otherwise consider an alternative marquee implementation or a pnpm override/packageExtension to relax the peer range intentionally.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +39
export function MarqueeFade({ className, ...props }: ComponentProps<'div'>) {
return (
<div
data-slot="marquee-fade"
className={cn(
'pointer-events-none absolute inset-y-0 z-10 w-16 sm:w-24',
className
)}
{...props}
/>
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

The fade overlay elements are purely decorative. Consider setting aria-hidden="true" on MarqueeFade (or the left/right variants) to ensure they’re ignored by assistive technologies.

Copilot uses AI. Check for mistakes.
- Import ReactNode type instead of using React.ReactNode namespace
- Use quote.url as stable React key instead of array index
- Add aria-hidden to decorative fade overlays
- Restore original rich content (emojis, JSX, line breaks) for quotes
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