shadimage
bjork-ui
componenthud-2source: ok · refreshed

Overlay 2

Overlay 2 is a production-ready hud component from BJORK UI, packaged for shadcn registries.

npx shadcn add "https://ui.isaiahbjork.com/hud-2.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · medium
animationframer-motion

Files5 · source cached 10/8/2026, 6:17:21 AM

components/bjork-ui/hud/overlay-2.tsx
"use client";

import { useId } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { useIsDarkTheme } from "@/hooks/use-is-dark-theme";
import { cn } from "@/lib/utils";
import { BJORK_PALETTE } from "@/components/bjork-ui/_core/palette";
import { surfaceVars } from "@/components/bjork-ui/_core/surface";

interface Overlay2Props {
  className?: string;
  compact?: boolean;
}

const readouts = [
  { label: "Latency p95", value: "38", unit: "ms" },
  { label: "Queue depth", value: "124", unit: "jobs" },
  { label: "Error rate", value: "0.02", unit: "%" },
  { label: "Uptime, 30 days", value: "99.97", unit: "%" },
];

// Ten-second throughput samples, GB/s. Static, so the overlay renders the same on every load.
const samples = [1.62, 1.7, 1.66, 1.81, 1.92, 1.88, 2.02, 2.11, 2.07, 2.19, 2.14, 2.26, 2.31, 2.28, 2.4, 2.37, 2.44, 2.48];

const WIDTH = 320;
const HEIGHT = 84;

function sparkPath(values: number[]) {
  const min = Math.min(...values);
  const max = Math.max(...values);
  const span = max - min || 1;
  const step = WIDTH / (values.length - 1);
  return values
    .map((v, i) => {
      const x = i * step;
      const y = HEIGHT - 6 - ((v - min) / span) * (HEIGHT - 12);
      return `${i === 0 ? "M" : "L"}${x.toFixed(1)} ${y.toFixed(1)}`;
    })
    .join(" ");
}

const panelVariants = {
  hidden: { opacity: 0, y: 12 },
  show: {
    opacity: 1,
    y: 0,
    transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1], staggerChildren: 0.07, delayChildren: 0.15 },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 6 },
  show: { opacity: 1, y: 0, transition: { duration: 0.4, ease: [0.22, 1, 0.36, 1] } },
};

