Skip to content
UI

@zenoxai/ui

0.1.0

Minor Changes

  • e3ebca4: Data display holds its density and treats values the same way everywhere.

    • New EmptyValue (@zenoxai/ui/patterns/empty-value): a muted dash for a missing value, announced as label (“No value” by default). Use it instead of a blank cell, “N/A” or “-”.
    • Table: containerClassName on Table and sticky on TableHeader, for a long table that scrolls inside its own container with the header in view. A selected row uses bg-primary-soft (it was a 40% tint, hard to tell from hover); hover is bg-surface-muted/60; the caption is left-aligned.
    • SeverityBadge soft uses the shared soft fills and Badge’s 8px padding, so severity and status chips side by side use the same reds and ambers.
    • ConfirmDialog: the value to type shows as a monospace chip on a muted fill. Write inputField.label without the value (“Type the tenant name to confirm”).
    • SectionHeading and CardAction center the action on the title and description together.
    • Chart: axis labels take the muted text token again (the selector missed Recharts 3), and the tooltip uses the Popover surface at body size.
    • InfoBanner is one row: the icon sits on the first line and the banner is 36px with or without an icon.
    • ValueActions keeps a table row at its normal height; AvatarGroup rings use the surface color and overlap less, so initials are not cut; HelpTooltip’s pointer target is 26 or 28px; DescriptionList keeps its label column at least 7.5rem wide; Item sm is 8px tighter than default; ListErrorState’s icon uses the danger tone; the ScrollArea thumb is visible.

    To update: if an app styled its own sticky table header around Table’s scroll container, use sticky instead.

  • 6a55cc8: Form controls share one size scale, one hover step, one disabled rule and one focus ring.

    • New size prop (sm 28px, default 32px, lg 36px) on Input, InputGroup, ComboboxInput, NumberInput and NumberInputRoot; lg added to SelectTrigger and NativeSelect. Replace className="h-7" on these controls with size="sm". Input no longer accepts the native numeric size attribute; set the width with a class.
    • Breaking (0.x): InputGroupButton sizes are now the Button sizes of the same name, so xs has Button’s text-xs text and a 12px icon, icon-xs a 12px icon, and sm and icon-sm are 28px instead of 32px, so they fit inside the group’s border. In an sm group, use the xs sizes.
    • Icons follow the size name: 12px at xs, 14px at sm, 16px at default and lg. Button icon-sm now has a 14px icon, and the Select and Native Select chevrons are 14px at sm.
    • NumberInput with steppers is 32px tall, like Input (it was 34px), with Input’s padding, dark fill and focus ring; the stepper icons are 16px.
    • Hover is surface-muted in both themes on ghost and outline Buttons, Toggle and calendar days. The outline Button and ButtonGroupText use the field border (border-input), and the outline Button shows the focus border in dark.
    • Disabled controls are dimmed to 50% with no fill change, and a group dims once. A Button with aria-busy="true" is not dimmed: disable it and set aria-busy while an action runs.
    • Unchecked Checkbox and Radio borders and the off Switch track use border-control (3:1); the Slider and Progress tracks use border-strong.
    • Search inputs no longer show the browser’s own clear button: each browser drew its own. Build a clear action with an InputGroupButton, as the Input Group docs show.
    • Calendar: the selected day keeps its fill on hover in dark, today is marked by primary semibold text instead of a fill, the days inside a range use primary-soft, and the day buttons use the cell radius.
    • Combobox highlights items like Select and the menus (primary-soft), opens 4px from the field, and its chips are 20px with a border. Select and Combobox popups use the motion tokens.
    • Select and Native Select use 16px text below md, so iOS no longer zooms on focus; the Select trigger stays highlighted while open.
    • Slider thumbs show the focus ring on keyboard focus.
    • aria-invalid on InputOTP now marks every slot; drop it from the slots.
    • Smaller fixes: Filter Chip lg uses the 12.5px body text, Label has the line height of FieldLabel, ButtonGroup sets data-orientation="horizontal" by default, Textarea hides the resize grip where it grows with its content, and the fields set their text color.
  • a0cfc6a: First release of @zenoxai/ui as a standalone, versioned package.

    • 58 primitives on Base UI 1.9, merged with the latest shadcn base-nova sources while keeping every Zenox customization; new: Drawer, Field, Logo (Zenox and Tellyu marks).

    • 12 patterns, several promoted from the Vydar web app: PageHeader (now with icon and hint), StatTile, StatusBadge, SeverityBadge, CopyButton and ValueActions, DescriptionList, ConfirmDialogHost with an imperative confirm(), ListEmptyState, ListErrorState, InfoBanner, SectionHeading, HelpTooltip.

    • Token values unchanged except one: light --text-muted goes from #647585 to #5c6c7c, which reached only 4.0 to 4.4:1 on --background and --surface-muted and now passes WCAG AA on every light surface. Added --brand-mark (text-brand-mark): the brand blue in light mode, white in dark mode, the default color of the logo components. Added --severity-*-soft-foreground (text-sev-<level>-soft-fg) so the soft severity badge reaches WCAG AA. Fixed: .force-light now uses the same --input border as light mode, and alias tokens (--card, --toast-*, …) resolve per theme scope instead of freezing to light values below <html>.

    • Fonts move to the variable @fontsource-variable packages (one file per family).

    • Toaster follows the .dark class through useColorScheme() and no longer needs next-themes.

    • Chart moves from patterns/chart to components/chart and is imported from that subpath only: the root entry does not re-export it, so apps without recharts can import from the root.

    • Accessibility fixes in the promoted patterns: a clickable StatTile no longer nests the hint button inside a role="button" card; the PageHeader hint sits beside the <h1>; CopyButton announces the copy and reports a missing Clipboard API; ConfirmDialog type-to-confirm is a single-line field that confirms on Enter and shows the expected value; InfoBanner is a role="note" instead of an alert.

    • Fixes in the primitives: Alert contrast; Badge defaults to the neutral tone; Checkbox and RadioGroup disabled styles and the indeterminate dash; Pagination links are links, not buttons; named Combobox buttons; Sidebar aria-current and a shortcut that leaves text fields alone; Carousel keys for vertical and RTL; one Slider thumb per value and thumbLabels; ToggleGroup orientation; CommandDialog title placement; Toaster style merge; NumberInput aligned with Input; one menu highlight style. The table header sits on a tinted band (bg-surface-muted) with text-text-secondary labels, so it reads apart from the rows. The full list is in docs/audits/2026-10-23-web-app-ui-audit.md.

    Migrating from the workspace copy in zenox-web-app: see docs/migration/from-zenox-web-app.md.

  • 855f68d: One look for selected and pressed items, toasts in the colors of their type, and dark-theme tokens that keep states visible.

    • Selection colors: a pressed Toggle and ToggleGroup item, the active FilterChip, the current sidebar item and the highlighted item of every menu use bg-primary-soft with text-primary-soft-foreground. text-primary on that fill was 4.0:1 in dark.
    • ToggleGroup: the segmented variant is as tall as a Button of the same size (28, 32 or 36px, track included), with concentric radii, and keeps one item pressed when it is not multiple. Joined outline items overlap their borders, so a pressed item shows its whole primary border.
    • Toaster: toast.success(), info(), warning() and error() take the colors of the Alert of the same tone by default (richColors now defaults to true; pass richColors={false} to keep every toast neutral). Toast text uses the type scale and the text tokens. New --toast-warning-* tokens.
    • Theme, dark: --surface-muted is #26262f (hover, header bands, skeletons and tracks were 1.08:1 against the surface), --text-muted is #8b95a1, --input is --border-strong and --success-soft is #052e16. Light: --chart-5 is #4796cb (the brand accent was 1.4:1 on white).
    • New tokens: --border-control (border-border-control), the 3:1 outline of a checkbox or a radio, and --severity-{critical,high,medium,low,info}-soft (bg-sev-*-soft).

    To update: if you passed richColors to every toast() call, you can drop it.

  • 709bf6b: StatTile keeps its figure neutral and gains a standard delta, units, exact values and a no-data state.

    • The figure is semibold (it was bold) and never colored. tone="warning" and tone="danger" now color the meta line, the reason the figure needs attention, and add the tone’s icon. Write meta as that reason (“Above the target of 10”); a toned tile without meta shows the icon beside the label.
    • New delta prop and StatDelta component: the change against a previous period on a soft chip with an arrow. trend sets the arrow, sentiment (positive, negative, neutral) sets the color, label is what screen readers announce.
    • New unit prop: the unit sits after the figure, smaller and in secondary text, and is read with it (“Patch coverage: 92%”).
    • New exactValue prop: the exact figure behind an abbreviation, on hover and for screen readers.
    • value accepts null: the tile shows an empty value instead of a figure.
    • A clickable tile’s hover is a neutral border and fill instead of a primary-tinted border, and its keyboard focus ring is drawn on the card (it was clipped by the card’s overflow).
    • A loading tile keeps the height of a loaded one: the meta line gets its own skeleton.

    To update: replace hand-built deltas in trailing with delta, choosing sentiment per metric. Pass null instead of 0 when the figure is missing. Check the meta of every toned tile reads as the reason for the tone.

  • 018db60: The type scale moves to rem, raises its floor to 11px and gains a compact title step (ADR 0006).

    • Sizes and line heights are in rem. At the browser’s default 16px nothing moves; when a user raises the browser’s font size, text now grows together with spacing and control heights.
    • text-xs is 11px on a 16px line (it was 10px on 14px). Badges, keyboard hints, menu labels, shortcuts, chart text and xs buttons grow by 1px. Badge keeps its 20px height.
    • New text-md (14px / 20px), the compact title step. cn() knows it, so it is not dropped when merged with a text color.
    • Title weights follow the role: page and section headings stay semibold, container titles (Card, Dialog, Alert Dialog, Sheet, Drawer, Empty) stay medium, and compact titles are semibold. PopoverTitle and the title of a size="sm" Card move from 12.5px medium to 14px semibold. CardTitle drops leading-snug, so a default card title sits on a 24px line.
    • text-dense-xs is now a deprecated alias of text-xs.
    • Select triggers and sidebar menu buttons lose vertical padding inside their fixed heights, so the text line always fits the box.

    To update: replace any text-xs used for a sentence, help text or a code block with text-sm; the guidelines no longer allow it. Replace text-dense-xs with text-xs. A font-semibold override on CardTitle or DialogTitle goes against the title levels; remove it.

