← Orion

registry:ui

Loading State

Pixel-grid loader with shimmer text and elapsed time.

Rendered example

Churning0.0s

Install

orion-themeshimmer
Open registry JSON

registry/ui/loading-state.tsx

"use client"

import { useEffect, useState } from "react"

import { Shimmer } from "@/registry/ui/shimmer"

/**
 * Pixel-grid loader for long-running work.
 *
 * Variants:
 *   Drive — square cells, chevron wavefront driving right; the 650ms
 *           cycle is shorter than the sweep, so two fronts are always
 *           in flight
 *   Dots  — same wavefront, circular cells
 *   Orbit — a comet lapping the grid perimeter
 *
 * Paired with a shimmering label and a live elapsed timer in mono
 * tabular figures.
 */

const CHEVRON = Array.from({ length: 9 }, (_, i) => {
  const r = Math.floor(i / 3)
  const c = i % 3
  return (c + Math.abs(r - 1)) * 90
})

const ORBIT_ORDER = [0, 1, 2, 5, 8, 7, 6, 3]
const ORBIT = Array.from({ length: 9 }, (_, i) => {
  const k = ORBIT_ORDER.indexOf(i)
  return k === -1 ? null : k * 110
})

const PATTERNS = {
  Drive: { delays: CHEVRON, duration: 650, round: false },
  Dots: { delays: CHEVRON, duration: 650, round: true },
  Orbit: { delays: ORBIT, duration: 950, round: false },
} satisfies Record<string, { delays: (number | null)[]; duration: number; round: boolean }>

export type LoadingStateVariant = keyof typeof PATTERNS

function useElapsed() {
  const [deciseconds, setDeciseconds] = useState(0)

  useEffect(() => {
    const id = setInterval(() => setDeciseconds((d) => d + 1), 100)
    return () => clearInterval(id)
  }, [])

  const total = deciseconds / 10
  if (total < 60) return `${total.toFixed(1)}s`
  return `${Math.floor(total / 60)}m ${(total % 60).toFixed(1)}s`
}

export function LoadingState({
  label = "Churning",
  variant = "Drive",
}: {
  label?: string
  variant?: LoadingStateVariant
}) {
  const elapsed = useElapsed()
  const { delays, duration, round } = PATTERNS[variant] ?? PATTERNS.Drive

  return (
    <div className="flex w-fit items-center gap-2.5">
      <span aria-hidden className="grid grid-cols-3 gap-[1.5px]">
        {delays.map((delay, i) => (
          <span
            key={i}
            className={`size-1 bg-ink ${round ? "rounded-full" : "rounded-[1px]"}`}
            style={{
              opacity: delay === null ? 0.07 : 0.15,
              animation: delay === null ? "none" : `pixel-on ${duration}ms ease-in-out ${delay}ms infinite`,
            }}
          />
        ))}
      </span>
      <Shimmer className="text-[13px]">{label}</Shimmer>
      <span className="font-mono text-[12px] text-ink-3 tabular-nums">{elapsed}</span>
    </div>
  )
}