feat: initial @jason/ui-kit — tokens, preset, components, motion, blocks, Impeccable
ci / build-and-design (push) Failing after 15s

This commit is contained in:
2026-07-16 00:36:53 -05:00
commit b75012525d
36 changed files with 1324 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import * as React from "react";
import { useInView, useMotionValue, useSpring, useReducedMotion } from "motion/react";
/** Count-up number for dashboards/stat cards. Springs to `value` when scrolled into view. */
export function AnimatedNumber({
value,
format = (n) => Math.round(n).toLocaleString(),
className,
}: {
value: number;
format?: (n: number) => string;
className?: string;
}) {
const ref = React.useRef<HTMLSpanElement>(null);
const inView = useInView(ref, { once: true, margin: "-20% 0px" });
const reduce = useReducedMotion();
const mv = useMotionValue(0);
const spring = useSpring(mv, { stiffness: 90, damping: 20 });
const [display, setDisplay] = React.useState(format(0));
React.useEffect(() => {
if (reduce) { setDisplay(format(value)); return; }
if (inView) mv.set(value);
}, [inView, value, reduce, mv, format]);
React.useEffect(() => spring.on("change", (v) => setDisplay(format(v))), [spring, format]);
return <span ref={ref} className={className}>{display}</span>;
}
+38
View File
@@ -0,0 +1,38 @@
import * as React from "react";
import { cn } from "../lib/cn";
/** Card with a soft brand glow that tracks the cursor. Depth without gimmick. */
export function SpotlightCard({
children,
className,
}: {
children: React.ReactNode;
className?: string;
}) {
const ref = React.useRef<HTMLDivElement>(null);
const [pos, setPos] = React.useState({ x: 50, y: 50, active: false });
const onMove = (e: React.MouseEvent<HTMLDivElement>) => {
const r = ref.current?.getBoundingClientRect();
if (!r) return;
setPos({ x: ((e.clientX - r.left) / r.width) * 100, y: ((e.clientY - r.top) / r.height) * 100, active: true });
};
return (
<div
ref={ref}
onMouseMove={onMove}
onMouseLeave={() => setPos((p) => ({ ...p, active: false }))}
className={cn("group relative overflow-hidden rounded-lg border border-border bg-surface p-6 shadow-sm transition-shadow duration-slow hover:shadow-md", className)}
>
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-slow group-hover:opacity-100"
style={{
background: `radial-gradient(400px circle at ${pos.x}% ${pos.y}%, hsl(var(--brand) / 0.10), transparent 60%)`,
}}
/>
<div className="relative">{children}</div>
</div>
);
}