Skip to content
Product block

Market Price Ticker

The moving price rail that sits under an exchange’s navbar — direction carried by an arrow and a sign as well as by colour, and the duplicate half dropped rather than read twice.

180 lineslucide-reactAdded 14 Sept 2026
  • ticker
  • prices
  • crypto
  • fintech
  • marquee

What's included

  • components/price-ticker-strip.tsx
  • Needs lucide-react

Works with

  • React
  • Next.js
  • Tailwind CSS
  • TypeScript

npx hoverlab add price-ticker-strip

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Start a page with this section — add more, order them, and leave with the page source.

Preview

BTC$63,204.10+2.41% up
ETH$3,118.62+1.08% up
SOL$147.93−3.22% down
USDC$1.00010.00% unchanged
XRP$0.5284−0.94% down
ADA$0.4471+5.67% up
AVAX$27.84+0.72% up
DOT$6.19−1.55% down

Indicative prices, delayed 15 minutes

Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.

Source

components/price-ticker-strip.tsx
/**
 * <PriceTickerStrip> — the moving price rail every exchange puts under its nav.
 *
 * It is the one piece of furniture that says "this is a market" before a
 * word of copy is read, and it is the thing a fintech landing page built
 * from generic marketing blocks always lacks.
 *
 * THREE THINGS IT DOES THAT A NAIVE TICKER GETS WRONG.
 *
 * 1. *Direction is never colour alone.* Red and green are the convention
 *    and they are also 1.4.1 — roughly one man in twelve cannot separate
 *    them, and a strip where the only difference between "up 4%" and "down
 *    4%" is hue is unreadable to him. Every row carries an arrow glyph and
 *    a signed number, so the colour is the third cue rather than the only
 *    one.
 *
 * 2. *The duplicate half is `aria-hidden`.* A seamless marquee needs the
 *    list twice; announcing it twice would read every price twice. Same
 *    call as <LogoCloud>, and the reason it is worth restating is that the
 *    obvious fix — a CSS-only duplicate via `content` — cannot be styled
 *    per row.
 *
 * 3. *It stops.* `motion-safe:` gates the animation, so a reader with
 *    `prefers-reduced-motion` gets the same prices as a static wrapped row
 *    rather than nothing; hovering pauses it, because a price you are
 *    trying to read should not slide out from under the pointer. Vestibular
 *    triggers aside, an unpausable ticker is simply hostile.
 *
 * PRICES ARE PRE-FORMATTED STRINGS. Not a `number` plus `Intl.NumberFormat`
 * at render: the server's locale is not the browser's, and a ticker that
 * renders "63,204.10" on the server and "63 204,10" in the client is a
 * hydration mismatch that throws the whole subtree away. Format upstream,
 * where you know the user's locale, and pass strings.
 *
 * There is no live data here and the block does not pretend otherwise —
 * `asOf` names when these numbers were true. Wire it to a socket and
 * replace the array; nothing about the layout changes.
 */

import * as React from 'react'
import { ArrowUpRight, ArrowDownRight } from 'lucide-react'

export interface TickerQuote {
  /** Ticker symbol — "BTC", "EUR/USD", "AAPL". */
  symbol: string
  /** The full name, shown under the symbol on the expanded variant. */
  name?: string
  /** Pre-formatted, currency symbol included. See the header. */
  price: string
  /** Signed and suffixed by you — "+4.12%", "−0.30%". */
  change: string
  direction: 'up' | 'down' | 'flat'
}

export interface PriceTickerStripProps {
  quotes?: TickerQuote[]
  /** Seconds for one full pass. Longer is calmer; 60 is about right for 8. */
  duration?: number
  /** When these numbers were true. A market strip with no timestamp lies. */
  asOf?: string
  /** Hairlines above and below, for sitting the strip under a navbar. */
  divided?: boolean
  className?: string
}

