Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/app/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,33 @@
animation: star-glow 2s ease-in-out infinite;
pointer-events: none;
}

/* Trusted-by logo strip: infinite horizontal marquee.
The strip renders the logo set twice; translating the inner row by -50%
slides the first copy off-screen exactly as the second copy moves into
its place, producing a seamless loop. Each set carries pr-5 so the
trailing gap is baked into the 50% math. */
@keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}

.animate-marquee {
animation: marquee 50s linear infinite;
will-change: transform;
}

.animate-marquee:hover,
.animate-marquee:focus-within {
animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
.animate-marquee {
animation: none;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reduced motion ignores quote rotation

Medium Severity

The new prefers-reduced-motion rule disables only the logo marquee animation. The carousel still runs its 8s setInterval advance and 300ms opacity fades, so users who request reduced motion see a static strip but quotes keep changing—contrary to the PR’s stated reduced-motion support for the carousel.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2042ef8. Configure here.

}
3 changes: 0 additions & 3 deletions packages/app/src/components/intro-section.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Quote } from 'lucide-react';

import { Card } from '@/components/ui/card';
import { MinecraftSplash } from '@/components/minecraft/minecraft-splash';
import { QuoteCarousel } from '@/components/quote-carousel';
Expand All @@ -17,7 +15,6 @@ export function IntroSection() {
<section>
<Card data-testid="intro-section">
<div className="relative flex items-start gap-2 mb-4">
<Quote className="size-5 shrink-0 mt-1 text-brand" />
<h2 className="text-lg font-semibold">
Open Source Continuous Inference Benchmark Trusted by GigaWatt Token Factories
</h2>
Expand Down
164 changes: 116 additions & 48 deletions packages/app/src/components/quote-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import Link from 'next/link';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Quote } from 'lucide-react';

import { track } from '@/lib/analytics';
import { ExternalLinkIcon } from '@/components/ui/external-link-icon';
Expand Down Expand Up @@ -66,36 +67,42 @@ function buildCompanyQuotes(quotes: CarouselQuote[], order?: string[]): CompanyE
return shuffleArray(entries);
}

