# duck/ui (full) > A dark-first design system with holographic sticker energy. Installs through the shadcn CLI under the @duck namespace, so your editor and your AI assistant already know how to add it. Source of truth: https://duckui.davideghiotto.it. Registry index: https://duckui.davideghiotto.it/r/registry.json. ## Setup ```json // components.json { "registries": { "@duck": "https://duckui.davideghiotto.it/r/{name}.json" } } ``` ```bash npx shadcn add @duck/theme # always first npx shadcn@latest add @duck/quack-button ``` ## Design rules 1. One holo element per viewport. The iridescent finish marks the single most important thing on screen; a second one halves the value of the first. 2. Lime is the meal, holo is the seasoning. Default actions use the solid `primary` variant. 3. One idle animation per viewport. Reactive animation (press, ripple, state change) has no budget because the user caused it. 4. Dark is designed first. Light is derived and checked separately. 5. Semantic tokens only. Components never reference a raw color. 6. Compose with shadcn/ui for everything duck/ui does not ship. ## Tokens Standard shadcn contract: background, foreground, card, card-foreground, popover, popover-foreground, primary, primary-foreground, secondary, secondary-foreground, muted, muted-foreground, accent, accent-foreground, destructive, destructive-foreground, border, input, ring, chart-1 to chart-5, radius. duck extras: `--holo` (linear gradient), `--foil` (conic gradient), `--glow` and `--glow-primary` (box shadows), `--sticker-border` (border width), `--sheet` / `--sheet-line` / `--cut` (sticker sheet backing), `--fx` / `--fy` / `--rx` / `--ry` (pointer position, written by useHoloPointer). ## Guides - Introduction: What duck/ui is, what it is not, and the four rules it runs on. (https://duckui.davideghiotto.it/docs) - Installation: Add the @duck registry to components.json and install the theme. (https://duckui.davideghiotto.it/docs/installation) - Theming: The token contract, the duck extras, and how to retune the palette. (https://duckui.davideghiotto.it/docs/theming) - Motion: The animation vocabulary: idle loops, transitions, and reduced motion. (https://duckui.davideghiotto.it/docs/motion) - For AI assistants: llms.txt, the shadcn MCP server, and the duck/ui skill. (https://duckui.davideghiotto.it/docs/ai) ## Components ### Quack Button A button with a full motion cycle: an idle animation while it waits, magnetic pull, a squash on press, and animated loading, success and error states. - Install: `npx shadcn@latest add @duck/quack-button` - Import: `import { QuackButton, quackButtonVariants } from "@/components/ui/quack-button"` - Category: Actions - Rendering: client component - npm dependencies: class-variance-authority, @radix-ui/react-slot, lucide-react - registry dependencies: @duck/theme, @duck/duck-spinner, @duck/use-holo-pointer | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"primary" | "holo" | "outline" | "ghost" | "danger"` | `"primary"` | Visual weight. Use holo for at most one button per screen. | | `size` | `"sm" | "default" | "lg" | "icon"` | `"default"` | Control height and padding. | | `idle` | `"none" | "breathe" | "sheen" | "pulse" | "float"` | `"none"` | Animation played while the button rests. One idle button per screen keeps the page calm. | | `magnetic` | `number` | `0` | Maximum pull toward the pointer in px. 0 turns magnetism off. | | `ripple` | `boolean` | `true` | Water ripple from the exact press point. | | `state` | `"idle" | "loading" | "success" | "error"` | `"idle"` | Drives the transition. Loading also sets aria-busy and disables the button. | | `loadingLabel` | `string` | - | Replaces the label while loading. | | `successLabel` | `string` | - | Replaces the label on success. | | `errorLabel` | `string` | - | Replaces the label on error. | | `asChild` | `boolean` | `false` | Render the child element instead of a button, for links. | Rules: - One idle animation per screen. Two competing loops read as a broken page. - Keep the label stable between idle and loading unless the wait is long enough to explain. - State is controlled. Reset it to idle yourself once the work finishes. ### Holo Button The signature CTA: an animated iridescent border on a plain button. No motion states, no pointer tracking, nothing to wire up. - Install: `npx shadcn@latest add @duck/holo-button` - Import: `import { HoloButton, holoButtonVariants } from "@/components/ui/holo-button"` - Category: Actions - Rendering: server safe - npm dependencies: class-variance-authority, @radix-ui/react-slot - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"holo" | "primary" | "outline" | "ghost"` | `"holo"` | holo is the iridescent border. primary is solid duck lime. | | `size` | `"sm" | "default" | "lg" | "icon"` | `"default"` | Control height and padding. | | `asChild` | `boolean` | `false` | Render the child element instead of a button, for links. | Rules: - Reach for holo-button when you want the look and nothing else. Reach for quack-button when the button has work to report. ### Copy Button Copies a string to the clipboard and confirms it. The check pops in, then the button returns to rest. - Install: `npx shadcn@latest add @duck/copy-button` - Import: `import { CopyButton } from "@/components/ui/copy-button"` - Category: Actions - Rendering: client component - npm dependencies: lucide-react - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | `string` | - | The string written to the clipboard. | | `label` | `string` | `"Copy"` | Accessible label at rest. | | `copiedLabel` | `string` | `"Copied"` | Accessible label after a successful copy. | | `onCopied` | `(value: string) => void` | - | Fires after the clipboard write resolves. | ### Sticker Card The die-cut sticker: thick border, generous radius, soft glow. Optional iridescent ring, pointer tilt and a corner that peels off the backing. - Install: `npx shadcn@latest add @duck/sticker-card` - Import: `import { StickerCard, StickerCardHeader, StickerCardTitle, StickerCardDescription, StickerCardContent, StickerCardFooter } from "@/components/ui/sticker-card"` - Category: Surfaces - Rendering: client component - registry dependencies: @duck/theme, @duck/use-holo-pointer | Prop | Type | Default | Description | | --- | --- | --- | --- | | `holo` | `boolean` | `false` | Swap the solid border for the iridescent ring. | | `tilt` | `boolean` | `false` | The card leans toward the pointer. | | `peel` | `boolean` | `false` | A corner lifts off the backing on hover. | Rules: - Peel and tilt together is a lot. Pick one per card. - In a grid of cards, at most one carries holo. ### Code Window A code block in a window frame, with a filename, an optional copy control and optional line numbers. Accepts plain text or pre-highlighted markup. - Install: `npx shadcn@latest add @duck/code-window` - Import: `import { CodeWindow } from "@/components/ui/code-window"` - Category: Surfaces - Rendering: server safe - registry dependencies: @duck/theme, @duck/copy-button | Prop | Type | Default | Description | | --- | --- | --- | --- | | `title` | `string` | - | Filename shown in the window bar. | | `html` | `string` | - | Pre-highlighted markup, for example from Shiki. Takes precedence over children. | | `copyValue` | `string` | - | Shows a copy control that writes this string. | | `lineNumbers` | `boolean` | `false` | Number each line. Needs markup that wraps lines in .line. | | `holo` | `boolean` | `false` | Iridescent frame instead of the solid border. | ### Terminal A command line that types itself. It waits until it scrolls into view, so the demo is never already over by the time it is seen. - Install: `npx shadcn@latest add @duck/terminal` - Import: `import { Terminal } from "@/components/ui/terminal"` - Category: Surfaces - Rendering: client component - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `lines` | `{ prompt?: string; text: string; output?: string }[]` | - | Commands to type, each with optional output printed underneath. | | `title` | `string` | `"bash"` | Window bar label. | | `prompt` | `string` | `"$"` | Default prompt character. | | `speed` | `number` | `34` | Milliseconds per character. | | `loop` | `boolean` | `false` | Replay from the top after a pause. | | `holo` | `boolean` | `false` | Iridescent frame. | Rules: - Under prefers-reduced-motion the whole transcript renders at once. - Keep transcripts under six lines. A terminal that types for a minute is a video, not a component. ### Sticker Sheet Backing paper with kiss-cut lines. Lays out a set of components the way a vinyl sheet lays out stickers. - Install: `npx shadcn@latest add @duck/sticker-sheet` - Import: `import { StickerSheet, StickerSheetCell } from "@/components/ui/sticker-sheet"` - Category: Surfaces - Rendering: server safe - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | - | Small caption printed in the sheet margin. | | `span` | `1 | 2 | 3` | `1` | On StickerSheetCell: cell width in grid columns at lg and up. | ### Video Card A YouTube card that stays cheap until it is wanted. The player only mounts on click, so a page full of these costs nothing on load. - Install: `npx shadcn@latest add @duck/video-card` - Import: `import { VideoCard } from "@/components/ui/video-card"` - Category: Surfaces - Rendering: client component - npm dependencies: lucide-react - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `videoId` | `string` | - | The part of the URL after v=. | | `title` | `string` | - | Card heading and iframe title. | | `channel` | `string` | - | Line under the title. | | `duration` | `string` | - | Runtime label, for example "12:04". | | `thumbnail` | `string` | - | Override the default YouTube poster. | | `holo` | `boolean` | `false` | Iridescent frame. | ### Holo Avatar A die-cut avatar sticker. The foil ring picks up the pointer the way a real holographic sticker picks up a light source. - Install: `npx shadcn@latest add @duck/holo-avatar` - Import: `import { HoloAvatar, HoloAvatarGroup, avatarVariants } from "@/components/ui/holo-avatar"` - Category: Display - Rendering: client component - npm dependencies: class-variance-authority - registry dependencies: @duck/theme, @duck/use-holo-pointer | Prop | Type | Default | Description | | --- | --- | --- | --- | | `src` | `string` | - | Image URL. | | `alt` | `string` | `""` | Alternative text for the image. | | `fallback` | `string` | - | Initials shown when the image is missing or fails. | | `size` | `"xs" | "sm" | "default" | "lg" | "xl"` | `"default"` | 28px through 96px. | | `shape` | `"circle" | "sticker"` | `"circle"` | sticker is a squircle with a slight tilt. | | `ring` | `"foil" | "holo" | "primary" | "none"` | `"foil"` | foil tracks the pointer. holo is the static gradient. | | `status` | `"online" | "away" | "offline"` | - | Dot in the lower corner, announced to screen readers. | | `max` | `number` | - | On HoloAvatarGroup: how many avatars to show before a +N chip. | Rules: - In a list of people, use ring="none" and give the foil to the one avatar that matters. ### Holo Badge A pill for status, counts and short labels. - Install: `npx shadcn@latest add @duck/holo-badge` - Import: `import { HoloBadge, holoBadgeVariants } from "@/components/ui/holo-badge"` - Category: Display - Rendering: server safe - npm dependencies: class-variance-authority - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | `"holo" | "primary" | "outline" | "muted" | "success" | "danger"` | `"holo"` | Pick by meaning, not by contrast. | ### Announcement A pill banner for one piece of news. A light sweeps across it on a slow loop so it reads as live without demanding attention. - Install: `npx shadcn@latest add @duck/announcement` - Import: `import { Announcement } from "@/components/ui/announcement"` - Category: Display - Rendering: server safe - npm dependencies: lucide-react - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `tag` | `string` | - | Short label in the leading chip, for example "new". | | `href` | `string` | - | Turns the banner into a link. | | `arrow` | `boolean` | `href ? true : false` | Trailing arrow that slides on hover. | ### Duck Spinner A duck paddling on water. The rings are the wake. - Install: `npx shadcn@latest add @duck/duck-spinner` - Import: `import { DuckSpinner, DuckGlyph } from "@/components/ui/duck-spinner"` - Category: Display - Rendering: server safe - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | `"sm" | "default" | "lg"` | `"default"` | 20px, 32px or 48px. | | `label` | `string` | `"Loading"` | Screen-reader text. The spinner is a live region. | ### Holo Separator A hairline that fades in from the edges. With a label it becomes a section break. - Install: `npx shadcn@latest add @duck/holo-separator` - Import: `import { HoloSeparator } from "@/components/ui/holo-separator"` - Category: Display - Rendering: server safe - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | - | Centered caption between two rules. | | `orientation` | `"horizontal" | "vertical"` | `"horizontal"` | Vertical stretches to the height of its flex parent. | | `holo` | `boolean` | `false` | Iridescent rule. | ### Glow Input Text input, textarea and a field wrapper that wires up label, helper text and errors so the control stays accessible. - Install: `npx shadcn@latest add @duck/glow-input` - Import: `import { GlowInput, GlowTextarea, GlowField } from "@/components/ui/glow-input"` - Category: Inputs - Rendering: server safe - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `label` | `string` | - | On GlowField: visible label above the control. | | `helper` | `string` | - | On GlowField: persistent hint below the control. | | `error` | `string` | - | On GlowField: replaces the helper, sets aria-invalid and role=alert. | | `required` | `boolean` | `false` | On GlowField: marks the control and adds the asterisk. | Rules: - Never use the placeholder as the label. GlowField exists so you do not have to. ### Duck Tabs Tabs with an indicator that slides between triggers. Arrows move, Home and End jump to the ends. - Install: `npx shadcn@latest add @duck/duck-tabs` - Import: `import { DuckTabs, DuckTabsList, DuckTabsTrigger, DuckTabsContent } from "@/components/ui/duck-tabs"` - Category: Navigation - Rendering: client component - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | `defaultValue` | `string` | - | Uncontrolled starting tab. | | `value` | `string` | - | Controlled active tab. | | `onValueChange` | `(value: string) => void` | - | Fires on every change. | ### Theme Switcher A three-way segmented control. The lime pill slides to the active option so the change reads as movement. - Install: `npx shadcn@latest add @duck/theme-switcher` - Import: `import { ThemeSwitcher } from "@/components/ui/theme-switcher"` - Category: Navigation - Rendering: client component - npm dependencies: next-themes, lucide-react - registry dependencies: @duck/theme | Prop | Type | Default | Description | | --- | --- | --- | --- | | - | - | - | No props. | Rules: - Needs a next-themes ThemeProvider above it with attribute="class". ### Quack Toast Transient messages that slide in from the corner. The queue is capped so a burst of events never buries the page. - Install: `npx shadcn@latest add @duck/quack-toast` - Import: `import { QuackToastProvider, useQuackToast } from "@/components/ui/quack-toast"` - Category: Feedback - Rendering: client component - npm dependencies: lucide-react - registry dependencies: @duck/theme, @duck/duck-spinner | Prop | Type | Default | Description | | --- | --- | --- | --- | | `max` | `number` | `3` | On QuackToastProvider: most toasts on screen at once. | | `toast` | `(options: ToastOptions) => void` | - | From useQuackToast. Takes title, description, variant and duration. | | `quack` | `() => void` | - | From useQuackToast. It quacks. | | `dismiss` | `(id: number) => void` | - | From useQuackToast. | Rules: - Toasts never steal focus. Anything the user must act on belongs inline, not in a toast. ## Hook ### useHoloPointer `import { useHoloPointer } from "@/hooks/use-holo-pointer"` Writes pointer position into CSS variables inside one animation frame, so foil, tilt and magnetism run entirely in CSS. Options: `tilt` (degrees, default 8), `magnet` (px, default 0), `reset` (return to rest on leave, default true), `disabled`. Returns a ref to attach to the element. Does nothing when the user prefers reduced motion.