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 · mediumanimationframer-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;
}