1

Changelog

New features, improvements, and fixes.

Separator tone and spacing

The separator between list items is lighter, menu separators get more room, and the sidebar separator no longer overflows.

v1.8.6

  • ItemSeparator now adds opacity-50 on top of bg-border. Separator itself and the menu separators are unchanged.
  • DropdownMenuSeparator, ContextMenuSeparator, MenubarSeparator, SelectSeparator and ComboboxSeparator go from 4px to 6px of margin-block.
  • SidebarSeparator goes from 8px to 12px of margin-inline.
  • SidebarSeparator inherited data-horizontal:w-full and overflowed the sidebar by its margin-inline. It now sets data-horizontal:w-auto.

Avatar xl size

Avatar gains an xl size and scales its overlap, fallback text and badge per size.

v1.8.6

  • Avatar takes xl alongside sm, default and lg. Diameter is 24 / 32 / 40 / 48px.
  • AvatarGroup overlap is now set per size — -6 / -8 / -10 / -12px — instead of a shared -8px. One shared value hid a third of a small avatar and a sixth of a large one.
  • AvatarFallback font size follows the diameter: 12 / 14 / 16 / 18px. lg previously matched default.
  • AvatarBadge is 8 / 10 / 12 / 12px.

cn package imports

Components now import cn directly from the cn package.

v1.8.5

  • Ondo UI components, demos, documentation site, and Design Inspector now import cn directly from the cn package.
  • Every registry item that uses cn declares it as an npm dependency, so shadcn add @ondo-ui/<component> installs it automatically.
  • Remove the @ondo-ui/utils registry item and the local lib/utils.ts helpers. Replace any local @/lib/utils imports with import { cn } from "cn".

Info colour

The info token moves from sky to the primary blue.

v1.8.4

  • --info is now blue-500, the same hue as --primary, in both the light and the dark theme. It was sky-500.
  • Every info surface follows: Badge, Button, Alert, Toast, Progress, Meter, Progress Ring, Meter Ring, Stepper, Timeline and Paragraph.
  • Alert keeps both a primary and an info variant. They now render the same hue.

Badge sizes

Badge gains a size scale and a retuned height, padding and radius.

v1.8.3

  • Badge takes a size prop with sm, default and lg. Height is 18 / 22 / 26px, horizontal padding 4 / 6 / 8px, font size 12 / 12 / 14px.
  • Corner radius is now set per size — 6 / 8 / 10px — instead of one shared pill radius. A single radius read as a pill at the small end and as a square at the large end.

Field spacing

Retune the Field spacing scale and move the option row overrides into the component.

v1.8.1

  • Tighten the gap between a label and its control to 8px, whether the field is stacked or laid out as a row, and the gap between two fields in a group to 20px. A group legend now sits 14px above its content.
  • Inset a stacked FieldLabel and FieldDescription by 3px so their text lines up with the text inside the control rather than sitting outside its rounded corner. Row fields stay flush with their checkbox.
  • Give a FieldLabel that directly follows its own checkbox or radio the body weight automatically. The className="font-normal" written at 25 call sites is no longer needed.
  • Replace FieldGroup's data-[slot=checkbox-group] rule, which never matched, with one that tightens the gap to 12px when every child is a row field. className="gap-3" and the data-slot="checkbox-group" spoof are no longer needed.
  • Document the whole spacing scale under Anatomy in both locales.

Paragraph

Add a Paragraph component whose body scale pairs one-to-one with the Heading scale.

v1.8.0

  • Add the Paragraph registry component: a paragraph styled from a six-step body scale.
  • Number the body scale to match Heading, so passing the same size to both keeps a title and its copy in proportion.
  • Support semantic variant colours, wrap, and the render prop for a different element.
  • Provide English and Korean documentation covering installation, usage, and every example.

Date Picker

Add a Date Picker composition with calendar, range, time, natural language, and RTL examples.

v1.7.0

  • Add the Date Picker registry composition built from the existing Button, Calendar, Field, Input, Input Group, and Popover primitives.
  • Include all 8 Base UI examples, covering basic and range selection, date of birth, text input, time, natural language parsing, and RTL layouts.
  • Provide complete English and Korean documentation for installation, usage, and every example.

Questionnaire

Add a multi-step questionnaire for fixed, freeform, multiple, and skippable answers.

v1.6.0

  • Add Questionnaire, a multi-step form composition built on the headless @shadcn/react primitive.
  • Support single and multiple selection, freeform answers, explicit skipping, required and external validation, controlled navigation, saved defaults, conditional items, progress state, and letter or number shortcuts.
  • Include all 14 upstream examples with Card and Dialog composition, animation, responsive layouts, and complete English and Korean documentation.

Design Inspector

Add a development-only inspector package for auditing rendered Ondo components.