const DEFAULT_QUOTES: TickerQuote[] = [
  { symbol: 'BTC', name: 'Bitcoin', price: '$63,204.10', change: '+2.41%', direction: 'up' },
  { symbol: 'ETH', name: 'Ethereum', price: '$3,118.62', change: '+1.08%', direction: 'up' },
  { symbol: 'SOL', name: 'Solana', price: '$147.93', change: '−3.22%', direction: 'down' },
  { symbol: 'USDC', name: 'USD Coin', price: '$1.0001', change: '0.00%', direction: 'flat' },
  { symbol: 'XRP', name: 'XRP', price: '$0.5284', change: '−0.94%', direction: 'down' },
  { symbol: 'ADA', name: 'Cardano', price: '$0.4471', change: '+5.67%', direction: 'up' },
  { symbol: 'AVAX', name: 'Avalanche', price: '$27.84', change: '+0.72%', direction: 'up' },
  { symbol: 'DOT', name: 'Polkadot', price: '$6.19', change: '−1.55%', direction: 'down' },
]

/*
  Utility classes rather than a style object, because these tokens are
  complete oklch() colours — `hsl(var(--primary))` would be dropped by the
  parser and fall back to inherited, which typechecks and renders invisible.
  `flat` deliberately gets the muted colour: nothing moved, so nothing
  should catch the eye.
*/
const DIRECTION_CLASS: Record<TickerQuote['direction'], string> = {
  up: 'text-emerald-600 dark:text-emerald-400',
  down: 'text-rose-600 dark:text-rose-400',
  flat: 'text-muted-foreground',
}

function DirectionGlyph({ direction }: { direction: TickerQuote['direction'] }) {
  if (direction === 'up') return <ArrowUpRight aria-hidden className="h-3.5 w-3.5" />
  if (direction === 'down') return <ArrowDownRight aria-hidden className="h-3.5 w-3.5" />
  /* An en dash, not an arrow: "flat" has no direction to point in. */
  return (
    <span aria-hidden className="text-xs leading-none">
      –
    </span>
  )
}

function Quote({ quote, hidden }: { quote: TickerQuote; hidden: boolean }) {
  return (
    <div
      aria-hidden={hidden || undefined}
      /* The duplicate half exists only to make the loop seamless. Under
         `prefers-reduced-motion` the rail stops scrolling and wraps, and a
         wrapped rail that lists every price twice is a bug rather than a
         seam — so the copy is dropped entirely there. <LogoCloud> tolerates
         the repeat because a repeated wordmark reads as a pattern; a
         repeated price reads as two different quotes. */
      className={`flex shrink-0 items-center gap-2.5 whitespace-nowrap ${
        hidden ? 'motion-reduce:hidden' : ''
      }`}
    >
      <span className="text-sm font-bold tracking-tight text-foreground">{quote.symbol}</span>
      <span className="text-sm tabular-nums text-muted-foreground">{quote.price}</span>
      <span
        className={`inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums ${
          DIRECTION_CLASS[quote.direction]
        }`}
      >
        <DirectionGlyph direction={quote.direction} />
        {quote.change}
        {/* The word the colour and the arrow are both standing in for. It
            is never rendered, which is the point — it exists so the row
            reads correctly out loud without adding a fourth visible cue. */}
        <span className="sr-only">
          {quote.direction === 'up' ? ' up' : quote.direction === 'down' ? ' down' : ' unchanged'}
        </span>
      </span>
    </div>
  )
}

export function PriceTickerStrip({
  quotes = DEFAULT_QUOTES,
  duration = 60,
  asOf = 'Indicative prices, delayed 15 minutes',
  divided = true,
  className = '',
}: PriceTickerStripProps) {
  return (
    <section
      aria-label="Market prices"
      className={`w-full bg-card/60 ${divided ? 'border-y border-border/60' : ''} ${className}`}
    >
      <div
        className="group relative overflow-hidden py-2.5"
        style={{
          maskImage: 'linear-gradient(to right, transparent, black 6%, black 94%, transparent)',
          WebkitMaskImage:
            'linear-gradient(to right, transparent, black 6%, black 94%, transparent)',
        }}
      >
        <div
          className="flex w-max items-center gap-8 motion-safe:animate-[price-ticker_var(--ticker-duration)_linear_infinite] motion-reduce:w-full motion-reduce:flex-wrap motion-reduce:justify-center motion-reduce:gap-x-8 motion-reduce:gap-y-2 group-hover:[animation-play-state:paused]"
          style={{ ['--ticker-duration' as string]: `${duration}s` }}
        >
          {[...quotes, ...quotes].map((quote, i) => (
            <Quote key={`${quote.symbol}-${i}`} quote={quote} hidden={i >= quotes.length} />
          ))}
        </div>
      </div>

      {asOf ? (
        <p className="px-4 pb-2 text-center text-[11px] text-muted-foreground/85 sm:px-6">
          {asOf}
        </p>
      ) : null}

      <style>{`
        @keyframes price-ticker {
          from { transform: translateX(0); }
          to   { transform: translateX(-50%); }
        }
      `}</style>
    </section>
  )
}