export function Overlay2({ className, compact = false }: Overlay2Props) {
  const reduce = useReducedMotion();
  const fillId = `overlay2-fill-${useId().replace(/:/g, "")}`;
  const isDark = useIsDarkTheme();
  const pal = BJORK_PALETTE[isDark ? "dark" : "light"];
  const motionProps = reduce
    ? { initial: false as const, animate: "show" as const }
    : { initial: "hidden" as const, animate: "show" as const };

  const path = sparkPath(samples);
  const latest = samples[samples.length - 1].toFixed(2);

  return (
    <div
      className={cn(
        "relative flex h-full min-h-[420px] w-full items-center justify-center overflow-hidden bg-transparent p-6",
        className,
      )}
      style={{ color: pal.text }}
    >
      <motion.section
        aria-label="Line 04 telemetry"
        className={cn(
          "w-full rounded-[18px] border border-[color:var(--bjork-border)] bg-[color:var(--bjork-surface)] shadow-[var(--bjork-shadow-surface)]",
          compact ? "max-w-[420px] p-4" : "max-w-[480px] p-5",
        )}
        style={surfaceVars(isDark ? "dark" : "light")}
        variants={panelVariants}
        {...motionProps}
      >
        <motion.header variants={itemVariants} className="flex items-center justify-between">
          <p className="font-mono text-[11px] uppercase tracking-[0.08em]" style={{ color: pal.textMuted }}>
            Line 04 · Telemetry
          </p>
          <p className="flex items-center gap-2 font-mono text-[11px] uppercase tracking-[0.08em]" style={{ color: pal.textMedium }}>
            <span className="relative flex size-1.5">
              {!reduce && (
                <span className="absolute inline-flex size-full animate-ping rounded-full opacity-60" style={{ background: pal.accent }} />
              )}
              <span className="relative inline-flex size-1.5 rounded-full" style={{ background: pal.accent }} />
            </span>
            Live
          </p>
        </motion.header>

        <motion.div variants={itemVariants} className="mt-4 flex items-end justify-between gap-4">
          <div>
            <p className="font-mono text-[11px] uppercase tracking-[0.08em]" style={{ color: pal.textMuted }}>
              Throughput
            </p>
            <p className={cn("mt-1 font-semibold tabular-nums tracking-tight", compact ? "text-3xl" : "text-4xl")}>
              {latest}
              <span className="ml-1.5 font-mono text-[13px] font-normal" style={{ color: pal.textMuted }}>
                GB/s
              </span>
            </p>
          </div>
          <p className="font-mono text-[11px] tabular-nums" style={{ color: pal.textMuted }}>
            +6.4% vs 1h
          </p>
        </motion.div>

        <motion.div variants={itemVariants} className="mt-3">
          <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} className="block h-auto w-full overflow-visible" role="img" aria-label="Throughput over the last ten minutes">
            <defs>
              <linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
                <stop offset="0%" stopColor={pal.accent} stopOpacity={0.22} />
                <stop offset="100%" stopColor={pal.accent} stopOpacity={0} />
              </linearGradient>
            </defs>
            {[0.25, 0.5, 0.75].map((f) => (
              <line
                key={f}
                x1={0}
                x2={WIDTH}
                y1={HEIGHT * f}
                y2={HEIGHT * f}
                stroke={pal.border}
                strokeWidth={1}
              />
            ))}
            <motion.path
              d={`${path} L${WIDTH} ${HEIGHT} L0 ${HEIGHT} Z`}
              fill={`url(#${fillId})`}
              initial={reduce ? false : { opacity: 0 }}
              animate={{ opacity: 1 }}
              transition={{ delay: reduce ? 0 : 0.5, duration: reduce ? 0 : 0.6 }}
            />
            <motion.path
              d={path}
              fill="none"
              stroke={pal.accent}
              strokeWidth={1.5}
              strokeLinejoin="round"
              strokeLinecap="round"
              initial={reduce ? false : { pathLength: 0 }}
              animate={{ pathLength: 1 }}
              transition={{ duration: reduce ? 0 : 1.2, ease: [0.22, 1, 0.36, 1] }}
            />
          </svg>
        </motion.div>

        <motion.dl variants={itemVariants} className="mt-4 grid grid-cols-2 gap-x-6 gap-y-3 border-t pt-4" style={{ borderColor: pal.border }}>
          {readouts.map((row) => (
            <div key={row.label} className="flex items-baseline justify-between gap-3">
              <dt className="font-mono text-[11px] uppercase tracking-[0.06em]" style={{ color: pal.textMuted }}>
                {row.label}
              </dt>
              <dd className="font-mono text-[13px] tabular-nums" style={{ color: pal.text }}>
                {row.value}
                <span className="ml-1 text-[11px]" style={{ color: pal.textMuted }}>
                  {row.unit}
                </span>
              </dd>
            </div>
          ))}
        </motion.dl>

        <motion.footer variants={itemVariants} className="mt-4 flex items-center justify-between border-t pt-3" style={{ borderColor: pal.border }}>
          <p className="font-mono text-[11px] tabular-nums" style={{ color: pal.textSoft }}>
            Sampled every 10 s
          </p>
          <p className="font-mono text-[11px] tabular-nums" style={{ color: pal.textSoft }}>
            Updated 14:32:08 UTC
          </p>
        </motion.footer>
      </motion.section>
    </div>
  );
}
hooks/use-is-dark-theme.ts
"use client"

import { useEffect, useState } from "react"
import { useTheme } from "next-themes"

/**
 * Hydration-safe dark flag for components that paint SVG or inline colors.
 *
 * The server and the first client render both report dark (the default look).
 * The real theme is applied after mount. Reading `resolvedTheme` during the
 * first client render makes React keep the server-rendered attributes on
 * light pages, which leaves dark-only strokes and fills in place.
 */
export function useIsDarkTheme(): boolean {
  const { resolvedTheme } = useTheme()
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  return mounted ? resolvedTheme !== "light" : true
}
lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
components/bjork-ui/_core/palette.ts
export type BjorkTone = "dark" | "light";

// Canvas, WebGL and SVG attribute colours. DOM code uses the CSS tokens with these as fallbacks.
export const BJORK_PALETTE = {
  dark: {
    bg: "#050505", stage: "#111111", surface: "#121212", raised: "#161616", active: "#202020",
    border: "#232323", borderStrong: "#343434",
    text: "#ededed", textMedium: "rgba(237,237,237,0.72)", textMuted: "rgba(237,237,237,0.6)",
    textSoft: "rgba(237,237,237,0.5)", textFaint: "rgba(237,237,237,0.22)", hair: "rgba(237,237,237,0.12)",
    accent: "#ec5c13", accentInk: "#ec5c13", accentFill: "#b84a12", accentSoft: "rgba(236,92,19,0.12)", accentMuted: "rgba(236,92,19,0.24)",
    accentFg: "#fff2ea", success: "#4cc38a", warning: "#f2b544", error: "#ff5c4d",
  },
  light: {
    bg: "#f7f5ef", stage: "#f7f5ef", surface: "#fffcf6", raised: "#f5efe3", active: "#efe7d8",
    border: "#eee6db", borderStrong: "#e1d7c8",
    text: "#171717", textMedium: "rgba(23,23,23,0.78)", textMuted: "rgba(23,23,23,0.68)",
    textSoft: "rgba(23,23,23,0.62)", textFaint: "rgba(23,23,23,0.22)", hair: "rgba(23,23,23,0.10)",
    accent: "#ec7d43", accentInk: "#b4531f", accentFill: "#ec7d43", accentSoft: "rgba(236,125,67,0.10)", accentMuted: "rgba(236,125,67,0.20)",
    accentFg: "#3f2112", success: "#1f8a55", warning: "#a86b00", error: "#c8361f",
  },
} as const;
components/bjork-ui/_core/surface.ts
import type { CSSProperties } from "react";
import type { BjorkTone } from "./palette";