v1.5.0

  • Add the @dou.so/design-inspector npm package: a development-only, same-origin overlay that audits mounted Components and Compositions across mobile, tablet, FHD, and QHD viewports, with component layers, comparison mode, keyboard shortcuts, page navigation, and DesktopWindow screenshots. It is imported from the package — with a precompiled, inspector-scoped stylesheet — rather than installed from the registry.
  • Delegate theming to the host application: the inspector resolves System through prefers-color-scheme, scopes the scheme to its own overlay, and exposes onThemeChange and applyPreviewTheme for applications whose theme mechanism differs from a light/dark class.
  • Add root data-variant markers to Button and Frame, and root data-slot markers to LiveWaveform, NumberCount, EmptyView, and NumberBadge so the inspector can identify instances and their presentation props.

Ondo CLI Command Surface

Add interactive registry installation and the full shadcn command surface.

v1.4.0

  • Add an interactive add menu for Ondo Components and Compositions.
  • Add direct item installation with add <name> and bulk installation with add --all.
  • Add Ondo-aware support for the shadcn commands used to search, view, apply, migrate, eject, configure MCP, and manage registries.
  • Add CLI documentation for project setup, registry installation, and command usage.

Framework Installation CLI

Add framework-aware project setup with Ondo registry configuration.

v1.3.1

  • Add the scoped @dou.so/ondo-ui CLI for framework-aware project setup.
  • Add init commands for Next.js, Vite, TanStack Start, React Router, Laravel, and Astro.
  • Automatically install the framework-compatible Ondo theme assets.
  • Register the @ondo-ui registry in the generated components.json file.
  • Add framework-specific installation guides for new and existing projects.

Stepper

A new Stepper component for accessible multi-step processes.

v1.2.0

Stepper adds a composable way to build multi-step processes while keeping navigation, state, and content synchronized:

  • Use controlled or uncontrolled selection with horizontal or vertical layouts.
  • Show active, completed, inactive, disabled, and loading states with optional custom indicators.
  • Compose titles, descriptions, separators, and panels without locking the UI to one presentation.
  • Navigate steps with arrow keys, Home, End, Enter, and Space through linked tab and panel semantics.
  • Keep inactive content mounted with forceMount when preserving local state is necessary.
  • Set active and completed colors with the semantic variant values default, info, success, warning, and destructive; default maps to the primary color.
  • Use activeVariant when the active step needs a different semantic color from completed steps.
  • Start from fourteen examples covering progress, status, inline, and vertical arrangements.

Timeline, Frame, and the meter family

Five components for reporting history and bounded values — plus a shared color scale across every progress and meter indicator.

v1.1.0

All five come from the same gap. ondo had good primitives for inputs and lists, but nothing purpose-built for reporting a history or a bounded value, so products kept hand-rolling the same shapes on top of Card and raw SVG.

Timeline shows events in chronological order, vertically or horizontally. Each item declares its step, and the timeline's value marks how far the sequence has advanced — items at or below it render as completed, and the rail fills to match.

<Timeline>
  <TimelineItem step={1}>
    <TimelineHeader>
      <TimelineDate>March 2024</TimelineDate>
      <TimelineTitle>Project Initialized</TimelineTitle>
    </TimelineHeader>
    <TimelineIndicator />
    <TimelineSeparator />
    <TimelineContent>
      Successfully set up the project repository and initial architecture.
    </TimelineContent>
  </TimelineItem>
</Timeline>

The indicator and the separator are separate parts, which is what makes the alternating, horizontal, and custom-indicator layouts in the docs possible without fighting the component.

The indicator itself takes variant for the color, fill for whether it draws as a ring or a disc, and animate for ping or pulse on the step that is currently running; that last one respects prefers-reduced-motion. size on the root scales the dot, its icon, and the whole rail together, because the item's margin and the separator's length are derived from the indicator size rather than hard-coded.

<Timeline value={2} size="lg">
  <TimelineItem step={2}>
    <TimelineIndicator variant="warning" fill="solid" animate="ping" />
    <TimelineSeparator />
  </TimelineItem>
</Timeline>

MeterRing is a radial reading of a bounded value, built on Base UI Meter, so it is announced correctly. The arc is a conic gradient masked into a ring — two elements, no SVG — and it takes currentColor, so variant recolors it.

<MeterRing value={0.68} max={1} format={{ style: "percent" }} variant="success">
  <MeterRingLabel>Coverage</MeterRingLabel>
</MeterRing>

formatValue replaces the centered text when a number format cannot say what is needed — an em-dash for "no reading yet" beats a ring confidently displaying 0.00.

Meter and ProgressRing fill in the rest of the grid. There are two questions to answer — is this a task finishing, or a value that simply is what it is? and bar or ring? — and now every combination has a component, so the name you reach for decides the ARIA role.

