DuckVizBeta
Packages

@duckviz/ui

Radix-based UI primitives and CSS theme system for DuckViz components.

@duckviz/ui provides lightweight, accessible UI components built on Radix UI primitives, plus a CSS custom property theme system. It's the shared design layer used by all other DuckViz packages.

Installation

npm install @duckviz/ui react react-dom

CSS auto-injection

Styles are bundled into the package and auto-injected when you import any component. No separate CSS import is needed.

Theme system

<DuckvizThemeProvider>

Wraps your app with CSS custom properties for theming. All DuckViz components read these variables.

import { DuckvizThemeProvider } from "@duckviz/ui";

<DuckvizThemeProvider preset="terracotta" mode="dark">
  {children}
</DuckvizThemeProvider>

Prop

Type

Built-in presets

10 presets are available out of the box:

PresetPrimary colorDescription
terracotta#c45a2cWarm earth tones (default)
oceanBlueCool ocean blues
forestGreenDeep forest greens
slateGrayNeutral slate gray
lavenderPurpleSoft lavender
emberOrange-redWarm embers
midnightDark blueDeep midnight blue
copperCopperWarm copper tones
rosePinkSoft rose
sageOliveEarthy sage green
import { ALL_PRESETS } from "@duckviz/ui";

// ALL_PRESETS is a readonly array of all preset objects
// Each preset has: { name, label, seeds: { primary, ... } }

Custom themes with buildThemeTokens()

Create a custom theme from a set of seed colors:

import { buildThemeTokens, DuckvizThemeProvider } from "@duckviz/ui";

const customTokens = buildThemeTokens({
  primary: "#2563eb",   // Blue primary
  surface: "#f8fafc",   // Light surface
  text: "#0f172a",      // Dark text
  border: "#e2e8f0",    // Light border
});

<DuckvizThemeProvider preset={customTokens} mode="light">
  {children}
</DuckvizThemeProvider>

useColorScheme()

React hook that returns the current color scheme:

import { useColorScheme } from "@duckviz/ui";

function MyComponent() {
  const { mode, resolvedMode } = useColorScheme();
  // mode: "light" | "dark" | "auto"
  // resolvedMode: "light" | "dark" (resolved from system if "auto")
}

Component library

All components are built on Radix UI primitives for accessibility and keyboard navigation.

Current version: 0.6.0. Recent additions:

  • 0.6.0Flex.gap accepts Mantine-style tokens ("xs" | "sm" | "md" | "lg" | "xl"10|12|16|20|32px). Previously token strings emitted invalid CSS (gap: lg) and silently dropped to 0. New exported type FlexGapToken. Raw numbers and CSS lengths still pass through untouched.
  • 0.5.0Modal always satisfies Radix's a11y contract. Chromeless modals (no title) no longer warn. New ariaDescription prop renders a visually-hidden Dialog.Description; the component opts out of the describedby warning when unset.
  • 0.4.0Drawer.position="bottom" slides up from the viewport edge (height via size). Drawer.nonBlocking lets pointer events through to content behind — useful for "tweak + regenerate" flows over a live dashboard.

Layout

ComponentDescription
FlexFlexbox layout container. gap accepts number, CSS length, or Mantine token (xs/sm/md/lg/xl)
TextText display with size and color props
XDivDesign-token-aware layout container with border, padding, width, height props
ScrollAreaScrollable container with custom scrollbar styling

Form controls

ComponentDescription
ButtonPrimary action button with variants: filled, subtle, outline, default, danger
ActionIconIcon-only button (square)
TextInputText input field with label, error, and section slots
TextareaMulti-line text input
CheckboxCheckbox with label
DropzoneFile drop zone for drag-and-drop uploads

Feedback

ComponentDescription
BadgeStatus badges and labels
AlertAlert banners
LoaderLoading spinner

Overlays

ComponentDescription
ModalDialog modal with title and close button
DrawerSide drawer panel
PopoverPopover floating panel (trigger + content)
TooltipHover tooltip
ComponentDescription
MenuDropdown menu (trigger + content + items)
TabsTab navigation (triggers + content panels)
NavLinkSidebar navigation link
StepperMulti-step progress indicator
PaginationPage navigation controls
AccordionCollapsible content sections

Example: Button

import { Button } from "@duckviz/ui";

<Button variant="filled" size="md" onClick={handleClick}>
  Generate Report
</Button>

<Button variant="outline" size="sm" leftSection={<IconPlus />}>
  Add Widget
</Button>

<Button variant="danger" size="sm">
  Delete
</Button>

Example: Modal

import { Modal } from "@duckviz/ui";

<Modal
  open={isOpen}
  onOpenChange={setIsOpen}
  title="Confirm Action"
  size={480}
>
  <p>Are you sure you want to proceed?</p>
  <Button onClick={() => setIsOpen(false)}>Cancel</Button>
  <Button variant="filled" onClick={handleConfirm}>Confirm</Button>
</Modal>

Example: Tabs

import { Tabs, TabsTrigger, TabsContent } from "@duckviz/ui";

<Tabs defaultValue="overview">
  <TabsTrigger value="overview">Overview</TabsTrigger>
  <TabsTrigger value="details">Details</TabsTrigger>

  <TabsContent value="overview">
    Overview content here
  </TabsContent>
  <TabsContent value="details">
    Details content here
  </TabsContent>
</Tabs>

Icons

@duckviz/ui/icons exports inline SVG icon components (no icon font dependency):

import { IconPlus, IconTrash, IconRefresh } from "@duckviz/ui/icons";

<IconPlus size={16} />

CSS custom properties

The theme system sets --app-* CSS custom properties on the root element. Key token groups:

  • --app-background-* — page and surface backgrounds
  • --app-surface-* — card and panel surfaces
  • --app-text-* — text colors (primary, secondary, subtle)
  • --app-border-* — border colors
  • --app-primary-* — brand primary color variants
  • --app-chart-* — chart color palette (10 colors)