Web InnoventixFreeCode

Gradient Stat

Original · free

gradient stats band

byWeb InnoventixReact + Tailwind
statxgradientstats
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/statx-gradient.json
statx-gradient.tsx
"use client";

import {
  type KeyboardEvent,
  useEffect,
  useId,
  useRef,
  useState,
} from "react";
import { motion, useInView, useReducedMotion } from "motion/react";

type Stat = {
  id: string;
  label: string;
  prefix?: string;
  suffix?: string;
  value: number;
  decimals: number;
  delta: string;
  trend: "up" | "down";
  positive: boolean;
  hint: string;
};

type Period = {
  id: string;
  label: string;
  caption: string;
  synced: string;
  stats: Stat[];
};

const PERIODS: Period[] = [
  {
    id: "30d",
    label: "Last 30 days",
    caption: "Rolling window, refreshed every hour",
    synced: "Synced 2 minutes ago",
    stats: [
      {
        id: "deploys",
        label: "Deploys shipped",
        value: 48912,
        decimals: 0,
        delta: "+12.4%",
        trend: "up",
        positive: true,
        hint: "Across 42 regions, zero-downtime rollouts",
      },
      {
        id: "revenue",
        label: "Revenue protected",
        prefix: "$",
        suffix: "M",
        value: 4.2,
        decimals: 1,
        delta: "+8.1%",
        trend: "up",
        positive: true,
        hint: "Auto-rollback caught it before customers noticed",
      },
      {
        id: "speed",
        label: "Median deploy time",
        suffix: "s",
        value: 38,
        decimals: 0,
        delta: "-6.0%",
        trend: "down",
        positive: true,
        hint: "p50 from git commit to live production traffic",
      },
      {
        id: "uptime",
        label: "Fleet uptime",
        suffix: "%",
        value: 99.98,
        decimals: 2,
        delta: "+0.02pt",
        trend: "up",
        positive: true,
        hint: "Measured against a four-nine availability SLA",
      },
    ],
  },
  {
    id: "ytd",
    label: "Year to date",
    caption: "Jan 1 through today, cumulative",
    synced: "Synced 6 minutes ago",
    stats: [
      {
        id: "deploys",
        label: "Deploys shipped",
        value: 612540,
        decimals: 0,
        delta: "+34.7%",
        trend: "up",
        positive: true,
        hint: "Across 42 regions, zero-downtime rollouts",
      },
      {
        id: "revenue",
        label: "Revenue protected",
        prefix: "$",
        suffix: "M",
        value: 58.6,
        decimals: 1,
        delta: "+41.2%",
        trend: "up",
        positive: true,
        hint: "Auto-rollback caught it before customers noticed",
      },
      {
        id: "speed",
        label: "Median deploy time",
        suffix: "s",
        value: 41,
        decimals: 0,
        delta: "-11.0%",
        trend: "down",
        positive: true,
        hint: "p50 from git commit to live production traffic",
      },
      {
        id: "uptime",
        label: "Fleet uptime",
        suffix: "%",
        value: 99.97,
        decimals: 2,
        delta: "+0.05pt",
        trend: "up",
        positive: true,
        hint: "Measured against a four-nine availability SLA",
      },
    ],
  },
  {
    id: "all",
    label: "Since launch",
    caption: "All time, from our first production deploy",
    synced: "Synced 11 minutes ago",
    stats: [
      {
        id: "deploys",
        label: "Deploys shipped",
        value: 3184900,
        decimals: 0,
        delta: "+212%",
        trend: "up",
        positive: true,
        hint: "Across 42 regions, zero-downtime rollouts",
      },
      {
        id: "revenue",
        label: "Revenue protected",
        prefix: "$",
        suffix: "M",
        value: 214,
        decimals: 1,
        delta: "+186%",
        trend: "up",
        positive: true,
        hint: "Auto-rollback caught it before customers noticed",
      },
      {
        id: "speed",
        label: "Median deploy time",
        suffix: "s",
        value: 47,
        decimals: 0,
        delta: "-3.0%",
        trend: "down",
        positive: true,
        hint: "p50 from git commit to live production traffic",
      },
      {
        id: "uptime",
        label: "Fleet uptime",
        suffix: "%",
        value: 99.95,
        decimals: 2,
        delta: "4-nine SLA",
        trend: "up",
        positive: true,
        hint: "Measured against a four-nine availability SLA",
      },
    ],
  },
];

