67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useRef, useState } from "react";
|
||
|
||
type Item = { v: string; up?: boolean; label: string };
|
||
|
||
/**
|
||
* A Bloomberg-style revenue ticker tape. Infinite CSS marquee with live-
|
||
* feeling deltas. The visible tape is aria-hidden (decorative motion); a
|
||
* single screen-reader summary conveys the same facts statically.
|
||
*
|
||
* Numbers are illustrative SAMPLES (matches the content note).
|
||
*/
|
||
const ITEMS: Item[] = [
|
||
{ v: "+$2.41M", up: true, label: "Q ARR added" },
|
||
{ v: "3.8×", up: true, label: "ROAS" },
|
||
{ v: "−34%", up: true, label: "CPA" },
|
||
{ v: "+217%", up: true, label: "Demo reqs" },
|
||
{ v: "+183%", up: true, label: "Organic" },
|
||
{ v: "+128", up: true, label: "Bookings/mo" },
|
||
{ v: "+52%", up: true, label: "ROAS 90d" },
|
||
{ v: "92%", up: true, label: "Retention" },
|
||
{ v: "+$40M", up: true, label: "Client revenue" },
|
||
];
|
||
|
||
export default function Ticker() {
|
||
const [tick, setTick] = useState(0);
|
||
const reduce = useRef(false);
|
||
|
||
// gentle "last digit flicker" to feel live (skipped on reduced motion)
|
||
useEffect(() => {
|
||
reduce.current = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||
if (reduce.current) return;
|
||
const id = setInterval(() => setTick((t) => t + 1), 2600);
|
||
return () => clearInterval(id);
|
||
}, []);
|
||
|
||
const Row = ({ aria }: { aria: boolean }) => (
|
||
<ul className="ticker__row" aria-hidden={aria ? undefined : true}>
|
||
{ITEMS.map((it, i) => (
|
||
<li className="ticker__item" key={i}>
|
||
<span className="ticker__arr" aria-hidden="true">
|
||
{it.up ? "▲" : "▼"}
|
||
</span>
|
||
<span className="ticker__val">{it.v}</span>
|
||
<span className="ticker__lab">{it.label}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
|
||
return (
|
||
<div className="ticker" data-tick={tick % 2}>
|
||
{/* SR-only static summary of the same data */}
|
||
<p className="sr-only">
|
||
Sample results across recent client programs: 3.8 times average return
|
||
on ad spend, plus 217 percent qualified demo requests, plus 183 percent
|
||
organic traffic, 92 percent client retention, and over 40 million
|
||
dollars in client revenue generated.
|
||
</p>
|
||
<div className="ticker__track" aria-hidden="true">
|
||
<Row aria />
|
||
<Row aria />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|