LinearRadial
Task completionProgressProgressRing
Bounded readingMeterMeterRing

ProgressRing is the genuinely new one here: because it is built on Base UI Progress, value={null} marks it indeterminate, and the arc becomes a spinner instead of claiming a number it does not have.

<ProgressRing value={null}>
  <ProgressRingLabel>Connecting</ProgressRingLabel>
</ProgressRing>

All four now share one color scale. variantdefault, info, success, warning, destructive — sets a text color on the root and the fill paints with currentColor, so a className="text-…" override works everywhere too. Progress gained this prop in the process; it previously hardcoded its fill.

Frame is the surface the Timeline's horizontal example sits on: a bordered container that nests panels inside it. The panel radius derives from the frame's, so the corners stay concentric instead of doubling up, and --frame-radius is the single knob for both.

<Frame stacked>
  <FramePanel>
    <FrameHeader>
      <FrameTitle>Deployment</FrameTitle>
    </FrameHeader>
  </FramePanel>
  <FramePanel>Second panel, joined to the first.</FramePanel>
</Frame>

Toast replaces Sonner

A Base UI toast component takes over from the Sonner wrapper.

v1.0.0

Toast is a new component built on Base UI Toast, and it replaces Sonner. Upstream shadcn made the same move on its Base UI variant: sonner is gone from the docs and toast took its place.

import { toast } from "@/components/ui/toast"
 
toast.add({
  title: "Event created",
  description: "Sunday, December 3 at 9:00 AM",
})

Mount Toaster once near the root of your app. It bundles the provider, portal, viewport, and default toast list, and toast is a standalone manager — the caller does not need to be inside the provider.

The whole surface is exported for custom layouts: Toast, ToastAction, ToastClose, ToastContent, ToastDescription, ToastPortal, ToastProvider, ToastTitle, ToastViewport, plus createToastManager and useToastManager.

This is a breaking change. Sonner is removed from the registry, and the sonner package is no longer a dependency. The APIs are not compatible:

- toast("Event created", { description: "…" })
+ toast.add({ title: "Event created", description: "…" })
 
- toast.success("Saved")
+ toast.add({ type: "success", title: "Saved" })

Status icons still cover success, info, warning, error, and loading, using the same icon set and semantic colors Sonner used, so the look carries over.

xl and 2xl sizes across the scale

Button, Input, Toggle, Select, and NativeSelect gain xl (44px) and 2xl (52px) steps.

v0.6.0

Button, Input, Toggle, Select, and NativeSelect now accept size="xl" and size="2xl". Button also gains icon-xl and icon-2xl.

sizeheighttexticonradius
xl44pxtext-base20pxrounded-lg
2xl52pxtext-lg24pxrounded-xl
<Button size="xl">Extra large</Button>
<Button size="2xl">2X large</Button>
<Button size="icon-2xl" aria-label="Add">
  <IconPlus />
</Button>
<Input size="2xl" placeholder="2X large" />

Select and NativeSelect accepted only sm and default, so they gain lg in the same release rather than skipping a step. Textarea and InputGroup gain the full scale too — a textarea matches an input of the same size on horizontal padding, text, and radius, and an input group passes its size down to the control inside.

Unlike the smaller sizes, xl and 2xl keep their text size at every breakpoint instead of dropping to text-sm on desktop. xl sits only 4px above lg, and the text size is what tells them apart.

These are the first sizes with a corner radius above rounded-md. ButtonGroup and ToggleGroup round their end caps to md, which would have squared off one side of an xl or 2xl child; those rules now key off the child's own data-size, so each child keeps its radius and a group mixing sizes still resolves per child.

InputGroup, Combobox, Select, and NativeSelect also gain xs, so the scale now runs xs through 2xl everywhere Input does. An xs input group shrinks its addon padding to leave room for a control.

InputGroupButton now takes every Button size, with each name meaning exactly what it means there. Omit size and the button fits itself to the group instead.

InputGroupButton's four named sizes are unchanged and still match upstream shadcn exactly. Omit size and the button now fits itself to the group instead; in a default group that is identical to xs, so nothing moves.

The default size is unchanged, so this is a non-breaking update.

Desktop Window

Static macOS, Windows 11, and Ubuntu window chrome for screenshots, heroes, and product mockups.

v0.5.0

