# @jarvis-ui Full Context
> An intentionally random collection of components and blocks, likely only useful to one person (me).
This file expands the Markdown pages listed in https://ui.jarv.is/llms.txt. Use https://ui.jarv.is/registry.json for the machine-readable shadcn registry index.
---
URL: https://ui.jarv.is/docs.md
Install @jarvis-ui items with the shadcn CLI.
# Introduction
Install @jarvis-ui items with the shadcn CLI.
@jarvis-ui is a shadcn-compatible registry of copy-and-paste components, blocks, hooks, and
utilities for product interfaces, powered by [\_cn](https://github.com/jakejarvis/_cn).
Install any @jarvis-ui item with the command shown on its docs page.
```sh
npx shadcn@latest add https://ui.jarv.is/r/copy-button.json
```
Use the package manager selector on item pages to switch between npm, pnpm, yarn, and bun commands. The installed files are regular React components you can edit in your app.
You can also manually add the registry directly to your [`components.json` file](https://ui.shadcn.com/docs/components-json):
```json
{
"registries": {
"@jarvis-ui": "https://ui.jarv.is/r/{name}.json"
}
}
```
Once configured, you can omit the full URL and refer to the registry simply as `@jarvis-ui` when adding new components:
```sh
npx shadcn@latest add @jarvis-ui/copy-button
```
---
URL: https://ui.jarv.is/docs/changelog.md
Recent changes to the registry or this site.
# Changelog
Recent changes to the registry or this site.
## April 25, 2026
- Migrated to the [\_cn template](https://github.com/jakejarvis/_cn).
---
URL: https://ui.jarv.is/components.md
Installable UI primitives and components.
# Components
Installable UI primitives and components.
- [Browser Window](https://ui.jarv.is/components/browser-window): A compact browser chrome frame for screenshots and previews.
- [Cookie Consent](https://ui.jarv.is/components/cookie-consent): A compact cookie consent card with pluggable accept and decline handlers.
- [Copy Button](https://ui.jarv.is/components/copy-button): A button that copies text to the clipboard with optional feedback.
- [Copyable Field](https://ui.jarv.is/components/copyable-field): A selectable, horizontally scrollable field with a copy action.
- [Parallax Card](https://ui.jarv.is/components/parallax-card): A generic card with pointer-driven tilt, glare, and parallax motion.
- [Scroll Area](https://ui.jarv.is/components/scroll-area): A scroll container with overlay scrollbars and automatic edge fades.
- [Stepper](https://ui.jarv.is/components/stepper): A keyboard-navigable multi-step flow with flexible triggers and panels.
- [Tipover](https://ui.jarv.is/components/tipover): A touch-friendly tooltip/popover hybrid.
- [Toast](https://ui.jarv.is/components/toast): A stacked toast manager built on Base UI with promise helpers, inline actions, and optional expandable details.
- [Video Player](https://ui.jarv.is/components/video-player): A themed media-chrome video player with composable controls.
---
URL: https://ui.jarv.is/components/browser-window.md
A compact browser chrome frame for screenshots and previews.
# Browser Window
A compact browser chrome frame for screenshots and previews.
## Installation
```bash
npx shadcn@latest add https://ui.jarv.is/r/browser-window.json
```
[Registry JSON](https://ui.jarv.is/r/browser-window.json)
## Preview
```tsx
import { BrowserWindow, BrowserWindowContent } from "@/components/ui/browser-window";
export function Preview() {
return (
);
}
```
## Source
### ui/browser-window.tsx
```tsx
import type * as React from "react";
import { cn } from "@/lib/utils";
type BrowserWindowProps = React.ComponentProps<"div"> & {
address?: React.ReactNode;
addressHref?: string;
};
function BrowserWindow({
address = "about:blank",
addressHref,
className,
children,
...props
}: BrowserWindowProps) {
return (
);
}
function BrowserWindowContent({ className, ...props }: React.ComponentProps<"div">) {
return (
);
}
function BrowserWindowImage({ className, alt, ...props }: React.ComponentProps<"img">) {
return (
);
}
export { BrowserWindow, BrowserWindowContent, BrowserWindowImage };
```
## Usage
### Basic frame
Use the browser window to frame product screenshots, app previews, or embedded content with a
lightweight browser chrome.
```tsx
import { BrowserWindow, BrowserWindowContent } from "@/components/ui/browser-window";
export function Example() {
return (
);
}
```
### Linked address
Pass `addressHref` when the address should open the live page in a new tab.
```tsx
import { BrowserWindow, BrowserWindowContent } from "@/components/ui/browser-window";
export function DocsPreview() {
return (
Components
);
}
```
### Screenshots
Use `BrowserWindowImage` when the framed content is a static screenshot.
```tsx
import {
BrowserWindow,
BrowserWindowContent,
BrowserWindowImage,
} from "@/components/ui/browser-window";
export function ScreenshotFrame() {
return (
);
}
```
### Embedded content
The content area is just a styled container, so you can render live UI inside it.
```tsx
import { BrowserWindow, BrowserWindowContent } from "@/components/ui/browser-window";
export function EmptyStatePreview() {
return (
{children ?? (
<>
We use cookies to understand how you use our service.{" "}
{learnMoreLabel}
>
)}
);
}
export { CookieConsent, type CookieConsentDecision, type CookieConsentProps };
```
## Usage
### Basic consent
Use controlled or uncontrolled state to connect the consent card to your preference storage.
```tsx
import { CookieConsent } from "@/components/ui/cookie-consent";
export function Example() {
return console.log(decision)} />;
}
```
### Store the decision
Use `onConsentChange` to persist the user decision in your own storage layer.
```tsx
import { CookieConsent } from "@/components/ui/cookie-consent";
export function ConsentBanner() {
return (
{
localStorage.setItem("cookie-consent", decision);
}}
/>
);
}
```
### Controlled display
Control `open` when the consent state comes from a loader, server response, or settings page.
```tsx
"use client";
import { useState } from "react";
import {
CookieConsent,
type CookieConsentDecision,
} from "@/components/ui/cookie-consent";
function saveCookieDecision(decision: CookieConsentDecision) {
localStorage.setItem("cookie-consent", decision);
}
export function ControlledConsent() {
const [open, setOpen] = useState(true);
return (
{
saveCookieDecision(decision);
}}
/>
);
}
```
### Custom copy
Customize labels and body copy when the consent message needs to match your product policy.
```tsx
import { CookieConsent } from "@/components/ui/cookie-consent";
export function AnalyticsConsent() {
return (
We use privacy-friendly analytics to understand which product areas need work.{" "}
Read the privacy policy.
);
}
```
---
URL: https://ui.jarv.is/components/copy-button.md
A button that copies text to the clipboard with optional feedback.
# Copy Button
A button that copies text to the clipboard with optional feedback.
## Installation
```bash
npx shadcn@latest add https://ui.jarv.is/r/copy-button.json
```
[Registry JSON](https://ui.jarv.is/r/copy-button.json)
## Preview
```tsx
import { CopyButton } from "@/components/ui/copy-button";
export function Preview() {
return (
);
}
```
## Source
### ui/copy-button.tsx
```tsx
"use client";
import { IconCheck, IconCopy } from "@tabler/icons-react";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
type CopyButtonClickEvent = Parameters<
NonNullable["onClick"]>
>[0];
type CopyButtonProps = Omit, "children" | "value"> & {
value: string | (() => string);
showLabel?: boolean;
copyLabel?: string;
copiedLabel?: string;
resetDelay?: number;
onCopied?: (value: string, event: CopyButtonClickEvent) => void;
onCopyError?: (error: unknown, event: CopyButtonClickEvent) => void;
};
function CopyButton({
value,
showLabel = false,
copyLabel = "Copy to clipboard",
copiedLabel = "Copied",
resetDelay = 2000,
variant = "ghost",
size,
className,
disabled,
onClick,
onCopied,
onCopyError,
...props
}: CopyButtonProps) {
const resetTimeoutRef = React.useRef(undefined);
const [copied, setCopied] = React.useState(false);
const buttonSize: React.ComponentProps["size"] =
size ?? (showLabel ? "sm" : "icon-sm");
React.useEffect(() => {
return () => window.clearTimeout(resetTimeoutRef.current);
}, []);
const handleClick = React.useCallback(
async (event: CopyButtonClickEvent) => {
onClick?.(event);
if (event.defaultPrevented || copied || disabled) {
return;
}
try {
const textToCopy = typeof value === "function" ? value() : value;
await copyTextToClipboard(textToCopy);
window.clearTimeout(resetTimeoutRef.current);
setCopied(true);
resetTimeoutRef.current = window.setTimeout(() => setCopied(false), resetDelay);
onCopied?.(textToCopy, event);
} catch (error) {
setCopied(false);
onCopyError?.(error, event);
}
},
[copied, disabled, onClick, onCopied, onCopyError, resetDelay, value],
);
return (
);
}
async function copyTextToClipboard(value: string) {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(value);
return;
}
const textArea = document.createElement("textarea");
textArea.value = value;
textArea.setAttribute("readonly", "");
textArea.style.position = "fixed";
textArea.style.inset = "0 auto auto -9999px";
document.body.append(textArea);
textArea.select();
const copied = document.execCommand("copy");
textArea.remove();
if (!copied) {
throw new Error("Copy command was rejected.");
}
}
export { CopyButton, type CopyButtonProps };
```
## Usage
### Icon button
Use the copy button for command snippets, tokens, URLs, and other short values.
```tsx
import { CopyButton } from "@/components/ui/copy-button";
export function Example() {
return ;
}
```
### Labeled button
Show a label when the copy action is one of several visible actions in a toolbar or card.
```tsx
import { CopyButton } from "@/components/ui/copy-button";
export function CopyInstallCommand() {
return (
);
}
```
### Dynamic values
Pass a function when the value should be read at click time, such as a generated token.
```tsx
import { useRef } from "react";
import { CopyButton } from "@/components/ui/copy-button";
export function CopyLatestToken() {
const tokenInputRef = useRef(null);
return (
Add your own content and keep the motion shell reusable.
);
}
```
### Tune the motion
Lower the tilt and hover scale for dense product cards, or increase them for expressive marketing
surfaces.
```tsx
import { ParallaxCard } from "@/components/ui/parallax-card";
export function ProductMetricCard() {
return (
Pipeline health
98.4%
);
}
```
### Disable motion
Set `disabled` when the same card needs to render in a static context such as a print view,
thumbnail grid, or automated screenshot.
```tsx
import { ParallaxCard } from "@/components/ui/parallax-card";
export function StaticPreviewCard() {
return (
Static preview
);
}
```
### Custom layers
Use the content and glare class hooks when the card needs tighter art direction.
```tsx
import { ParallaxCard } from "@/components/ui/parallax-card";
export function ArtworkCard() {
return (
Campaign artwork
);
}
```
---
URL: https://ui.jarv.is/components/scroll-area.md
A scroll container with overlay scrollbars and automatic edge fades.
# Scroll Area
A scroll container with overlay scrollbars and automatic edge fades.
## Installation
```bash
npx shadcn@latest add https://ui.jarv.is/r/scroll-area.json
```
[Registry JSON](https://ui.jarv.is/r/scroll-area.json)
## Preview
```tsx
import { ScrollArea } from "@/components/ui/scroll-area";
export function Preview() {
const activity = [
"Renewed domainstack.io",
"Queued DNS audit",
"Pinned registrar notes",
"Updated MX records",
"Synced billing contact",
"Archived expired domains",
"Reviewed transfer lock",
"Checked nameserver drift",
"Scheduled renewal reminder",
"Exported portfolio report",
];
return (
{activity.map((item, index) => (
{item}#{index + 1}
))}
);
}
```
## Source
### ui/scroll-area.tsx
```tsx
"use client";
import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
import type * as React from "react";
import { cn } from "@/lib/utils";
type ScrollAreaProps = ScrollAreaPrimitive.Root.Props & {
scrollFade?: boolean;
scrollbarGutter?: boolean;
hideScrollbar?: boolean;
scrollRef?: React.Ref;
contentRef?: React.Ref;
};
function ScrollArea({
className,
children,
scrollFade = true,
scrollbarGutter = false,
hideScrollbar = false,
scrollRef,
contentRef,
...props
}: ScrollAreaProps) {
return (
{children}
{!hideScrollbar && (
<>
>
)}
);
}
function ScrollBar({
className,
orientation = "vertical",
...props
}: ScrollAreaPrimitive.Scrollbar.Props) {
return (
);
}
export { ScrollArea, ScrollBar };
```
## Usage
### Basic viewport
Use the scroll area when content needs a compact viewport with subtle edge fades and overlay
scrollbars.
```tsx
import { ScrollArea } from "@/components/ui/scroll-area";
export function Example() {
return ...;
}
```
### Lists and sidebars
Give the root a fixed height and put normal block content inside.
```tsx
import { ScrollArea } from "@/components/ui/scroll-area";
export function ActivityList({ items }: { items: string[] }) {
return (
{items.map((item) => (
{item}
))}
);
}
```
### Horizontal overflow
Use it for long single-line content too. The edge fade works on both axes.
```tsx
import { ScrollArea } from "@/components/ui/scroll-area";
export function CommandStrip() {
return (
) : null}
);
}
function ToastList({
icons,
closeButton,
position,
richColors,
}: {
icons: Partial>;
closeButton: boolean;
position: ToastPosition;
richColors: boolean;
}) {
const { toasts } = ToastPrimitive.useToastManager();
return toasts.map((t) => (
));
}
export interface ToasterProps {
/**
* Where toasts appear on screen.
* @default "bottom-right"
*/
position?: ToastPosition;
/**
* Maximum number of visible toasts before older ones are removed.
* @default 3
*/
visibleToasts?: number;
/**
* Default auto-dismiss duration in milliseconds.
* @default 4000
*/
duration?: number;
/**
* Show a close button on every toast.
* @default false
*/
closeButton?: boolean;
/**
* Use colorful backgrounds for typed toasts (success, error, etc.).
* @default false
*/
richColors?: boolean;
/**
* Gap between toasts in pixels.
* @default 14
*/
gap?: number;
/**
* Distance from viewport edges in pixels.
* @default 32
*/
offset?: number;
/**
* Override default icons per toast type.
*/
icons?: Partial>;
}
function Toaster({
position = "bottom-right",
visibleToasts = 3,
duration = 4000,
closeButton = false,
richColors = false,
gap = 14,
offset = 32,
icons = {},
}: ToasterProps) {
const viewportStyle: ToastViewportStyle = {
"--toast-offset": `${offset}px`,
"--toast-gap": `${gap}px`,
"--toast-peek": "10px",
};
return (
);
}
async function copyTextToClipboard(value: string) {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(value);
return;
}
const textArea = document.createElement("textarea");
textArea.value = value;
textArea.setAttribute("readonly", "");
textArea.style.position = "fixed";
textArea.style.inset = "0 auto auto -9999px";
document.body.append(textArea);
textArea.select();
const copied = document.execCommand("copy");
textArea.remove();
if (!copied) {
throw new Error("Copy command was rejected.");
}
}
export { Toaster };
```
## Usage
### App root setup
Render one `Toaster` near your React app root, then call `toast` from any client component.
```tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Toaster } from "@/components/ui/toast";
import { App } from "./app";
import "./index.css";
createRoot(document.getElementById("root")!).render(
,
);
```
### Basic feedback
Use basic typed toasts for routine product feedback.
```tsx
import { toast } from "@/components/ui/toast";
export function SaveButton() {
return ;
}
```
### Descriptions
Add descriptions when the user needs context, not just confirmation.
```tsx
toast.success("Deployment complete", {
description: "Production is now serving build 1042.",
});
toast.warning("Verification paused", {
description: "A required secret is missing for the production smoke test.",
});
```
### Actions
Use actions for reversible operations or next steps.
```tsx
toast.info("Invite copied", {
action: {
label: "Undo",
onClick: () => {
void restoreInvite(inviteId);
},
},
});
```
### Promise states
Use `toast.promise` when a toast should track an async operation from loading to final state.
```tsx
await toast.promise(publishRelease(), {
loading: {
title: "Publishing release",
description: "Uploading assets and warming the deployment.",
},
success: (release) => ({
title: "Release published",
description: `${release.version} is live in production.`,
}),
error: (error) => ({
title: "Publish failed",
description: error instanceof Error ? error.message : "Try again from the releases page.",
}),
});
```
### Error details
Error toasts with string descriptions include a copy-details button by default. Set
`hideCopyButton` when copying the description is not useful.
```tsx
toast.error("Upload failed", {
description:
"The source map upload hit a permissions error. Re-authenticate the CI token and retry.",
closeButton: true,
});
```
### Expandable details
Use expandable content for diagnostics, checklists, or secondary detail that should not
dominate the collapsed toast.
```tsx
toast.warning("Verification paused", {
description: "A required secret is missing for the production smoke test.",
actionLayout: "stacked-end",
actionVariant: "outline",
action: {
label: "Open checklist",
onClick: () => openDeploymentChecklist(),
},
expandableContent: (
`VERCEL_TOKEN` is not configured for this environment.
`POSTHOG_API_KEY` is missing from the deployment step.
Smoke tests were skipped after the build artifact upload.
),
});
```
### Manual dismissal
Use a longer-lived loading toast when work crosses screens, then dismiss it explicitly.
```tsx
const toastId = toast.loading("Syncing workspace", {
description: "This can take a few seconds.",
duration: 30_000,
});
try {
await syncWorkspace();
toast.success("Workspace synced");
} finally {
toast.dismiss(toastId);
}
```
### API
Refer to the [Base UI documentation](https://base-ui.com/react/components/toast) for more details.
---
URL: https://ui.jarv.is/components/video-player.md
A themed media-chrome video player with composable controls.
# Video Player
A themed media-chrome video player with composable controls.
## Installation
```bash
npx shadcn@latest add https://ui.jarv.is/r/video-player.json
```
[Registry JSON](https://ui.jarv.is/r/video-player.json)
## Preview
```tsx
import {
VideoPlayer,
VideoPlayerContent,
VideoPlayerControlBar,
VideoPlayerMuteButton,
VideoPlayerPlayButton,
VideoPlayerSeekBackwardButton,
VideoPlayerSeekForwardButton,
VideoPlayerTimeDisplay,
VideoPlayerTimeRange,
VideoPlayerVolumeRange,
} from "@/components/ui/video-player";
export function Preview() {
return (
Sample video from MDN Web Docs.
);
}
```
## Source
### ui/video-player.tsx
```tsx
"use client";
import {
MediaControlBar,
MediaController,
MediaMuteButton,
MediaPlayButton,
MediaSeekBackwardButton,
MediaSeekForwardButton,
MediaTimeDisplay,
MediaTimeRange,
MediaVolumeRange,
} from "media-chrome/react";
import type * as React from "react";
import { cn } from "@/lib/utils";
type VideoPlayerVariables = React.CSSProperties & Record<`--${string}`, string>;
type VideoPlayerContentProps = React.ComponentProps<"video"> & {
captionsSrc?: string;
captionsLabel?: string;
captionsSrcLang?: string;
};
const videoPlayerVariables = {
"--media-primary-color": "var(--primary)",
"--media-secondary-color": "var(--background)",
"--media-text-color": "var(--foreground)",
"--media-background-color": "var(--background)",
"--media-control-hover-background": "var(--accent)",
"--media-font-family": "var(--font-sans)",
"--media-live-button-icon-color": "var(--muted-foreground)",
"--media-live-button-indicator-color": "var(--destructive)",
"--media-range-track-background": "var(--border)",
} satisfies VideoPlayerVariables;
const emptyCaptionsSrc = "data:text/vtt,WEBVTT%0A%0A";
function VideoPlayer({ style, ...props }: React.ComponentProps) {
return ;
}
function VideoPlayerControlBar(props: React.ComponentProps) {
return ;
}
function VideoPlayerTimeRange({
className,
...props
}: React.ComponentProps) {
return ;
}
function VideoPlayerTimeDisplay({
className,
...props
}: React.ComponentProps) {
return ;
}
function VideoPlayerVolumeRange({
className,
...props
}: React.ComponentProps) {
return ;
}
function VideoPlayerPlayButton({
className,
...props
}: React.ComponentProps) {
return ;
}
function VideoPlayerSeekBackwardButton({
className,
...props
}: React.ComponentProps) {
return (
);
}
function VideoPlayerSeekForwardButton({
className,
...props
}: React.ComponentProps) {
return (
);
}
function VideoPlayerMuteButton({
className,
...props
}: React.ComponentProps) {
return ;
}
function VideoPlayerContent({
className,
children,
captionsSrc = emptyCaptionsSrc,
captionsLabel = "Captions",
captionsSrcLang = "en",
...props
}: VideoPlayerContentProps) {
return (
);
}
export {
VideoPlayer,
VideoPlayerControlBar,
VideoPlayerTimeRange,
VideoPlayerTimeDisplay,
VideoPlayerVolumeRange,
VideoPlayerPlayButton,
VideoPlayerSeekBackwardButton,
VideoPlayerSeekForwardButton,
VideoPlayerMuteButton,
VideoPlayerContent,
};
```
## Usage
### Basic player
Use the video player wrappers to compose a media-chrome player with your app theme tokens.
```tsx
import { VideoPlayer, VideoPlayerContent, VideoPlayerControlBar } from "@/components/ui/video-player";
export function Example() {
return (
);
}
```
### Full controls
Compose only the controls your player needs. The wrappers forward props to `media-chrome`.
```tsx
import {
VideoPlayer,
VideoPlayerContent,
VideoPlayerControlBar,
VideoPlayerMuteButton,
VideoPlayerPlayButton,
VideoPlayerTimeDisplay,
VideoPlayerTimeRange,
VideoPlayerVolumeRange,
} from "@/components/ui/video-player";
export function TrainingVideo() {
return (
);
}
```
### Captions
Pass a captions track when the media has subtitles or transcripts.
```tsx
import { VideoPlayer, VideoPlayerContent, VideoPlayerControlBar } from "@/components/ui/video-player";
export function CaptionedVideo() {
return (
);
}
```
### Theme overrides
Override media-chrome CSS variables through `style` for one-off branded players.
```tsx
import type { CSSProperties } from "react";
import { VideoPlayer, VideoPlayerContent, VideoPlayerControlBar } from "@/components/ui/video-player";
type VideoTheme = CSSProperties & Record<`--${string}`, string>;
const playerTheme = {
"--media-primary-color": "white",
"--media-secondary-color": "black",
"--media-control-hover-background": "rgb(255 255 255 / 0.16)",
} satisfies VideoTheme;
export function BrandedVideo() {
return (
);
}
```
### Seek controls
Add skip controls for long-form content where users need quick navigation.
```tsx
import {
VideoPlayerControlBar,
VideoPlayerSeekBackwardButton,
VideoPlayerSeekForwardButton,
} from "@/components/ui/video-player";
export function PlaybackControls() {
return (
);
}
```