shadcn/ui × React Aria — Choose your foundation
Install this video
Adds the full composition, its remocn dependencies, and the prompt to your Remotion project via the shadcn registry.
$ pnpm dlx shadcn@latest add kapishdima/remocn-demo/shadcn-ariaRender it locally
Renders the MP4 on your machine with the Remotion CLI.
$ pnpm dlx remotion render shadcn-aria out/shadcn-aria.mp4 --scale=2 --crf=15 --x264-preset=slower --jpeg-quality=95 --gl=angleThe prompt
Reconstructed draftA reconstruction of the prompt this video was generated from.
The code
The exact source the AI wrote — the same files the install command puts in your project.
"use client";
// shadcn/ui × React Aria — the July 2026 changelog cut ("choose your
// foundation"). Register sampled live from ui.shadcn.com dark tokens
// (2026-07-17): #0a0a0a canvas, #fafafa ink, #171717 card, #a1a1a1 muted,
// white/10 hairline, radius 10, Geist + Geist Mono. Guest brand React Aria:
// exact header mark (viewBox 200 206 800 790), #7F57FF on dark.
//
// After the news + the flag, the heart of the cut is React Aria's own three
// signature demos, rebuilt from react-aria.adobe.com's "Bring your own
// styles" section — the DatePicker ("Date Planted"), the ComboBox
// ("Assignee", with the entering animation the site itself advertises), and
// the Slider ("Opacity", defaultValue 30) — each acting out one of React
// Aria's value words on the pinned dark tokens with the violet as accent.
//
// One camera: beats enter from below and exit upward on springs; sections
// dive forward through the remocn push-through; no lateral travel anywhere.
// One backdrop: shader-caustics, whose light takes the violet the moment the
// aria mark completes and keeps it.
import React from "react";
import {
AbsoluteFill,
Easing,
Sequence,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { loadFont as loadSans } from "@remotion/google-fonts/Geist";
import { loadFont as loadMono } from "@remotion/google-fonts/GeistMono";
import { ShaderCaustics } from "@/components/remocn/shader-caustics";
import { SoftBlurIn } from "@/components/remocn/soft-blur-in";
import { pushThrough } from "@/components/remocn/push-through";
const { fontFamily: GEIST } = loadSans("normal", {
weights: ["400", "500"],
subsets: ["latin", "latin-ext"],
});
const { fontFamily: GEIST_MONO } = loadMono("normal", {
weights: ["400"],
subsets: ["latin", "latin-ext"],
});
// ---------------------------------------------------------------------------
// Live ui.shadcn.com dark tokens (fetched 2026-07-17)
// ---------------------------------------------------------------------------
const BG = "#0a0a0a";
const INK = "#fafafa";
const MUTED = "#a1a1a1";
const CARD = "#171717";
const HAIRLINE = "rgba(255,255,255,0.10)";
const RADIUS = 10;
// React Aria's own value from light-dark(#6733FF, #7F57FF) — dark side.
const ARIA = "#7F57FF";
const SANS = `${GEIST}, -apple-system, BlinkMacSystemFont, sans-serif`;
const MONO = `${GEIST_MONO}, ui-monospace, SFMono-Regular, monospace`;
// ---------------------------------------------------------------------------
// Timeline
// ---------------------------------------------------------------------------
const S_INTRO = 160;
const S_CMD = 150;
const S_CAL = 116;
const S_COMBO = 132;
const S_SLIDER = 100;
const S_OUTRO = 166;
const T = 26; // push-through overlap
export const SHADCN_ARIA_DURATION =
S_INTRO + S_CMD + S_CAL + S_COMBO + S_SLIDER + S_OUTRO - 5 * T; // 694
// The violet arrives in the light as the aria mark completes in the intro,
// and never leaves.
const ACCENT_AT = 48;
// ---------------------------------------------------------------------------
// Exact marks
// ---------------------------------------------------------------------------
// Official shadcn/ui logo — shadcn-ui/ui apps/v4/components/icons.tsx:
// two round-cap diagonals in a 256 viewBox, strokeWidth 32.
const SHADCN_LINES = [
{ x1: 192, y1: 40, x2: 40, y2: 192, len: 214.96 },
{ x1: 208, y1: 128, x2: 128, y2: 208, len: 113.14 },
] as const;
function ShadcnMark({
size,
progress,
color = INK,
}: {
size: number;
progress: number; // 0..1 draw-on
color?: string;
}) {
// Reveal the two round-capped diagonals with a diagonal wipe along the
// stroke direction (top-right → bottom-left). A dashoffset draw-on flashes a
// big round-cap dot before the line grows, and the two lines start at
// different points — the wipe paints the whole mark on as one gesture with
// no floating dot.
const c = Math.max(-1, Math.min(1, 2 * progress - 1));
let clip: string | undefined;
if (progress <= 0) {
clip = "polygon(100% 0%, 100% 0%, 100% 0%)";
} else if (progress >= 1) {
clip = undefined;
} else if (c >= 0) {
clip = `polygon(0% 0%, 0% ${(c * 100).toFixed(2)}%, ${((1 - c) * 100).toFixed(2)}% 100%, 100% 100%, 100% 0%)`;
} else {
clip = `polygon(${(-c * 100).toFixed(2)}% 0%, 100% 0%, 100% ${((1 + c) * 100).toFixed(2)}%)`;
}
return (
<div style={{ width: size, height: size, clipPath: clip }}>
<svg
width={size}
height={size}
viewBox="0 0 256 256"
fill="none"
style={{ display: "block" }}
>
{SHADCN_LINES.map((l, i) => (
<line
key={i}
x1={l.x1}
y1={l.y1}
x2={l.x2}
y2={l.y2}
stroke={color}
strokeWidth={32}
strokeLinecap="round"
strokeLinejoin="round"
/>
))}
</svg>
</div>
);
}
// Exact React Aria header mark — react-aria.adobe.com (one path, two
// subpaths), viewBox "200 206 800 790", dark-theme fill #7F57FF.
const ARIA_PATH =
"M720.67 205.995C867.583 205.995 986.679 325.091 986.68 472.003C986.68 590.753 908.865 691.325 801.446 725.521L979.312 948.055C994.438 966.98 980.963 995 956.736 995H795.612C778.743 995 762.715 987.629 751.734 974.823L697.365 911.421L493.126 653.39C457.134 607.918 489.518 540.979 547.511 540.977L720.67 540.971C758.758 540.971 789.635 510.091 789.635 472.003C789.634 433.915 758.758 403.038 720.67 403.038H429.939C404.955 403.038 388.623 391.886 373.994 373.623L277.349 252.966C262.194 234.045 275.664 205.996 299.905 205.995H720.67Z M396.605 720.706C407.798 705.406 430.443 704.843 442.381 719.568L503.816 797.018H502.786L535.569 838.934C548.074 854.358 549.943 877.191 538.047 893.09L476.638 972.545C465.692 986.707 448.803 995 430.903 995H242.276C218.18 995 204.665 967.248 219.523 948.278L337.992 797.018H337.923L396.605 720.706Z";
function AriaMark({
size,
trace,
fill,
color = ARIA,
}: {
size: number;
trace: number; // 0..1 outline trace
fill: number; // 0..1 bottom-up fill
color?: string;
}) {
const clipId = React.useId();
const fillH = 790 * fill;
return (
<svg
width={size}
height={size * (790 / 800)}
viewBox="200 206 800 790"
fill="none"
style={{ display: "block" }}
>
<defs>
<clipPath id={clipId}>
<rect x={200} y={996 - fillH} width={800} height={fillH} />
</clipPath>
</defs>
<path
d={ARIA_PATH}
pathLength={1}
stroke={color}
strokeWidth={7}
strokeDasharray={1}
strokeDashoffset={1 - trace}
opacity={interpolate(fill, [0.75, 1], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
})}
/>
<path d={ARIA_PATH} fill={color} clipPath={`url(#${clipId})`} />
</svg>
);
}
// ---------------------------------------------------------------------------
// Motion helpers — one camera: enter from below, exit upward.
// ---------------------------------------------------------------------------
function useRise(
at: number,
{
dist = 32,
damping = 15,
stiffness = 120,
exitAt,
}: {
dist?: number;
damping?: number;
stiffness?: number;
exitAt?: number;
} = {},
) {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const s = spring({
frame: frame - at,
fps,
config: { damping, stiffness, mass: 1 },
});
const enterOpacity = interpolate(frame, [at, at + 12], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
let y = (1 - s) * dist;
let opacity = enterOpacity;
if (exitAt !== undefined) {
const e = interpolate(frame, [exitAt, exitAt + 14], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.in(Easing.cubic),
});
y -= e * 40;
opacity *= 1 - e;
}
return { transform: `translateY(${y}px)`, opacity };
}
function Rise({
at,
dist,
damping,
stiffness,
exitAt,
style,
children,
}: {
at: number;
dist?: number;
damping?: number;
stiffness?: number;
exitAt?: number;
style?: React.CSSProperties;
children: React.ReactNode;
}) {
const anim = useRise(at, { dist, damping, stiffness, exitAt });
return <div style={{ ...style, ...anim }}>{children}</div>;
}
// A violet focus ring — React Aria's signature — springing onto a box.
function FocusRing({
at,
radius,
inset = -6,
}: {
at: number;
radius: number;
inset?: number;
}) {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const s = spring({
frame: frame - at,
fps,
config: { damping: 12, stiffness: 170, mass: 0.9 },
});
const opacity = interpolate(frame, [at, at + 6], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const scale = 1.06 - 0.06 * s;
return (
<div
style={{
position: "absolute",
inset,
borderRadius: radius,
border: `2px solid ${ARIA}`,
boxShadow: "0 0 0 4px rgba(127,87,255,0.30)",
opacity,
transform: `scale(${scale})`,
}}
/>
);
}
// A value caption that rises under a component demo.
function Caption({ at, text }: { at: number; text: string }) {
return (
<Rise
at={at}
dist={26}
style={{
position: "absolute",
left: 0,
right: 0,
top: 552,
display: "flex",
justifyContent: "center",
}}
>
<span style={{ fontSize: 30, color: INK, fontWeight: 400 }}>{text}</span>
</Rise>
);
}
// A small drawn check, used by the ComboBox selected row.
function CheckMark({ at, size = 18, color = "#fff" }: { at: number; size?: number; color?: string }) {
const frame = useCurrentFrame();
const p = interpolate(frame, [at, at + 10], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.bezier(0.3, 0, 0.2, 1),
});
const LEN = 18;
return (
<svg width={size} height={size} viewBox="0 0 20 20" fill="none">
<polyline
points="4,10.5 8.5,15 16,6"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={LEN}
strokeDashoffset={LEN * (1 - p)}
/>
</svg>
);
}
// ---------------------------------------------------------------------------
// Scene 1 — the news, named (opener)
// ---------------------------------------------------------------------------
function Lockup({
size,
drawStart,
gap = 32,
crossSize = 18,
}: {
size: number;
drawStart: number;
gap?: number;
crossSize?: number;
}) {
const frame = useCurrentFrame();
const draw = interpolate(frame, [drawStart, drawStart + 34], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.bezier(0.3, 0, 0.2, 1),
});
const trace = interpolate(frame, [drawStart + 4, drawStart + 38], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.bezier(0.3, 0, 0.3, 1),
});
const fill = interpolate(frame, [drawStart + 26, drawStart + 54], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
easing: Easing.bezier(0.3, 0, 0.2, 1),
});
const crossOpacity = interpolate(
frame,
[drawStart + 30, drawStart + 42],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" },
);
return (
<div style={{ display: "flex", alignItems: "center", gap }}>
<ShadcnMark size={size} progress={draw} />
<span
style={{
fontSize: crossSize,
color: MUTED,
opacity: crossOpacity,
fontFamily: SANS,
}}
>
✕
</span>
<AriaMark size={size} trace={trace} fill={fill} />
</div>
);
}
function IntroScene() {
return (
<AbsoluteFill>
<div
style={{
position: "absolute",Showing the first 400 of 1139 lines. View the full file on GitHub.