// The library surface (CANON.md §1). Values mirror app/globals.css :root and .dark. Change both together.
export const BJORK_SURFACE = {
  light: {
    surface: "#fffcf6", surfaceMuted: "rgba(250, 246, 237, 0.94)", surfaceHover: "#f5efe3", surfaceActive: "#efe7d8",
    panel: "#f8f2e7", menu: "rgba(255, 252, 246, 0.98)", field: "rgba(255, 252, 246, 0.96)",
    fieldMuted: "rgba(248, 242, 231, 0.82)", fieldInset: "rgba(239, 231, 216, 0.72)",
    border: "#eee6db", borderMuted: "#f5ede2", borderStrong: "#e1d7c8",
    shadowSurface: "inset 0 7px 14px rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.92), inset 1px 0 0 rgba(88, 72, 49, 0.026), inset -1px 0 0 rgba(255, 255, 255, 0.68), 0 14px 22px -9px rgba(66, 52, 33, 0.11)",
    shadowSoft: "inset 0 1px 0 rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.86)",
    shadowPanel: "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.64), 0 18px 38px -30px rgba(66, 52, 33, 0.2)",
    shadowMenu: "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 0 12px 24px rgba(88, 72, 49, 0.022), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.62), 0 22px 44px -26px rgba(66, 52, 33, 0.22)",
    shadowInset: "inset 0 1px 10px rgba(88, 72, 49, 0.12), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.72)",
  },
  dark: {
    surface: "#121212", surfaceMuted: "rgba(22, 22, 22, 0.92)", surfaceHover: "#161616", surfaceActive: "#202020",
    panel: "#0d0d0d", menu: "rgba(18, 18, 18, 0.98)", field: "rgba(18, 18, 18, 0.95)",
    fieldMuted: "rgba(18, 18, 18, 0.55)", fieldInset: "rgba(9, 9, 9, 0.75)",
    border: "#232323", borderMuted: "#1c1c1c", borderStrong: "#343434",
    shadowSurface: "inset 0 7px 14px rgba(255, 255, 255, 0.03), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.06), 0 14px 20px -6px rgba(0, 0, 0, 0.45)",
    shadowSoft: "inset 0 1px 0 rgba(255, 255, 255, 0.045)",
    shadowPanel: "inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 18px 36px -28px rgba(0, 0, 0, 0.9)",
    shadowMenu: "inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 12px 24px rgba(255, 255, 255, 0.018), inset 0 -18px 26px rgba(0, 0, 0, 0.24), 0 22px 42px -22px rgba(0, 0, 0, 0.9)",
    shadowInset: "inset 0 1px 10px rgba(0, 0, 0, 0.45)",
  },
} as const;

/** Writes the surface tokens for a pinned tone as --bjork-* custom properties. Spread first; kit-specific keys may override. */
export function surfaceVars(tone: BjorkTone): CSSProperties {
  const s = BJORK_SURFACE[tone];
  return {
    "--bjork-surface": s.surface, "--bjork-surface-muted": s.surfaceMuted, "--bjork-surface-hover": s.surfaceHover,
    "--bjork-surface-active": s.surfaceActive, "--bjork-panel": s.panel, "--bjork-menu": s.menu, "--bjork-field": s.field,
    "--bjork-field-muted": s.fieldMuted, "--bjork-field-inset": s.fieldInset, "--bjork-border": s.border,
    "--bjork-border-muted": s.borderMuted, "--bjork-border-strong": s.borderStrong,
    "--bjork-shadow-surface": s.shadowSurface, "--bjork-shadow-soft": s.shadowSoft, "--bjork-shadow-panel": s.shadowPanel,
    "--bjork-shadow-menu": s.shadowMenu, "--bjork-shadow-inset": s.shadowInset,
  } as CSSProperties;
}

More from bjork-ui

Browse all components in this library →