Live BTC UI

Editor · Preview playground

React · Sandpack
import { useEffect, useMemo, useState } from "react";

const styles = `
:root {
  font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
  background: #030712;
  color: #f8fafc;
}

body {
  margin: 0;
}

.page {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 3rem 1.5rem;
  background: radial-gradient(circle at 20% 0%, #1f2937 0%, #030712 60%);
}

.card {
  width: min(420px, 100%);
  padding: 2.75rem;
  border-radius: 32px;
  background: rgba(15, 23, 42, 0.78);
  border: 1px solid rgba(148, 163, 184, 0.25);
  box-shadow: 0 36px 120px -28px rgba(15, 23, 42, 0.9);
  backdrop-filter: blur(18px);
  display: grid;
  gap: 1.75rem;
}

.symbol {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: rgba(226, 232, 240, 0.72);
}

.price {
  margin: 0;
  font-size: clamp(3rem, 8vw, 4rem);
  font-weight: 600;
  letter-spacing: -0.045em;
  transition: color 220ms ease, transform 220ms ease;
}

.price[data-direction="up"] {
  color: #22c55e;
}

.price[data-direction="down"] {
  color: #ef4444;
}

.price[data-direction="flat"] {
  color: #38bdf8;
}

.delta {
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", monospace;
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.28em;
  color: rgba(226, 232, 240, 0.68);
}

.meta {
  font-size: 0.95rem;
  color: rgba(148, 163, 184, 0.68);
}
`;

type PriceSample = {
  value: number;
  movement: "up" | "down" | "flat";
  source: string;
};

const samples: PriceSample[] = [
  { value: 68123.42, movement: "up", source: "binance" },
  { value: 67811.05, movement: "down", source: "coinbase" },
  { value: 67898.77, movement: "up", source: "coinbase" },
  { value: 67745.13, movement: "down", source: "coingecko" },
  { value: 67745.13, movement: "flat", source: "binance" },
];

const priceFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
});

const deltaFormatter = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  minimumFractionDigits: 2,
  signDisplay: "always",
});

export default function App() {
  const [step, setStep] = useState(0);
  const current = samples[step];
  const previous = samples[(step - 1 + samples.length) % samples.length];

  useEffect(() => {
    const id = window.setInterval(() => {
      setStep((value) => (value + 1) % samples.length);
    }, 1800);
    return () => window.clearInterval(id);
  }, []);

  const delta = useMemo(
    () => Number((current.value - previous.value).toFixed(2)),
    [current.value, previous.value],
  );

  const deltaLabel = useMemo(() => {
    if (delta === 0) return "Flat";
    return delta > 0 ? "Rising" : "Falling";
  }, [delta]);

  return (
    <>
      <style>{styles}</style>
      <main className="page">
        <article className="card">
          <span className="symbol">BTC / USDT</span>
          <p className="price" data-direction={current.movement}>
            {priceFormatter.format(current.value)}
          </p>
          <div className="delta" aria-live="polite">
            {deltaFormatter.format(delta)} · {deltaLabel}
          </div>
          <div className="meta">
            Source: {current.source.toUpperCase()}
          </div>
        </article>
      </main>
    </>
  );
}