Before you paste

  • Styling is Tailwind utility classes on semantic tokens (bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.
  • Install: npm i lucide-react
  • Every prop has a default, so it renders standalone before you wire it up.

Where it goes

Drop it at components/price-ticker-strip.tsx and import it where you need the section:

import { PriceTickerStrip } from '@/components/price-ticker-strip'

Customize

3 of this block’s props are simple enough to drive from here. Change them and the block below re-renders — it is the same component whose source is above, not a mock of it. Everything else it accepts is in the table underneath.

Props

Read out of the component’s own type and signature, so this cannot drift from the source below. Every prop has a default — the component renders standalone before you pass it anything.

PropTypeDefault
quotesTickerQuote[]DEFAULT_QUOTES
durationSeconds for one full pass. Longer is calmer; 60 is about right for 8.number60
asOfWhen these numbers were true. A market strip with no timestamp lies.string'Indicative prices, delayed 15 minutes'
dividedHairlines above and below, for sitting the strip under a navbar.booleantrue
classNamestring''

Not using React?

The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.

price-ticker-strip.html
<!--
  Market Price Ticker — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block has no interactive behaviour, so nothing is missing.
-->
<section aria-label="Market prices" class="w-full bg-card/60 border-y border-border/60 ">
  <div class="group relative overflow-hidden py-2.5" style="mask-image:linear-gradient(to right, transparent, black 6%, black 94%, transparent);-webkit-mask-image:linear-gradient(to right, transparent, black 6%, black 94%, transparent)">
    <div class="flex w-max items-center gap-8 motion-safe:animate-[price-ticker_var(--ticker-duration)_linear_infinite] motion-reduce:w-full motion-reduce:flex-wrap motion-reduce:justify-center motion-reduce:gap-x-8 motion-reduce:gap-y-2 group-hover:[animation-play-state:paused]" style="--ticker-duration:60s">
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">BTC</span>
        <span class="text-sm tabular-nums text-muted-foreground">$63,204.10</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+2.41%<span class="sr-only"> up</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">ETH</span>
        <span class="text-sm tabular-nums text-muted-foreground">$3,118.62</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+1.08%<span class="sr-only"> up</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">SOL</span>
        <span class="text-sm tabular-nums text-muted-foreground">$147.93</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−3.22%<span class="sr-only"> down</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">USDC</span>
        <span class="text-sm tabular-nums text-muted-foreground">$1.0001</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-muted-foreground"><span aria-hidden="true" class="text-xs leading-none">–</span>0.00%<span class="sr-only"> unchanged</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">XRP</span>
        <span class="text-sm tabular-nums text-muted-foreground">$0.5284</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−0.94%<span class="sr-only"> down</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">ADA</span>
        <span class="text-sm tabular-nums text-muted-foreground">$0.4471</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+5.67%<span class="sr-only"> up</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">AVAX</span>
        <span class="text-sm tabular-nums text-muted-foreground">$27.84</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+0.72%<span class="sr-only"> up</span></span>
      </div>
      <div class="flex shrink-0 items-center gap-2.5 whitespace-nowrap ">
        <span class="text-sm font-bold tracking-tight text-foreground">DOT</span>
        <span class="text-sm tabular-nums text-muted-foreground">$6.19</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−1.55%<span class="sr-only"> down</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">BTC</span>
        <span class="text-sm tabular-nums text-muted-foreground">$63,204.10</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+2.41%<span class="sr-only"> up</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">ETH</span>
        <span class="text-sm tabular-nums text-muted-foreground">$3,118.62</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+1.08%<span class="sr-only"> up</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">SOL</span>
        <span class="text-sm tabular-nums text-muted-foreground">$147.93</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−3.22%<span class="sr-only"> down</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">USDC</span>
        <span class="text-sm tabular-nums text-muted-foreground">$1.0001</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-muted-foreground"><span aria-hidden="true" class="text-xs leading-none">–</span>0.00%<span class="sr-only"> unchanged</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">XRP</span>
        <span class="text-sm tabular-nums text-muted-foreground">$0.5284</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−0.94%<span class="sr-only"> down</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">ADA</span>
        <span class="text-sm tabular-nums text-muted-foreground">$0.4471</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+5.67%<span class="sr-only"> up</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">AVAX</span>
        <span class="text-sm tabular-nums text-muted-foreground">$27.84</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-emerald-600 dark:text-emerald-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-right h-3.5 w-3.5" aria-hidden="true"><path d="M7 7h10v10"></path><path d="M7 17 17 7"></path></svg>+0.72%<span class="sr-only"> up</span></span>
      </div>
      <div aria-hidden="true" class="flex shrink-0 items-center gap-2.5 whitespace-nowrap motion-reduce:hidden">
        <span class="text-sm font-bold tracking-tight text-foreground">DOT</span>
        <span class="text-sm tabular-nums text-muted-foreground">$6.19</span>
        <span class="inline-flex items-center gap-0.5 text-xs font-semibold tabular-nums text-rose-600 dark:text-rose-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-right h-3.5 w-3.5" aria-hidden="true"><path d="m7 7 10 10"></path><path d="M17 7v10H7"></path></svg>−1.55%<span class="sr-only"> down</span></span>
      </div>
    </div>
  </div>
  <p class="px-4 pb-2 text-center text-[11px] text-muted-foreground/85 sm:px-6">Indicative prices, delayed 15 minutes</p>
  <style>@keyframes price-ticker {
          from { transform: translateX(0); }
          to   { transform: translateX(-50%); }
        }</style>
</section>
  • This is rendered HTML, not a translation of the React source. The Tailwind classes carry the design and work in any framework.
  • It is one frame: the component in its initial state, with no props applied beyond the defaults.
  • Requires Tailwind, and the design tokens the classes reference (bg-card, text-muted-foreground, and so on). The template ZIPs ship a globals.css that defines them.

What each framework gets across the whole catalog — effects convert properly; this rung is markup.

For AI

The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.

See the prompt

Used in these pages

Want the whole screen instead of this one section? Open a page and copy it entire.

9 more blocks in Charts & Metrics

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse Charts & Metrics

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once

More Charts & Metrics blocks

View category
Open the full page for this block

Sparkline Metric Cards

KPI cards with a hand-built SVG trend line — no charting dependency for a path this file computes in twelve lines.

Charts & Metrics142 linesNo deps
Open the full page for this block

CSS Bar Chart Panel

Categorical bars in plain CSS with gridlines, hover values and a screen-reader table carrying the real numbers.

Charts & Metrics139 linesNo deps
Open the full page for this block

Quota & Usage Meters

Consumption against plan limits, with warning thresholds, honest overage display and unmetered quotas that do not fake a bar.

Charts & Metrics172 lines1 dep
Open the full page for this block

Dual-Series Line Chart

A year-over-year trend in inline SVG with a printed axis, no charting dependency and the same data repeated as a screen-reader table.

Charts & Metrics277 linesNo deps
Open the full page for this block

Donut Breakdown

Where a total went, drawn as one conic-gradient ring with the total in the hole and every number repeated in the legend.

Charts & Metrics205 linesNo deps
Open the full page for this block

Daily Activity Heatmap

A year of daily activity as one square per day, bucketed against a quantile so a single outlier cannot flatten the scale.

Charts & Metrics231 linesNo deps
Open the full page for this block

Conversion Funnel

A funnel that leads with the step-to-step rate and the people who fell out of it, not the tidy share-of-top percentages nobody can act on. Biggest drop named in words, and a footnote saying which definition of funnel this is.

Charts & Metrics195 lines1 dep
Open the full page for this block

Metric Alert List

Alert thresholds with their current distance from firing, so a quiet alert is visibly armed rather than possibly broken.

Charts & Metrics141 linesNo deps
Open the full page for this block

KPI Summary Band

The top-of-dashboard figures, each carrying the comparison period that makes a delta mean anything.

Charts & Metrics118 linesNo deps