Patch Changes

  • 51f3289: The documentation moves to https://ui.zenox.services: the links in ai/USAGE.md and in the generated component registry (ai/components.json, ai/components.md) point there. The registry also follows DOCS_SITE_URL when it is set, like the docs build.

  • d7594e0: Navigation, tabs and disclosure components follow the selection, focus and motion rules of the rest of the system.

    • Tabs: the default variant is the segmented ToggleGroup control. The list is a bordered bg-surface track with a 2px inset, the triggers fill it (26px in a 32px track, 3px above and below), and the active tab takes bg-primary-soft, text-primary-soft-foreground and a primary inset ring instead of a raised pill with a shadow. Hover is bg-surface-muted, and inactive text is text-text-secondary in both themes. The line variant keeps its primary text and underline, and now scrolls sideways when the tabs do not fit; its bottom rule is an inset shadow instead of border-b, so the underline is no longer clipped.
    • Sidebar: hover and press use the neutral bg-surface-muted; the selection tint marks the current page only, and keeps it on hover. Focus uses the system 3px ring. size="sm" menu buttons and both sizes of SidebarMenuSubButton use the 12.5px body text (heights unchanged); sub-button icons follow the text color. SidebarTrigger and SidebarRail are named “Toggle sidebar”.
    • Pagination: the current page takes the selection colors with a primary border instead of the outline button.
    • Breadcrumb links show the system focus ring; the ellipsis is one line tall with a 14px icon, so the trail keeps its height.
    • AccordionTrigger extends 8px past its item on each side, with matching padding, so the focus ring clears the text while the text stays aligned with the panel.
    • CollapsibleContent animates its height with the AccordionContent keyframes instead of snapping.
    • ResizableHandle: the line tints primary on hover and turns solid primary while dragged, the grip uses border-control (3:1 on the surface), the focus ring is the system 3px ring, and the pointer strip is 8px wide.
    • Carousel: below md, CarouselPrevious and CarouselNext sit 8px inside the edges instead of off screen. From md up they stay 48px outside.
    • Skeleton uses border-strong in dark, where surface-muted faded into the surface at the low point of the pulse.
    • AlertAction is centered on the first line of text at any button height. AlertDescription and EmptyDescription use text-text-secondary; EmptyDescription drops its relaxed line height and EmptyTitle its negative tracking.

    To update: tests that find the sidebar trigger by the name “Toggle Sidebar” must use “Toggle sidebar”. An Accordion placed flush against the edge of a scrolling or clipping container with less than 8px of padding needs that padding, or its triggers overflow by 8px. A Carousel whose buttons you positioned with your own left-* or right-* classes keeps them from md up; below md add max-md: classes if you want a different placement.

  • e0713e5: Overlays and menus now share one look, one motion and one row geometry.

    • Dialog, AlertDialog, Sheet and Drawer: a 1px ring and shadow-lg (Sheet and Drawer drop their inner-edge border), a 4px header gap, a 24px title line (Dialog loses leading-none) and text-text-secondary descriptions. AlertDialog default is 400px wide from sm, like a sm Dialog.
    • Dialog and AlertDialog stop at the viewport height less 1rem on each side and scroll inside, so the footer stays reachable on short screens.
    • The Dialog and Sheet close button sits at the same offset (14px from the top, 12px from the right), centered on the first title line, and the header reserves room for it: long titles wrap before the X instead of under it.
    • Sheet enters in 200ms and exits in 120ms with the theme easings, and its backdrop uses the Dialog one. HoverCard, ContextMenu, Menubar and the NavigationMenu popup use the same fade-and-scale transition as Popover and DropdownMenu instead of keyframe animations. Submenus and the NavigationMenu popup use shadow-md like every floating panel.
    • Menu rows line up: DropdownMenuItem moves from 8px to 6px side padding, like its checkbox, radio and sub-trigger items. Menubar checkbox and radio items put the check on the right, as in DropdownMenu and ContextMenu, and MenubarLabel uses the same caption style. Every menu popup is at least 144px wide. All item types highlight on data-highlighted.
    • Command: the highlighted item uses the selection colors (bg-primary-soft, text-primary-soft-foreground, icons and shortcut included) instead of a near-invisible bg-muted. Rows are 26px with a 4.8px radius and 6px padding, like menu rows, in a popover and in CommandDialog alike; group headings use the menu label inset. The Command root is rounded-lg and can be overridden with className.
    • MenubarTrigger shows the focus ring. NavigationMenu triggers and links are 32px high with a 16px chevron; the active link takes the selection colors and keeps them on hover; an open trigger uses the full hover fill.
    • HoverCard is centered on its trigger (alignOffset defaults to 0).
    • Drawer: the header aligns left from md (it stayed centered on wide screens), and a footer right after the header keeps a 16px gap without a pt-4 patch.
    • CommandDialog’s default title and description are now “Command palette” and “Search for a command to run”. If you relied on the old defaults, for example in a test that queries the dialog by name, pass them explicitly.

    To update: remove any className="pt-4" added to a DrawerFooter that follows a DrawerHeader, and any inset added to Menubar labels or items only to line them up with checkbox items.

Release notes are generated by Changesets from the files in .changeset/. Do not edit this file by hand.