DesktopWindow wraps any content in desktop window chrome:

  • Three platforms, one markup. os="macos", os="windows", and os="ubuntu" share the same children. The prop flips CSS rather than the DOM, so switching platforms is a one-word change — the controls and the title move on their own. "ubuntu" renders the Yaru/GNOME headerbar Ubuntu ships by default; other Linux desktops draw windows differently and are out of scope.
  • Chrome is fixed, content is yours. Titlebar backgrounds, traffic light colors, corner radii, and the Windows close-button red are pinned to the real platform values in oklch, so the frame reads as that OS in any project. Only DesktopWindowContent follows your theme.
  • Decorative by default. Controls render as aria-hidden spans and stay out of the tab order. Pass onClose, onMinimize, or onMaximize and that one control becomes a labeled button with a focus ring.
  • Platform-accurate detail. macOS reveals its glyphs on titlebar hover or focus; Windows 11 and Ubuntu show them always. Ubuntu's close button is a gray circle like its neighbours — the red close button is a Windows trait. No new CSS variables, and the only dependency is class-variance-authority.

It is chrome, not a window manager: no dragging, resizing, or minimise and maximise state. Toolbars, app icons, and sidebars go in the titlebar as children.

Live Waveform

A canvas-based real-time audio waveform visualizer with microphone input, static and scrolling modes, and a processing state.

v0.4.0

LiveWaveform visualizes audio in real time on a canvas, for voice and multimodal UIs:

  • Two modes. static paints symmetric bars across the frequency bands; scrolling shows historical average volume moving right to left like a timeline.
  • Processing state. processing animates a wave while you wait for input — and settles to a calm static frame under prefers-reduced-motion.
  • Bring your own stream. Pass a stream to visualize audio you already have (a shared mic track, or an agent's response audio); otherwise it requests the microphone itself and cleans the stream up on unmount.
  • The render loop parks itself when idle, callbacks are read from refs so inline handlers don't re-acquire the microphone, and the bars default to the primary color — className's text-* or barColor override it.

Adapted from ElevenLabs UI's live-waveform to Base UI-era ondo conventions.

Heading

A new Heading component that keeps a heading's semantic level and its visual size independent.

v0.3.0

Heading renders an h1h6 for application UI, keeping the document outline and the visual scale apart:

  • level sets the rendered tag and, on its own, the matching size.
  • size overrides that scale independently, so an h2 can carry h1 weight when the design asks for it while the outline stays correct. Sizes 16 are a single bold ramp from 2.25rem down to 1.125rem, sized in rem with unitless line-height ratios and em tracking, composed from utilities with no theme token behind them.
  • wrap maps to the Tailwind text-wrap utilities (normal, nowrap, balance, pretty) to control how a heading breaks across lines.
  • render swaps the element while keeping the styles, for cases like a legend inside a fieldset.

Rendered markdown and MDX already get their heading styles from the typeset layer, so this component is meant for application UI rather than prose.

Semantic color variants

info, success, and warning join destructive as first-class variants across Button, Badge, Alert, and Sonner.

v0.2.1

Semantic colors beyond destructive. The info, success, and warning tokens now surface as first-class variants across the registry:

  • Button, Badge, and Alert gain info, success, and warning variants — the same soft-tint recipe as destructive.
  • Alert variants now fill their background and border with the semantic tint, plus a new primary variant.
  • Toast messages (toast.success / .info / .warning / .error) are themed with the semantic tokens.

Animated number counters

NumberCount and NumberBadge bring rolling-digit counts to avatars, icons, and stats.

v0.2.0

Two new items built on @number-flow/react:

  • NumberCount — a new component: an animated number whose digits roll between values. A thin wrapper over NumberFlow with ondo defaults; every NumberFlow prop (format, prefix, suffix, spinTiming, trend, …) passes straight through.
  • NumberBadge — a new composition: a self-anchoring count badge for avatars and icons. Wraps its children and overlays an animated count with 99+ overflow, hide-on-zero, and placement.

Both install their @number-flow/react dependency automatically.

Input gets size variants

Input now matches Button's xs/sm/default/lg size scale.

v0.1.1

Input now accepts a size prop — xs, sm, default, and lg — matching the height, padding, and radius scale already used by Button. Pair a compact Input with a Button size="sm" (or any other size) and they'll line up.

<Input size="xs" placeholder="Extra small" />
<Input size="sm" placeholder="Small" />
<Input size="default" placeholder="Default" />
<Input size="lg" placeholder="Large" />

The default size is unchanged, so this is a non-breaking update.

Introducing Compositions

A new layer of ready-made components composed from ondo-ui primitives.

v0.1.0

We've added a new Compositions layer to ondo-ui — the space between components and blocks.

  • Components are the low-level primitives you compose yourself.
  • Compositions are ready-made components that combine primitives behind a focused props API.
  • Blocks are full page sections (coming soon).

EmptyView

The first composition is EmptyView — a ready-made empty state composed from Empty and Button. Pass your content and go:

bunx --bun shadcn@latest add @ondo-ui/empty-view

Browse compositions on the new Compositions page, now in the top nav and the docs sidebar.