function TrendArrow({ trend }: { trend: "up" | "down" }) {
  return (
    <svg
      viewBox="0 0 12 12"
      width={11}
      height={11}
      aria-hidden="true"
      className={trend === "down" ? "rotate-180" : ""}
    >
      <path d="M6 2 L10.2 9 L1.8 9 Z" fill="currentColor" />
    </svg>
  );
}

function CountUp({
  target,
  decimals,
  active,
  reduced,
}: {
  target: number;
  decimals: number;
  active: boolean;
  reduced: boolean;
}) {
  const [display, setDisplay] = useState(reduced ? target : 0);
  const displayRef = useRef(display);
  displayRef.current = display;

  useEffect(() => {
    if (reduced) {
      setDisplay(target);
      return;
    }
    if (!active) return;
    const from = displayRef.current;
    const start = performance.now();
    const duration = 1200;
    let raf = 0;
    const tick = (now: number) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setDisplay(from + (target - from) * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, active, reduced]);

  const formatted = display.toLocaleString("en-US", {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });

  return <span className="tabular-nums">{formatted}</span>;
}

export default function StatxGradient() {
  const reduced = useReducedMotion() ?? false;
  const sectionRef = useRef<HTMLDivElement>(null);
  const inView = useInView(sectionRef, { once: true, amount: 0.25 });

  const [periodIndex, setPeriodIndex] = useState(0);
  const btnRefs = useRef<(HTMLButtonElement | null)[]>([]);
  const period = PERIODS[periodIndex];

  const headingId = useId();
  const groupLabelId = useId();
  const captionId = useId();

  function handleKey(e: KeyboardEvent<HTMLButtonElement>, i: number) {
    const count = PERIODS.length;
    let next = i;
    if (e.key === "ArrowRight" || e.key === "ArrowDown") next = (i + 1) % count;
    else if (e.key === "ArrowLeft" || e.key === "ArrowUp")
      next = (i - 1 + count) % count;
    else if (e.key === "Home") next = 0;
    else if (e.key === "End") next = count - 1;
    else return;
    e.preventDefault();
    setPeriodIndex(next);
    btnRefs.current[next]?.focus();
  }

  return (
    <section
      aria-labelledby={headingId}
      className="relative w-full overflow-hidden bg-slate-50 px-4 py-20 sm:py-28 dark:bg-slate-950"
    >
      <style>{`
        @keyframes statxg-float {
          0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
          50% { transform: translate3d(0, -18px, 0) scale(1.08); }
        }
        @keyframes statxg-floatB {
          0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
          50% { transform: translate3d(16px, 12px, 0) scale(1.06); }
        }
        @keyframes statxg-sheen {
          0% { transform: translateX(-140%) skewX(-12deg); }
          55%, 100% { transform: translateX(560%) skewX(-12deg); }
        }
        @keyframes statxg-pulse {
          0%, 100% { opacity: 1; transform: scale(1); }
          50% { opacity: 0.35; transform: scale(1.9); }
        }
        .statxg-blobA { animation: statxg-float 9s ease-in-out infinite; }
        .statxg-blobB { animation: statxg-floatB 11s ease-in-out infinite; }
        .statxg-sheen { animation: statxg-sheen 7s ease-in-out infinite; }
        .statxg-dot { animation: statxg-pulse 2.4s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .statxg-blobA, .statxg-blobB, .statxg-sheen, .statxg-dot {
            animation: none !important;
          }
        }
      `}</style>

      <div ref={sectionRef} className="mx-auto max-w-6xl">
        <header className="mx-auto max-w-2xl text-center">
          <p className="text-sm font-semibold uppercase tracking-[0.2em] bg-gradient-to-r from-indigo-500 via-violet-500 to-sky-500 bg-clip-text text-transparent">
            Platform metrics
          </p>
          <h2
            id={headingId}
            className="mt-3 text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl dark:text-white"
          >
            Numbers that hold up in production
          </h2>
          <p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
            Every figure below comes straight from the control plane, not a
            slide deck. Switch the window to see how the fleet performs over
            time.
          </p>
        </header>

        <div className="mt-8 flex justify-center">
          <div
            role="radiogroup"
            aria-labelledby={groupLabelId}
            className="relative inline-flex items-center gap-1 rounded-full border border-slate-200 bg-slate-100 p-1 dark:border-slate-700 dark:bg-slate-800/70"
          >
            <span id={groupLabelId} className="sr-only">
              Select a reporting period
            </span>
            {PERIODS.map((p, i) => {
              const selected = i === periodIndex;
              return (
                <button
                  key={p.id}
                  ref={(el) => {
                    btnRefs.current[i] = el;
                  }}
                  type="button"
                  role="radio"
                  aria-checked={selected}
                  tabIndex={selected ? 0 : -1}
                  onClick={() => setPeriodIndex(i)}
                  onKeyDown={(e) => handleKey(e, i)}
                  className="relative rounded-full px-4 py-2 text-sm font-medium outline-none transition-colors focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-100 dark:focus-visible:ring-offset-slate-800"
                >
                  {selected && (
                    <motion.span
                      layoutId="statxg-pill"
                      aria-hidden="true"
                      className="absolute inset-0 rounded-full bg-white shadow-sm dark:bg-slate-950"
                      transition={
                        reduced
                          ? { duration: 0 }
                          : { type: "spring", stiffness: 420, damping: 34 }
                      }
                    />
                  )}
                  <span
                    className={
                      "relative z-10 " +
                      (selected
                        ? "text-slate-900 dark:text-white"
                        : "text-slate-500 dark:text-slate-400")
                    }
                  >
                    {p.label}
                  </span>
                </button>
              );
            })}
          </div>
        </div>

        <motion.div
          initial={reduced ? false : { opacity: 0, y: 26 }}
          animate={
            reduced
              ? undefined
              : inView
                ? { opacity: 1, y: 0 }
                : { opacity: 0, y: 26 }
          }
          transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
          className="relative mt-10 overflow-hidden rounded-3xl bg-gradient-to-br from-indigo-600 via-violet-600 to-sky-500 p-4 shadow-2xl shadow-indigo-500/25 ring-1 ring-white/10 sm:p-6 dark:from-indigo-800 dark:via-violet-900 dark:to-sky-800 dark:shadow-black/50"
        >
          <span
            aria-hidden="true"
            className="statxg-blobA pointer-events-none absolute -left-12 -top-16 h-56 w-56 rounded-full bg-white/25 blur-3xl"
          />
          <span
            aria-hidden="true"
            className="statxg-blobB pointer-events-none absolute -bottom-20 right-0 h-64 w-64 rounded-full bg-sky-300/30 blur-3xl"
          />
          <span
            aria-hidden="true"
            className="statxg-sheen pointer-events-none absolute inset-y-0 left-0 w-1/5 bg-gradient-to-r from-transparent via-white/15 to-transparent"
          />

          <div className="relative">
            <div className="flex flex-wrap items-center justify-between gap-3 px-2 pb-4 pt-1 sm:px-3">
              <span className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-xs font-semibold text-white ring-1 ring-white/25">
                <span className="relative flex h-2 w-2">
                  <span className="statxg-dot absolute inline-flex h-full w-full rounded-full bg-emerald-300" />
                  <span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-300" />
                </span>
                Live control plane
              </span>
              <span
                id={captionId}
                aria-live="polite"
                className="text-xs font-medium text-white/80"
              >
                {period.caption}
              </span>
            </div>

            <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4 lg:grid-cols-4">
              {period.stats.map((stat, i) => (
                <motion.div
                  key={stat.id}
                  initial={reduced ? false : { opacity: 0, y: 16 }}
                  animate={
                    reduced
                      ? undefined
                      : inView
                        ? { opacity: 1, y: 0 }
                        : { opacity: 0, y: 16 }
                  }
                  transition={{
                    duration: 0.5,
                    ease: [0.22, 1, 0.36, 1],
                    delay: reduced ? 0 : 0.12 + i * 0.08,
                  }}
                  className="group relative flex flex-col gap-3 rounded-2xl bg-white/10 p-5 ring-1 ring-white/15 backdrop-blur-sm transition-colors hover:bg-white/[0.16] sm:p-6"
                >
                  <div className="flex items-start justify-between gap-3">
                    <span className="text-sm font-medium text-white/75">
                      {stat.label}
                    </span>
                    <span
                      className={
                        "inline-flex shrink-0 items-center gap-1 rounded-full bg-white/15 px-2 py-0.5 text-xs font-semibold ring-1 ring-white/20 " +
                        (stat.positive
                          ? "text-emerald-100"
                          : "text-rose-100")
                      }
                    >
                      <span
                        className={
                          stat.positive
                            ? "text-emerald-300"
                            : "text-rose-300"
                        }
                      >
                        <TrendArrow trend={stat.trend} />
                      </span>
                      {stat.delta}
                    </span>
                  </div>

                  <div className="text-4xl font-semibold tracking-tight text-white sm:text-[2.75rem] sm:leading-none">
                    {stat.prefix}
                    <CountUp
                      target={stat.value}
                      decimals={stat.decimals}
                      active={inView}
                      reduced={reduced}
                    />
                    {stat.suffix}
                  </div>

                  <p className="text-sm leading-relaxed text-white/65">
                    {stat.hint}
                  </p>

                  <span
                    aria-hidden="true"
                    className="pointer-events-none absolute inset-x-5 bottom-0 h-px origin-left scale-x-0 bg-gradient-to-r from-white/60 to-transparent transition-transform duration-500 group-hover:scale-x-100"
                  />
                </motion.div>
              ))}
            </div>

            <div className="mt-4 flex flex-wrap items-center justify-between gap-2 px-2 pt-1 text-xs text-white/70 sm:px-3">
              <span>{period.synced}</span>
              <span className="inline-flex items-center gap-1.5">
                <svg
                  viewBox="0 0 16 16"
                  width={13}
                  height={13}
                  aria-hidden="true"
                  className="text-white/70"
                >
                  <path
                    d="M8 1.5 2 4v3.6c0 3.4 2.5 6.1 6 6.9 3.5-.8 6-3.5 6-6.9V4L8 1.5Z"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth={1.3}
                    strokeLinejoin="round"
                  />
                  <path
                    d="m5.6 8 1.7 1.7L10.6 6.4"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth={1.3}
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  />
                </svg>
                SOC 2 Type II verified figures
              </span>
            </div>
          </div>
        </motion.div>
      </div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →
Four Stat Metric Band

Four Stat Metric Band

Original

A four-column metric band with gradient figures inside a hairline-divided card, ideal for headline numbers like uptime, latency and growth.

Labelled Stats With Dividers

Labelled Stats With Dividers

Original

Three centred stats separated by responsive dividers, each pairing a headline figure with an uppercase label and supporting detail.

Trusted By Logo Cloud

Trusted By Logo Cloud

Original

A responsive six-up logo strip using simple inline-SVG marks and wordmarks with a grayscale-to-colour hover, for a trusted-by section.

Hero Stat With Supporting Copy

Hero Stat With Supporting Copy

Original

A two-column layout pairing one oversized hero statistic with a supporting paragraph and a checklist card explaining the number.

Count-up stats reveal

Count-up stats reveal

Original

A four-card stat band whose gradient numbers count up from zero as the section scrolls into view, with staggered blur-in cards, a pulsing eyebrow, floating aurora blobs and a shimmering underline sweep.

Animated SVG progress rings

Animated SVG progress rings

Original

Four circular SVG progress rings that draw their gradient arcs and count up to their target percentage the moment they enter view, over a slowly rotating conic halo with a soft glow pulse behind each ring.

Staggered stat band with marquee ticker

Staggered stat band with marquee ticker

Original

A dark stat band where 3D-tilt cards stagger into view with count-up figures over an animated gradient mesh, finished by an infinite marquee ticker of highlight chips that pauses on hover.

Counter Row Stat

Counter Row Stat

Original

animated counter stats row

Cards Stat

Cards Stat

Original

stat cards with icons

Split Stat

Split Stat

Original

stats beside copy

Icons Stat

Icons Stat

Original

icon stat grid

Progress Stat

Progress Stat

Original

stats with progress rings