Live BTC UI
React · SandpackEditor · Preview playground
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> </> ); }