Changelog
Changelog
Unreleased
Changed
- The showcase examples index is one flat page. Every example card shows a schematic preview image from
static/examples/, and the/examples/landing,/examples/spa, and/examples/aicollection pages are removed.
0.21.1
Fixed
PromptBarnow measures multiline wrapping against the compact input width and measures again after container resizes, which prevents layout oscillation.- An open
PromptBarpolicy list now closes and blocks value changes when the composer becomes busy or disabled. - The
PromptBarpolicy trigger now supports closed typeahead, and keyboard highlighting scrolls inside long policy lists.
0.21.0
Breaking
PromptBarreplaces the native execution-policy<select>with a delegated select trigger.- Optional attachment, dictation, policy, and measurement controls now render only when the consumer wires their data or callbacks.
PromptBarreplaces its two-row footer with automatic compact and expanded layouts.
Added
PromptBar.shapeselects the rounded or pill surface shape.PromptBaranimates layout and surface-radius changes and disables this motion when the user prefers reduced motion.
0.20.0
Added
- The provider-neutral
@binarydreams/uikit/aientrypoint. It exportsActivityStatus,AgentTrace,AgentResponse,ApprovalRequest,ToolCallList,AgentTaskList,Conversation,Message,PromptBar,Recommendation,ContextList,ChangeReview, andSelectionActionswith their public types. LiveRegion, a visually hidden ARIA live region for consumer-controlled announcements.ScrubInput, a native numeric field with optional pointer dragging on its visible label, bounds, steps, suffixes, formatting, and controlled values.
Changed
- Component API extraction and registry coverage now distinguish the package root from
./ai. AI components do not export from@binarydreams/uikit. Textareanow uses a real label, stable hint and error IDs, mergedaria-describedby,aria-invalid, alert errors, and a bindable native element reference.Checkboxnow uses the sharedAccessibleNamecontract for a visible label,aria-label, oraria-labelledby.Collapsibleaddsvariant="plain". The existing bordered panel remains the default.Tableadds captions, card or scroll responsive modes, and controlled row selection with stable row identity and localized labels.Toolbaractions now select icon-only, label-only, or combined presentation and a semantic tone. Toggle selection remains controlled by each group.
0.19.0
Added
- Three named
BDThemeOverridesobjects:BD_PRESET_GRAPHITE_DARK,BD_PRESET_BLUEPRINT, andBD_PRESET_BLUEPRINT_DARK. Together with the Graphite Light base, they form the supported 2x2 Graphite/Blueprint and Light/Dark model. Consumers pass these objects toProvider.overrides. - Optional override axes for font roles, type metrics, control geometry, deep radius profiles, stroke, effects and focus, motion, and grid patterns.
- Semantic variables for component layers, control and table recipes, panel and item radii, stroke, status foregrounds, control thumbs, and themed patterns.
- The
--bd-font-proserole for authored prose. It falls back to--bd-font-textwhen an override does not supply a separate stack.
Changed
- Graphite Light is now the bare-
Providerbase and default. It uses zinc layers, dark neutral text, compact geometry, ledger radii, shadowless outer elevation, and interrupted execution traces. - Graphite Dark keeps the compact Graphite geometry on near-black layers. Blueprint Light and Blueprint Dark use roomier rounded geometry and technical-drafting palettes.
- The showcase theme controls now select Graphite or Blueprint independently from Light, Dark, or system mode.
- Controls now consume semantic recipe variables for type, geometry, layers, radii, stroke, focus, motion, and patterns. Components do not branch by theme ID.
BD_UTILITY_CSSnow reads generated type and prose variables. Regenerate any hand-written:rootvariable block withbdBuildCssVars().
Removed
- The previous style preset exports. The public preset surface now contains only the three objects listed above.
0.18.0
This breaking release consolidates public components around semantic contracts. No deprecated aliases remain.
Breaking
Meteris removed. Replace<Meter ... />with<Progress kind="measurement" ... />.MeterPropsis removed. Replace it withProgressMeasurementProps.MeterToneis removed. Replace it withProgressTone.- Existing task progress now requires
kind. Replace<Progress ... />with<Progress kind="task" ... />. AlertDialogis removed. Replace it with<Modal kind="confirmation" ... />.AlertDialogConfirmVariantis removed. Replace it withModalConfirmVariant.- Existing general modals now require
kind. Replace<Modal ... />with<Modal kind="dialog" ... />. DateFieldis removed. Replace it with<DateTimeField mode="date" ... />.DatePickeris removed. Replace it with<DateTimeField mode="date-picker" ... />.DateRangeFieldis removed. Replace it with<DateTimeField mode="date-range" ... />.DateRangePickeris removed. Replace it with<DateTimeField mode="date-range-picker" ... />.TimeFieldis removed. Replace it with<DateTimeField mode="time" ... />.TimeRangeFieldis removed. Replace it with<DateTimeField mode="time-range" ... />.ScrollAreais removed. Replace it with native overflow on the owning element.Stackis removed. Replace it with local flex or grid composition and--bd-*spacing tokens.Subsectionis removed. Replace it with semantic HTML, local headings, and local grid composition.Insetis removed. Replace it with padding on the content owner and--bd-*spacing tokens.AspectRatiois removed. Replace it with native CSSaspect-ratioon the media owner.Stageis removed from the package. Replace it with the showcase-onlyStagefrom$showcasefor documentation specimens.StackDirection,StackGap,StackAlign, andStackJustifyare removed withStack. Replace them with local CSS values.StageVariantandStageInsetare removed from the package. Replace them with the showcaseStagecontract.
Changed
DateRange,RangePreset,TimeRange,DEFAULT_PRESETS, andformatRangeDefaultnow export fromDateTimeField.NavigationMenu.openModeaccepts"hover"or"click". The default is"hover"; touch activation uses clicks in both modes.ContextMenu.triggeris required and delegates props to one consumer-owned target element. The component no longer adds a trigger wrapper.- The documentation section order is now example, accessibility, guidance, API, related content, and source. Source is direct and last.
- Component API tables, generated Markdown, and LLM feeds now show optional prop defaults.
Fixed
- Placeholder removes decorative guide lines. StatusDot renders two reduced-motion-safe pulse waves. Alert uses the input radius. Skeleton uses a 2.8 s shimmer. Table uses the surface background and accent-tint hover and focus treatment for data rows.
0.17.2
Removed
src/content/generated/and its generators. The shiki theme is derived from the palette at load time (src/showcase/code/shiki-theme.ts), prerender entries come straight from the registries (src/content/prerender.ts), and the component API manifest is served as thevirtual:bd-component-apimodule extracted from the library source during dev/build.docs:api,docs:api:check, and thedocs:prerender:*scripts are gone.- The standalone
scripts/docs/validate-registry.mjsandscripts/docs/validate-content.mjsvalidators and theirdocs:registry:check/docs:content:check/docs:checkscripts. Their real-data checks now run inside thenode:testsuite (npm test).
Changed
- Chip
lgsize now matches the 44 px Input height. The compactmdsize remains the default for filter groups. - Button accepts the
context: ButtonContextprop. Product is the default context. - Button supports the
roundedshape. Product Buttons now default to the rounded 12 px shape. context="marketing"opts into the pill shape and primary-button glow.- Button size metrics remain 36 px (
sm), 44 px (md), and 52 px (lg). - Building and tooling require Node 26 and npm 11 (
engines,.nvmrc, andpackageManagerinpackage.json).svelte.config.jscontinues to import the registries and tokens as TypeScript natively. - The Yarn v1 lockfile was replaced with npm’s
package-lock.json; npm now owns dependency installation and every repository command. - Runtime and development dependencies were refreshed to their latest compatible releases.
- Public peer minimums are now
svelte ^5.56.7and@internationalized/date ^3.12.2. - TypeScript remains on 6.0.3 until the Svelte toolchain supports TypeScript 7.
0.17.0
This release stops the package from forcing the entire Phosphor set into consumer bundles, and brings the showcase up to the standard the design system documents.
Breaking
Icontakes a glyph component instead of a name. Replacename="House"withglyph={HouseIcon}and import the glyph yourself:import HouseIcon from 'phosphor-svelte/lib/HouseIcon';. The suffixed export is the correct one — unsuffixed aliases are deprecated in Phosphor v3.IconNameis removed. The generated name union is gone, and with it the namespace import that forced the whole Phosphor set into every bundle.IconGlyph(and the unchangedIconWeight) are exported from the package root in its place; a bundler now keeps only the glyphs an application imports.- Every icon-bearing prop takes a glyph component.
Button,Chip,CopyButton,Input,RatingGroup,Tabs,AppShell,Breadcrumb,Toolbar,CommandPalette,Menu,NavigationMenu,Alert,Avatar,EmptyState, andToastcarryIconGlyphwhere they carried a name;Tooltip.triggerIconfollows. Pass the imported component where a string used to go. - Icon props no longer accept a snippet; use the paired content prop. A glyph and a snippet are both functions at runtime, so one prop cannot discriminate between them the way
IconName | Snippetdid.ButtongainsiconContentandtrailingContent,InputgainsleadingContentandtrailingContent, andAlertgainsiconContent; each takes precedence over its glyph prop.Alert.iconis nowIconGlyph | boolean—truestill selects the tone’s canonical glyph.
Added
- Component documentation pages show the demo source. Each page renders the full source of the demo beside it, lazily loaded, syntax-highlighted, and copyable. The source is read from the very file that renders the demo, so the two cannot drift.
- Every foundation has its own page. Brand, colors, typography, spacing, radius, elevation, gradients, grid backgrounds, and motion are separate registry-driven routes with an
en/rudocument and an optional live demo, replacing the single page that carried all nine.
Changed
- Code samples are highlighted against a theme generated from the palette.
yarn docs:apiderives a full Shiki theme fromBD_TOKENS, replacingcreateCssVariablesTheme, whose fixed ~11-token vocabulary left Svelte markup, attributes, and most identifiers uncoloured. A representative example source renders 10 distinct colours where it previously rendered 5. - Phosphor glyphs are imported deeply throughout.
sveltePhosphorOptimize()only rewrites named barrel imports inside Vite, so tooling outside it compiled all ~1500 glyph modules. The library ships deep imports, anddist/contains no barrel import at all. - Example screens are composed from kit primitives. The ten screens are rebuilt on
Stack,Card,Section,Inset,Divider, andSegmentedunder three rules: a state is expressed once, status colour marks a boundary or a control rather than a filled region, and one region has one rhythm owner. - Static builds address assets from the deployment base.
paths.relativeis pinned tofalse, so a host that answers unknown paths withindex.htmlstill serves a styled page at any route depth. - Every
yarn buildemits Storybook underbuild/storybook/. It previously built only inyarn build:release, so deployments that ran the plain build served no/storybook/.
Removed
FoundationsCollectionandFoundationGuidance— replaced by the per-foundation routes.- The
--bd-shiki-*custom properties — the generated theme carries its own colours. - 672 orphaned message keys, left behind by the documentation and examples rebuild. Both locales stay in exact parity at 528 keys.
0.16.0
This release hardens public component contracts, accessibility, SSR output, and the documentation and release surfaces. It is not source-compatible with every 0.15.x consumer; apply the migrations below before upgrading.
Breaking
AppShellnow requires one localizedlabels: AppShellLabelsobject. ReplacenavLabel,tabsLabel,openDrawerLabel, andcloseDrawerLabelwithprimaryNavigation,mobileNavigation,openNavigation,closeNavigation, andskipToContentinsidelabels.MeterandPinInputrequire an accessible name. Supply exactly one of a visiblelabel, nativearia-label, or nativearia-labelledby; ambiguous and unnamed combinations are rejected by their public prop types.Card.interactiveis removed. Use a semantic link or button for actions instead of putting hover-only interaction styling on a generic card.PinInput.ariaLabelis removed. Use the nativearia-labelattribute when there is no visiblelabel.Swatchmoved to the showcase layer and is no longer exported from the package. Replace package imports with the showcase-onlySwatchwhere documenting tokens; consumer applications should render their own token presentation.
Added
- Public contracts are exported for hardened components.
AccessibleName,InputProps,MeterProps,PinInputProps,SelectProps,AppShellProps, andAppShellLabelsare available from the package root. Cardkeeps fixed tokenized padding container-safe. Its border box, minimum inline size, and maximum inline size preserve the existing spacing recipes inside narrow containers.Meter.valueLabelexposes human-readable values. The visible value and meter semantics now share the same normalized range.PinInputis form-native and SSR-safe. It forwards native input and form attributes to one stable control, provides a no-JavaScript fallback, and preserves exact length validation after hydration.Table.rowKeyprovides stable row identity. Consumers can supply a primitive key selector; object identity remains the fallback.- Canonical release commands are part of the repository contract.
yarn devis the primary development command, andyarn build:releaseproduces and validates the site, package, and Storybook underbuild/storybook/.
Changed
Inputpreserves external label composition. Pair a publicLabel forwith the matching Inputid, or supply at most one internallabel, nativearia-label, or nativearia-labelledby; the public type no longer rejects the valid external-label shape.AppShellmobile navigation is a modal Bits UI dialog. It uses the Provider portal boundary, manages focus and restoration, closes at the canonical desktop breakpoint, includes a skip link, and keeps bottom navigation semantic.- Overlay triggers delegate to consumer-owned elements.
ModalandAlertDialogpass trigger props through their snippets instead of adding nested interactive wrappers. CommandPaletteitem identity is independent of visible titles. Duplicate translated titles no longer collide.- Scoped
Providerutility styles are fully isolated. Utility selectors remain inside the scope or shadow root while document-mode CSS stays unchanged. Icondocuments and exposes the complete Phosphor glyph contract. Dynamic names continue to resolve against the full upstream set.- Success and warning semantic tones meet WCAG AA. Both support white text on solid fills and tone text on their 12% surface tints.
Fixed
- Disabled or loading link-shaped
Buttons cannot navigate or activate. Their semantics, keyboard behavior, and pointer behavior now match disabled buttons. Accordionpreserves correct focus and presence behavior. Closed panel descendants unmount, and caret and panel motion respect reduced-motion preferences.Selectcomposes native ARIA descriptions consistently. Consumeraria-describedbyis preserved alongside the single current feedback ID; an error replaces the hint and forcesaria-invalid="true".- The site emits the complete default theme and utility CSS during SSR. Header geometry is styled before hydration, removing the first-load layout flash.
- Documentation and release surfaces are synchronized. Generated API data resolves imported public prop aliases, component guidance and demos remain paired in English and Russian, all public components have sorted Storybook stories, and the site links to the base-aware Storybook artifact.
0.15.2
A mobile-input hardening release from the consumer-app mobile audit: no new APIs, three fixes.
Fixed
Inputtext is 16px, ending iOS Safari’s zoom-on-focus. The native<input>was set in 15px, and iOS Safari auto-zooms the whole page (and leaves it zoomed) whenever a control with a computed font-size under 16px receives focus — every text field in every consumer app, on every visit, at every phone width. The bump is flat (no media query): 1px is visually negligible next to keeping desktop and touch renders identical. Consumers should not counter it withmaximum-scale=1— that suppresses pinch-zoom on some Android browsers and fails accessibility audits.Input’s reveal toggle has a real touch target. The show/hide-password button was content-sized to its 18px icon — under even the WCAG 2.5.8 AA 24px minimum, and sitting inside the competing 44px input target, so a thumb tap usually focused the field (popping the keyboard) instead of toggling. The button now stretches to the field height with an explicit width — a 36×38px hit area — while a negative right margin eats into the field’s padding so the icon stays within 1px of where it drew before. Its focus ring dropsoutline-offsetto 0 so the ring stays inside the field border instead of poking past it.Buttongrows with a wrapping label. The size recipe pinned an exactheight, so a label that wrapped to three or more lines (long localized strings on narrow phones) painted outside the pill into neighboring content. The recipe now setsmin-height: single-line and icon-only buttons render pixel-identical to 0.15.1 (icon-only stays square — its width rule is separate), and only a multiline label grows the pill to contain itself. Note that buttons in a stretch-aligned flex row now equalize to the tallest sibling, which is the wanted outcome in the kit’s own dialog footers.
0.15.1
Fixed
PinInputcells shrink on narrow containers. The six 46px cells plus 10px gaps put the row at a fixed 326px — wider than the content area of a phone-width card (e.g. ~262px inside apadding="lg"Card at 390px viewport), so the row forced a horizontal scroll. Cells now carryflex: 0 1 46px; min-width: 0and shrink evenly to fit; at rest the row is pixel-identical to 0.15.0.- Document-mode base rule resets the UA body margin.
BD_UTILITY_CSS/ the Provider’s document-mode injection now emitbody { margin: 0; … }. The browser-default 8px margin put a halo aroundAppShell’s 100dvh grid — a permanent 16px document scroll plus 16px of lost width on every screen. The showcase’s ownapp.htmlalways carried this reset; consumers got nothing. The scoped (.bd-scope) rule is unchanged — stripping margins off an arbitrary host element is not the kit’s call.
0.15.0
An accessibility and SSR-ergonomics release driven by the first consumer apps: heading levels that fit a real document outline, a <main> landmark, localizable shell labels, OTP autofill, a visible Switch label, an AA-compliant muted text token, and the utility stylesheet as a public export. Additive — no renames, no removed props.
Added
Subsection.headingLevel(1–6, default3). The title used to be a hardcoded<h3>, forcing an h1→h3 skip in any app whose shell already carries the<h1>. The style is now on a.bd-subsection-titleclass, so every level looks identical. Adopters: passheadingLevel={2}when the subsection sits directly under a page<h1>(e.g. insideAppShellwith an<h1>in the top bar).AppShellrenders the content card as a<main>landmark. Screen-reader users previously got no main region from the shell at all. NewmainLandmark?: boolean(defaulttrue) opts out for the embedded case — a shell mounted inside a page that already has its own<main>(a document must carry exactly one). Adopters: drop any<main>wrapper you were rendering inside the shell’s content slot — it would now be nested and invalid.Switch.label— a visible caption wired to the control through a real<label for>(bits-uiLabel.Root), so clicking the text toggles the switch, mirroringCheckbox’s composition and sizing (13/14/15px acrosssm/md/lg).ariaLabelis unchanged and stays the right tool for icon-only placements; with nolabelthe rendered DOM is byte-identical to 0.14.0. (0.14.0 renamed the old aria-onlylabeltoariaLabel; thislabelis a new, visible thing.)PinInputlabeling +ariaLabel.labelnow renders as a true<label for>bound to the hidden<input>bits-ui overlays across the cells (it was a bare, unassociated<span>), newariaLabelnames the input when no visible label is wanted, the error line is wired viaaria-describedby/aria-invalid, and the presentational cell row isaria-hiddenso the code isn’t announced twice.BD_UTILITY_CSS/BD_UTILITY_CSS_ID— the exact document-mode utility stylesheet theProviderinjects (bodybase rule +.bd-*type/gradient/grid classes) and the id of its<style>element, exported from the package root. SSR apps inline<style id={BD_UTILITY_CSS_ID}>{BD_UTILITY_CSS}</style>in<head>for a themed first paint (no FOUC) instead of hand-mirroring the classes; the Provider sees the id on mount and skips its own injection. The Provider’s runtime behavior is unchanged.
Changed
--bd-text-mutednow meets WCAG AA.rgba(14, 42, 102, 0.62)composited to ~4.25:1 on white — just under the 4.5:1 minimum for the 11–13px copy the kit sets in it. The alpha is now0.68: 5.09:1 on surface, 4.95:1 on surface-alt, 4.83:1 on canvas, 4.63:1 on sunken. Muted copy across the kit gets very slightly darker; the muted↔text hierarchy is preserved. Review before adopting only if you fine-tuned custom UI against the old value.AppShell.tabsLabeldefault no longer glues an English suffix ontonavLabel. It was`${navLabel} (compact)`, which broke localization for any non-EnglishnavLabel; it now falls back tonavLabelas-is. Localized apps:navLabel,openDrawerLabel,closeDrawerLabel, andtabsLabelall default to English strings — pass localized values for all of them; givetabsLabela distinct string when the two nav landmarks should be told apart.
Fixed
PinInputpinsautocomplete="one-time-code"on its input explicitly (iOS SMS/email code autofill). bits-ui already defaults to it, so this is a guarantee rather than a behavior change — it’s now part of this component’s contract rather than an upstream default.
0.14.0
A breaking API-naming release. No new components and no behavior change beyond the fixes listed under Added — this pass converges prop and type names across the kit onto consistent vocabulary (variant for visual recipes, description for the secondary-copy slot, ariaLabel for aria-only strings, value/checked/active matched to the right bindable per control). There are no aliases or deprecated shims: update call sites to the new names.
Breaking — core / tokens
BD_TOKENS.bp→BD_TOKENS.breakpoint. The breakpoints map only; the unrelated--bd-bp-*blueprint CSS variables are untouched.type DeepPartial→BDDeepPartial, exported fromsrc/lib/index.tsunder the new name.BDDensityfields renamed:rowH→rowHeight,padY→paddingY,padX→paddingX. The CSS variables they feed (--bd-row-h,--bd-pad-y,--bd-pad-x) are unchanged.BDAccentloses its deadnotefield. It carried no runtime behavior; the type and theBD_ACCENTliteral both drop it.internal/boundary.ts:BDBoundary.root→scopeRoot, including every overlay’sgetBoundary()?.root()call site.Provider:root→scoped, semantics inverted.root?: boolean(defaulttrue) is nowscoped?: boolean(defaultfalse);scoped={true}is the oldroot={false}. Theaccentandthemeshorthand props are gone —overrides?: BDThemeOverridesis the single entry point for per-instance recoloring.Provider root={false}→Provider scoped;Provider accent={X}→Provider overrides={{ accent: X }};Provider theme={Y}→Provider overrides={{ theme: Y }}.
Breaking — controls
Swatch:name→label,role→tag,fg→tagColor.Link:tone→variant('accent' | 'muted'values unchanged).LogoStrip:names→labels;type LogoStripTone→LogoStripVariant(proptone→variant).Avatar:tone→variant;type AvatarTone→AvatarVariant; the'cream'literal is now'sunken'.size?: numberwidens tosize?: number | ControlSize(sm=32,md=40,lg=56 px; raw numbers still work).Alert:icon?: IconName | Snippet | true→icon?: IconName | Snippet | boolean.CodeBlock:language?: CodeLang | string→CodeLang | (string & {})(same runtime type, better autocomplete).Stat:eyebrow?: Snippet→leading?: Snippet.EmptyState:body→description.Meter:MeterToneis now exported from a newMeterTypes.tsasexport type MeterTone = 'accent' | 'success' | 'warning' | 'danger'.Button:full→fullWidth.Checkbox:value/onValueChange→checked/onCheckedChange(still bindable).Switch:value/onValueChange→checked/onCheckedChange;label→ariaLabel(it was always aria-only).Chip:value/onValueChange→active/onActiveChange.Segmented:label→ariaLabel.Tabs:label→ariaLabel;type Tab→TabItem.Textarea:count→showCount.Input:icon→leading(pairs with the existingtrailing).CopyButton:successTone?: boolean→tone?: 'neutral' | 'success'(oldtruemaps to'success').Accordion:AccordionItem.id→value(matchesTab/RadioOption/SelectOptionand bits-ui itself).Calendar: public propplaceholder→viewDate(still forwarded to bits-ui’sbind:placeholderinternally).PinInput:type: 'numeric' | 'alphanumeric' | 'text'→characters: 'numeric' | 'alphanumeric' | 'any'—'text'becomes'any'.FileUpload:FileUploadError’stype: 'size' | 'type'→reason: 'maxSize' | 'accept'.DateRangePicker:min/max→minValue/maxValue;valueis now optional and bindable, defaulting to{ start: undefined, end: undefined }.- Import renames:
Tab→TabItem.
Breaking — layout
Card:tone→variant;type CardTone→CardVariant.Stage:tone→variant;note→description;minH→minHeight;inset?: boolean | 'center' | 'flushX' | 'flushY'→inset?: StageInset('none' | 'plain' | 'center' | 'flushX' | 'flushY', default'none'; the old baretrueis now'plain'). NewStageTypes.tsexportsStageVariantandStageInset.Section:kicker→description.Subsection:note→description.ScrollArea:maxHeight?: number→number | string(number = px, string = raw CSS length);frame→framed.Toolbar:ToolbarToggleGroupitems now use an exportedtype ToolbarToggleItem = { value: string; label: string; icon?: IconName }fromToolbarTypes.ts.Breadcrumb:type Crumb→BreadcrumbItem;label→navLabel;separatorandseparatorSnippetmerge into a singleseparator?: BreadcrumbSeparator | Snippet. NewoverflowLabel?: string(default'Show hidden crumbs'), wired to the overflowMenu’striggerLabel.AppShell:AppNavItem.active→current(still rendersaria-current="page"). NewopenDrawerLabel?: string(default'Open navigation') andcloseDrawerLabel?: string(default'Close navigation') replace the two hardcoded aria-labels on the mobile drawer; newtabsLabel?: string(default`${navLabel} (compact)`) labels the mobile tab bar.searchTriggerliterals (including'topbar') are unchanged.Divider:labelandverticalare now mutually exclusive at the type level — supplyinglabelwithorientation="vertical"is a type error.- Import renames:
Crumb→BreadcrumbItem.
Breaking — overlays
Modal:confirmTone→confirmVariant;type ModalConfirmTone→ModalConfirmVariant. Newexport type ModalSize = 'sm' | 'md' | 'lg'inModalTypes.ts, now used to type the existingsizeprop.AlertDialog:tone→confirmVariant(type AlertDialogTone→AlertDialogConfirmVariant);actionLabel→confirmLabel;onAction→onConfirm— this converges the dialog’s vocabulary ontoModal‘s.CommandPalette:type CmdItem→CommandPaletteItem;type CmdGroup→CommandPaletteGroup. Item shape (icon/image/kbd/meta) is unchanged.NavigationMenu:type NavMenuLink→NavigationMenuLink;type NavMenuEntry→NavigationMenuEntry.Toast: thetoast.erroralias is gone —toast.dangeris canonical.ToastData.action.onClick→onSelect.Tooltip: exactly one oftrigger(snippet) ortriggerIcon(IconName) is now required, enforced by a discriminated union — supplying both, or neither, is a type error.- Import renames:
CmdItem/CmdGroup→CommandPaletteItem/CommandPaletteGroup,NavMenuLink/NavMenuEntry→NavigationMenuLink/NavigationMenuEntry,ModalConfirmTone→ModalConfirmVariant,AlertDialogTone→AlertDialogConfirmVariant.
Breaking — data / charts
Table:type Column→TableColumn;TableSort.dir→direction;onSort?: (key, sort) => void→onSortByChange?: (sort: TableSort) => void(the redundantkeyargument is gone — read it offsortinstead).Pagination:showEdges→showFirstLast.Chart:kind→variant(type ChartKind→ChartVariant);legend→showLegend;tooltip→showTooltip(tooltipContentis unchanged).Sparkline:kind→variant. Every exportedSpark*-prefixed type is nowSparkline*(SparkKind→SparklineVariant, etc.). The previously-internal_sharedcurve type is now a publicexport type SparklineCurve = 'linear' | 'monotone'inSparklineTypes.ts, and thecurveprop is typed with it.- Import renames:
Column→TableColumn,ChartKind→ChartVariant,Spark*→Sparkline*.
Added — behavior fixes bundled with the renames
Avatara11y fix. NewstatusLabel?: stringprop. The accessible status text used to be hardcoded to “Online”/“Offline” regardless of the actualstatusvalue; it now auto-derives only forstatus="online"/status="offline", usesstatusLabelfor the other five status values, and omits the aria text entirely when neither applies.Accordion.headingLevelnow does something. The heading level was accepted as a prop but the component always rendered an<h3>; it now renderssvelte:element this={'h' + (it.headingLevel ?? 3)}, so per-item heading levels actually take effect.FileUpload.filesis bindable. The selection used to be private internal state, readable only throughonFilesChange; it’s nowfiles = $bindable([]), so callers can both read and seed the selection.onFilesChangestill fires on every change.DateRangePicker.labelis now visible. It used to be aria-only; it now renders a caption above the trigger (mirroringDateField’s label markup,.bd-drp-label) while keeping the accessible name wired to the trigger.Select.hint. Newhint?: stringprop, rendered likeInput’s hint/error line below the trigger (errorwins when both are set).
0.13.0
Provider is now first-class Shadow-DOM-safe, so the kit can mount inside a shadow root — an embeddable widget on a page you don’t control — without touching the host document. Backward-compatible: document mode (root={true}, the default) is unchanged.
Added
- Shadow-DOM boundary.
Provider root={false}now injects the utility styles into the correct root — an adopted stylesheet when it sits inside aShadowRoot, otherwise a scoped#bd-kit-utilities-scoped<style>in<head>— scopes the basebackground/color/fontrule to its own.bd-scopewrapper instead ofbody, and never mutatesdocument.documentElementorbody. It also renders an in-scope.bd-portal-layerand publishes a portal boundary through Svelte context (src/lib/internal/boundary.ts). - Every overlay that portals a floating panel —
Select,DatePicker,Popover,Menu,Tooltip,Modal,Sheet,AlertDialog,ContextMenu,Menubar,LinkPreview,CommandPalette— now portals into that boundary when a scopedProvideris present, so the panel stays inside the shadow tree. With no scopedProvider(document mode) they portal todocument.bodyexactly as before. - Spacing proximity ramp (DESIGN.md §12). Codified the vertical-rhythm doctrine: a Tight → Grouped → Related → Distinct → Zone ramp mapping how related two blocks are to a
--bd-s-*gap, a 40px whitespace ceiling, and a “one spacing owner” rule. Stackgains a2xl(40px) gap — the Zone tier of the proximity ramp.Subsectiongains aflushprop — drops its own bottom margin so a parent rhythm container owns the vertical spacing. The default keeps the current 48/32px margin, so existing layouts are unchanged.- The package now ships
DESIGN.mdandCHANGELOG.mdso the design-system rules travel with the install — consumers get the usage contract without opening the repository.
Changed
.bd-h1–.bd-h6utility classes now resetmarginto0. Semantic headings (<h1 class="bd-h5">) no longer drag the browser’s default heading margin into a flex/grid rhythm; the type utilities own font, not spacing.- Otherwise nothing breaking.
Provider root={true}(document mode, the default) behaves byte-for-byte as in 0.12.0, andSubsection’s default spacing is unchanged.
0.12.0
Every component bits-ui ships now has a kit counterpart. 18 new components, four internal rebases with unchanged public props, and a new /components showcase route.
Added
Label(primitives/) — native<label>(bits-uiLabel.Root) sized toControlSize, with arequiredasterisk marker.Meter(presentational/) — static measurement bar (bits-uiMeter,role="meter") for a known range — battery, storage — never a task in flight, which is whatProgressis for.Collapsible(interactive/) — single expand/collapse panel; the panel height animates from bits-ui’s real measured--bits-collapsible-content-height, never a guessed max-height.RatingGroup(interactive/) — glyph rating with one rovingrole="slider"tab stop; arrow keys set the value,allowHalffor half-glyph precision.Calendar(interactive/) — single-date grid, sibling to the range gridDateRangePickeralready wraps.DateField,DatePicker,DateRangeField,TimeField,TimeRangeField(interactive/) — the rest of the segmented date/time family: text inputs built from one focusableSegmentper part,DatePickeradding aCalendargrid in a floating panel. All take and returnDateValue/TimeValue/DateRange/TimeRangefrom@internationalized/date/bits-ui, neverDate.AspectRatio(layout/) — fixed width÷height frame for embeds and media.ScrollArea(layout/) — styled scroll container with a slim custom bar in place of the native one.Toolbar(layout/) — data-driven action row (button/link/separator/toggle-group items) with bits-ui’s roving focus, matchingMenu’s declarative item-array model.AlertDialog(overlays/) — forced-choice confirm (role="alertdialog"); unlikeModal, it never dismisses on Escape or an outside click.ContextMenu(overlays/) — right-click action menu overMenu’s declarativeMenuItem[]model and panel classes.LinkPreview(overlays/) — hover/focus preview card for a link; never opens on touch.Menubar(overlays/) — desktop-style File/Edit/View bar; once one menu is open, arrow keys move between the others.NavigationMenu(overlays/) — top nav with hover/focus-opened panels and a sliding indicator under the active trigger.Button.href— render as an<a>instead of a<button>; bits-ui swaps the element and its disabled semantics for us. See Changed below./componentsroute — a five-section catalogue (inputs, dates and time, navigation, feedback, layout) for every component above plusLabel/RatingGroup/Collapsible/Meter/AspectRatio/ScrollArea/AlertDialog, all of which had nowhere to live before.
Changed
Button,Avatar,Divider,Segmentednow sit onbits-ui. Public props are unchanged for all four (Buttongains the additivehrefabove).Buttondelegates ontobits-ui’sButton.Root, which renders<button>or<a>and owns the disabled semantics of each.Avatardelegates ontobits-ui’sAvatar(Root/Image/Fallback), which tracks a real imageloadingStatusinstead of our oldonerrorguess.Dividerdelegates ontobits-ui’sSeparator.Segmenteddelegates ontobits-ui’sToggleGroup type="single"instead ofTabs— the correct semantics for a group of toggles, not a tablist — so external selectors keyed onrole="tab"will no longer match aSegmentedoption; its look, props, and keyboard model are otherwise unchanged.DateRangePickermoved from/chartsto/components. It’s an input, not a chart; it only sat there because it shipped alongsideChart. Thech-daterangesection and its nav entry are gone from/charts; the picker now lives in/components’ “Dates and time” section alongside its new siblings.- Nav reordered. Foundations → Components → Charts → Mobile → Landing → App landing → SPA → Guidelines. Charts moves up from the bottom to sit with the other building blocks, ahead of the scenario pages.
0.11.0
Four new components and one additive Popover API. No breaking changes.
Added
CodeBlock(presentational/) — read-only snippet surface with a copy control. No highlighter is bundled;highlightis an opt-in callback whose return value is injected with{@html}, so the caller owns sanitisation.Pagination(data/) — controlled page controls over bits-ui’sPagination, which owns the page math, the ellipsis items, and the item range behind the1–50 of 1,234summary. A standalone sibling ofTable.Breadcrumb(layout/) —nav > oltrail; pastmaxItemsthe middle collapses into aMenu.Tabs(interactive/) — panelled tabs withunderlineandpillvariants.Segmentedremains the compact, panel-less sibling.Popover.anchor— position the panel against anyMeasurable({ getBoundingClientRect() }) instead of a trigger.triggeris now optional; an anchored popover renders none.Popover.focusOnOpen— passfalseto leave focus where it is, for a mention menu over a textarea. The panel is then never focused, so the caller owns the combobox ARIA on the input side.Measurabletype, exported from the package.bits-uidoes not export its own.
0.10.0
The headless layer moves from melt to bits-ui.
melt (next-gen) shipped no release after 2026-01-04 and no library commit after 2026-03-04. bits-ui is the maintained successor from the same author, with a wider component surface. Every component’s public props survive the move except where noted below: the DOM, the bd-* class names, and the --bd-* tokens are unchanged, because our element is delegated through bits-ui’s child snippet rather than rendered by it.
Breaking
Treeis removed.Tree,TreeNode, andNodeStateare no longer exported. It was the lastmelt-only component with nobits-uicounterpart and no external consumers.DateRangePickermoves toDateValue.DateRangeis now{ start: DateValue | undefined; end: DateValue | undefined }from@internationalized/date, replacing{ start: Date; end: Date }. Both ends became optional because the calendar reports a half-open range while the user is mid-selection — code readingrange.startmust now handleundefined.RangePreset.getRangereturns the same shape.peerDependenciestightened.svelteis now^5.33.0, the floorbits-uisets.@internationalized/date ^3.8.1is a new peer:DateValueappears in the public type surface, so the consumer must resolve the same package instance the kit does. Install it if you useDateRangePicker.PinInputErrormoved. It used to be re-exported frommelt/builders; it now ships from the kit’s own types.
Changed behaviour, unchanged signatures
CommandPalette. With the defaultfilter={true}, results are now ranked by a relevance score rather than filtered by case-insensitive substring, so the order of results differs.filter={false}and a predicatefilterkeep their exact previous semantics.Menu. It was aPopoverwithrole="menu"painted on by hand and a hand-rolled roving cursor. It is now a real menu with typeahead, submenu support, and managed focus. External selectors that relied on the old DOM will not match.Modal,Sheet,CommandPalette. These rendered a native<dialog>driven byshowModal(), with the backdrop pushed into the top layer via the Popover API. They now renderbits-ui’s portalledrole="dialog"panel, which owns the focus trap, the scroll lock, Escape, and outside-click. Leaving the top layer means the backdrop and the panel position themselves withz-index.- Overlay animation.
bits-uimounts content already open and waits for a CSS animation before unmounting, so every overlay’s enter/exit is now a@keyframesanimation keyed ondata-state="open" | "closed"instead of atransitionon[data-open]. - Floating panels moved onto the modal stacking plane.
meltrendered a popover’s content inline, so aSelectinside aModalinherited the modal’s stacking context.bits-uiportals it to<body>, where the old--bd-z-dropdown(100) sat far below--bd-z-modal(900) — the panel rendered behind the dialog that owned it.Popover,Menu,Select, andTooltipnow use a new--bd-z-floatingtoken, which shares the modal’s value; portal mount order settles the rest.Modal’s andCommandPalette’s backdrops moved from--bd-z-overlayto--bd-z-modalso a dialog opened from aSheetdims it.
Added
--bd-z-floatingtoken. See above.BD_TOKENS.zgains afloatingentry.Checkboxsupportsindeterminate. A bindable tri-state prop reportingaria-checked="mixed". The component is no longer a native<input type="checkbox">.Switchreportsrole="switch". The oldTogglebuilder never rendered it.Toastergained alabelprop for the accessible name of the toast region.DateRangePicker’s default caption survives mid-selection. With a start picked and an end pending it reads12 Mar – …instead of falling back to the placeholder.
Internal
ToasterandFileUploadare hand-rolled:bits-uiships neither. The toaster owns its queue, its pause-on-hover countdown (which resumes from the remaining time rather than restarting), and thearia-livepoliteness split — assertive fordangerandwarning, polite otherwise.FileUploadowns the hidden input, theaccept/maxSizevalidation, and the selection list.DateRangePickersheds 537 lines of hand-written calendar logic and the 123-linedate.ts. Locale-correct calendars,isDateUnavailable, and the keyboard contract now come frombits-ui’sRangeCalendar.- Hand-rolled keyboard handling now survives in exactly two places, both for want of a
bits-uicounterpart:AppShell’s Escape-closes-the-drawer, andFileUpload’s Enter/Space-opens-the-picker.