This component no longer exists. It was in ruixen-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-12 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Banner Countdown
ruixen-ui/banner-countdownRemovedComponent
A countdown banner with Fluid Numerals — odometer-style rolling digits, colon heartbeat, and height-collapse dismiss.
Live preview
live · rendered by the registry
Rendered by ruixen-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ruixen.com/r/banner-countdown.jsonSource
1"use client";23import * as React from "react";4import { cn } from "@/lib/utils";5import { ArrowRight, X } from "lucide-react";67/* ═══════════════════════════════════════════════════════════8 Fluid Numerals — digits that roll like a physical odometer.9 Each digit is independent: when 10→09 the tens-place rolls10 1→0 separately from the ones-place rolling 0→9.11 ═══════════════════════════════════════════════════════════ */1213export interface BannerCountdownProps {14 title: string;15 endDate: Date;16 action?: { label: string; href: string };17 onExpire?: () => void;18 onDismiss?: () => void;19 className?: string;20}2122/* ── time math ── */23function calc(end: Date) {24 const s = Math.max(0, Math.floor((end.getTime() - Date.now()) / 1000));25 return {26 total: s,27 days: Math.floor(s / 86400),28 hours: Math.floor((s % 86400) / 3600),29 minutes: Math.floor((s % 3600) / 60),30 seconds: s % 60,31 };32}3334/* ── single rolling digit (0-9) ── */35function Digit({ value }: { value: number }) {36 const [cur, setCur] = React.useState(value);37 const [prev, setPrev] = React.useState<number | null>(null);3839 React.useEffect(() => {40 if (value !== cur) {41 setPrev(cur);42 setCur(value);43 const id = setTimeout(() => setPrev(null), 400);44 return () => clearTimeout(id);45 }46 }, [value, cur]);4748 return (49 <span className="relative inline-flex h-[1.4em] w-[0.62em] items-center justify-center overflow-hidden">50 {prev !== null && (51 <span className="absolute inset-0 flex items-center justify-center animate-[digit-out_0.35s_ease-in_forwards]">52 {prev}53 </span>54 )}55 <span56 key={cur}57 className={cn(58 "absolute inset-0 flex items-center justify-center",59 prev !== null &&60 "animate-[digit-in_0.35s_cubic-bezier(0.16,1,0.3,1)]",61 )}62 >63 {cur}64 </span>65 </span>66 );67}6869/* ── digit pair container ── */70function Pair({ value }: { value: number }) {71 return (72 <div className="flex items-center justify-center rounded-md bg-foreground/[0.04] px-2.5 py-1.5 font-mono text-xl font-medium tabular-nums leading-none text-foreground">73 <Digit value={Math.floor(value / 10)} />74// … truncated
What it pulls in
npm packages