function QuoteBlock({ quote }: { quote: CarouselQuote }) {
function QuoteText({ quote }: { quote: CarouselQuote }) {
return (
<blockquote className="w-full">
<p className="text-sm lg:text-base leading-relaxed text-muted-foreground italic">
&ldquo;{highlightBrand(quote.text)}&rdquo;
<blockquote className="m-0 p-0 border-0">
<p className="text-sm lg:text-base leading-relaxed text-muted-foreground">
<Quote className="inline-block mr-2 -mt-1 size-4 text-brand align-middle" aria-hidden="true" />
{highlightBrand(quote.text)}
</p>
<footer className="mt-3 flex items-center gap-3">
<CompanyLogo org={quote.org} logo={quote.logo} />
<div className="h-12 w-0.5 bg-brand" />
<div className="text-sm">
{quote.link ? (
<a
href={quote.link}
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-foreground hover:text-brand transition-colors group"
>
<span className="group-hover:underline">{quote.name}</span>
<ExternalLinkIcon />
</a>
) : (
<span className="font-semibold text-foreground">{quote.name}</span>
)}
<span className="block text-muted-foreground text-xs">{quote.title}</span>
</div>
</footer>
</blockquote>
);
}

function QuoteAuthor({ quote }: { quote: CarouselQuote }) {
return (
<div className="flex items-center gap-3">
<CompanyLogo org={quote.org} logo={quote.logo} />
<div className="h-12 w-0.5 bg-brand" />
<div className="text-sm">
{quote.link ? (
<a
href={quote.link}
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-foreground hover:text-brand transition-colors group"
>
<span className="group-hover:underline">{quote.name}</span>
<ExternalLinkIcon />
</a>
) : (
<span className="font-semibold text-foreground">{quote.name}</span>
)}
<span className="block text-muted-foreground text-xs">{quote.title}</span>
</div>
</div>
);
}

export function QuoteCarousel({
quotes,
overrides = {},
Expand Down Expand Up @@ -160,61 +167,122 @@ export function QuoteCarousel({

return (
<div
className="flex flex-col gap-4"
className="flex flex-col gap-5"
onMouseEnter={() => {
hovering.current = true;
}}
onMouseLeave={() => {
hovering.current = false;
}}
>
{/* Org name strip */}
<div className="flex flex-wrap justify-center gap-x-6 md:gap-x-8 gap-y-2 mx-4">
{entries.map((e, i) => (
<button
key={e.org}
type="button"
onClick={() => goTo(i)}
className={`text-xs font-semibold tracking-wide uppercase transition-colors duration-200 ${
i === activeIndex ? 'text-foreground' : 'text-[#808488] hover:text-muted-foreground'
}`}
>
{labels[e.org] ?? e.org}
</button>
))}
{/* Org logo strip — infinite marquee carousel; clickable, active is highlighted.
Each set carries `pr-5` so the trailing gap is baked into the 50%
translate, keeping the loop seamless. */}
<div className="overflow-hidden">
<div className="flex w-max animate-marquee">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Marquee and quote pause diverge

Medium Severity

The quote carousel's auto-advance and logo marquee animations pause independently. The quote rotation pauses on hover of the entire carousel, while the logo strip only pauses on hover or focus of the marquee element itself. This leads to scenarios where one component pauses while the other continues, creating an inconsistent user experience.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2042ef8. Configure here.

{[0, 1].map((copy) => (
<div
key={copy}
className="flex items-center gap-x-5 pr-5 shrink-0"
aria-hidden={copy === 1 ? true : undefined}
>
{entries.map((e, i) => {
const isActive = i === activeIndex;
return (
<button
key={e.org}
type="button"
onClick={() => goTo(i)}
title={labels[e.org] ?? e.org}
aria-label={`Show quote from ${labels[e.org] ?? e.org}`}
tabIndex={copy === 1 ? -1 : undefined}
className={`group flex h-10 shrink-0 items-center justify-center px-2 rounded-md transition-all duration-200 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/40 ${
isActive
? 'bg-accent/60'
: 'opacity-50 hover:opacity-100'
}`}
>
{e.quote.logo ? (
<img
src={`/logos/${e.quote.logo}`}
alt={labels[e.org] ?? e.org}
className={`h-6 sm:h-7 max-w-[110px] object-contain transition-all duration-200 ${
isActive ? 'grayscale-0 dark:invert' : 'grayscale dark:invert'
}`}
loading="lazy"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Strip logos lack error fallback

Low Severity

The marquee strip renders sponsor logos with a plain img and no onError handler, while the active author row below uses CompanyLogo, which swaps to initials when the asset fails. A missing or broken file can show a broken image in the strip next to a correct fallback in the author block.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2042ef8. Configure here.

) : (
<span
className={`text-xs font-semibold tracking-wide uppercase ${
isActive ? 'text-foreground' : 'text-muted-foreground'
}`}
>
{labels[e.org] ?? e.org}
</span>
)}
</button>
);
})}
</div>
))}
</div>
</div>

{/* All quotes stacked in same grid cell — tallest sets height */}
<div className="grid items-center">
{/* Stacked quote texts — tallest sets the cell height. */}
<div className="grid items-start">
{entries.map((e, i) => {
const isActive = i === activeIndex;
return (
<div
key={e.org}
className={`col-start-1 row-start-1 ${
isActive
? `transition-opacity duration-300 ease-in-out ${fading ? 'opacity-0' : 'opacity-100'}`
? `transition-opacity duration-300 ease-in-out ${
fading ? 'opacity-0' : 'opacity-100'
}`
: 'opacity-0 invisible pointer-events-none'
}`}
aria-hidden={!isActive}
>
<QuoteBlock quote={e.quote} />
<QuoteText quote={e.quote} />
</div>
);
})}
</div>

{moreHref && (
<div className="flex justify-end">
{/* Bottom row: active quote's author (left) and "See more" link (right),
aligned to the same bottom baseline via items-end. */}
<div className="flex items-end justify-between gap-4">
<div className="grid items-end flex-1 min-w-0">
{entries.map((e, i) => {
const isActive = i === activeIndex;
return (
<div
key={e.org}
className={`col-start-1 row-start-1 ${
isActive
? `transition-opacity duration-300 ease-in-out ${
fading ? 'opacity-0' : 'opacity-100'
}`
: 'opacity-0 invisible pointer-events-none'
}`}
aria-hidden={!isActive}
>
<QuoteAuthor quote={e.quote} />
</div>
);
})}
</div>
{moreHref && (
<Link
href={moreHref}
className="text-xs font-bold text-brand hover:underline"
className="text-xs font-bold text-brand hover:underline shrink-0"
onClick={() => track('quote_carousel_see_more_clicked')}
>
See more supporters &rarr;
</Link>
</div>
)}
)}
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion packages/app/src/components/quotes/quotes-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function QuoteCard({
}) {
const content = (
<blockquote className="space-y-4">
<p className="text-base lg:text-lg leading-relaxed text-muted-foreground italic">
<p className="text-base lg:text-lg leading-relaxed text-muted-foreground">
&ldquo;{highlightBrand(text)}&rdquo;
</p>
<footer className="flex items-center gap-3">
Expand Down
Loading