@mbsw/ui
v0.15.0
Published
MBSW React UI utilities built for the Tailwind CSS v4, Radix, and shadcn/ui composition model.
Downloads
4,163
Readme
@mbsw/ui
MBSW React UI utilities built for the Tailwind CSS v4, Radix, and shadcn/ui composition model.
The Card and Breadcrumb release wave is published as @mbsw/[email protected], paired
with the unchanged published @mbsw/[email protected] at Foundation source pin
814c7decc365dcea29bbd7b719892530bf473c21. That published release's Component
source authority refreshes the governed Card contract and adds Breadcrumb,
BreadcrumbItem, and BreadcrumbSeparator while preserving the published Chart
and Dialog surfaces. The immutable Chart
history binds ChartLegendSwatch, ChartLegend, ChartLegendSection,
ChartTooltipItem, and ChartTooltip to promoted source commit
080e13311f87cc45978fa45d01941aa11464adca; the UI exposes this presentation
family through the public root, @mbsw/ui/chart.css, and aggregate stylesheet.
The token package retains primitive/opacity.opacity.40 across its 488 public
manifest records. The UI requires the >=0.6.0 <1.0.0 tokens peer contract.
@mbsw/[email protected] is the immutable Card and Breadcrumb predecessor preserved by
the later package waves. The published immutable predecessor pair
@mbsw/[email protected] and @mbsw/[email protected] remains historical Registry custody.
That predecessor contains ActivityDot, Spinner, RemoveBadge, Empty, Tab,
TabsList, ToggleLink, and AvatarGroupCount; 0.12.0 preserves those roots and
the five published Chart presentation runtimes.
The Inspector and ProgressRing wave is packaged as @mbsw/[email protected], reusing
the unchanged @mbsw/[email protected] peer. It adds the governed Inspector,
InspectorHeader, InspectorContent, InspectorSeparator, and ProgressRing public
contracts plus inspector.css and progress-ring.css through the existing
@mbsw/ui/styles.css aggregate. Dialog remains a preserved public surface; its
separately reviewed correction is not part of this package candidate. This
packed README records immutable package contents, while consumers query the npm
Registry for mutable dist-tags and current availability.
The MediaSlot wave is packaged as @mbsw/[email protected], reusing the unchanged
@mbsw/[email protected] peer. It adds the governed MediaSlot public contract and
media-slot.css through the existing @mbsw/ui/styles.css aggregate. Dialog is
excluded from this package wave and remains for a separately governed follow-up
release. This packed README records immutable package contents, while consumers
query the npm Registry for mutable dist-tags and current availability.
The Dialog, DropdownMenu, and Select correction wave is packaged as
@mbsw/[email protected], reusing the unchanged @mbsw/[email protected] peer. It carries
the governed Dialog and SplitDialog geometry, programmatic-focus and public
floating-layer host corrections; Select selected/open-state, trigger-width and
placement corrections; and DropdownMenu anchor-width, placement and composed
Dialog-host corrections. No new package entry or aggregate stylesheet import is
required. This packed README records immutable package contents, while consumers
query the npm Registry for mutable dist-tags and current availability.
The 0.5.0 Release Train A package is merge-verified on canonical main and
keeps this repository's current public exports and aggregate stylesheet. Its
canonical [email protected] root dependency is the approved Radix dependency
migration from the individually packaged Radix dependencies in the published
0.4.0 artifact. All 17 family verifiers are active, and exact-live-main
release:verify-main passed with Storybook, visual, accessibility, pack and
clean-consumer gates. This packed README records the release status verified at
its publication boundary. Consumers query the npm Registry for mutable dist-tag
and current availability. Storybook source and verifiers remain internal and
are not packed.
The 0.6.0 Release Train B package is published with Card, ScrollArea,
InlineEdit, Toggle, and the standalone day/month/year Calendar. All five
families remain on the root @mbsw/ui export and the existing aggregate
stylesheet.
The 0.7.0 UI maintenance release is published and keeps the public import
surface stable while correcting Calendar weekday typography/color, using SVG
for the Toggle trailing-slot Storybook example, and adopting the InlineEdit
6.10 actions slot. It reuses the exact published @mbsw/[email protected] peer;
tokens were not republished for this immutable UI-only release.
The @mbsw/[email protected] Textarea release is published. It adds the native
Textarea root export and its aggregate CSS. Its @mbsw/tokens peer is
>=0.3.0 <1.0.0; the packed UI surface proved that 0.3.0 was the minimum
supported tokens version and that the selected release tokens stayed inside
the range. Later additive compatible tokens-only releases reuse this UI without
a bridge UI release.
The published 0.12.0 release refreshes the governed Card contract and adds
the governed Breadcrumb, BreadcrumbItem, and
BreadcrumbSeparator public contracts. Consumers render
one to three items, provide aria-label or aria-labelledby on Breadcrumb,
and mark only the final BreadcrumbItem as current. Non-current items require
a non-empty native href or an asChild link that owns a non-empty href or
to. The package emits breadcrumb.css and includes it in the existing
@mbsw/ui/styles.css aggregate; consumers do not import a new package entry.
import {
Calendar,
Card,
CardContent,
CardHeader,
CardTitle,
InlineEdit,
ScrollArea,
Textarea,
Toggle,
} from '@mbsw/ui';
<Card>
<CardHeader>
<CardTitle>릴리즈 준비</CardTitle>
</CardHeader>
<CardContent>본문</CardContent>
</Card>;
<ScrollArea variant="vertical">스크롤 콘텐츠</ScrollArea>;
<InlineEdit defaultValue="릴리즈 이름" />;
<Textarea aria-label="릴리즈 설명" defaultValue="Textarea consumer contract" />;
<Toggle shape="round">B</Toggle>;
<Calendar
defaultMonth={new Date(Date.UTC(2026, 7, 1))}
today={new Date(Date.UTC(2026, 7, 9))}
/>;Consumers import the Foundation stylesheet and the Component stylesheet once in their application entry point:
import '@mbsw/tokens/styles.css';
import '@mbsw/tokens/motion.css';
import '@mbsw/ui/styles.css';Tailwind animation utilities used by MBSW Components are compiled from the
pinned Tailwind build dependency into @mbsw/ui/styles.css. Consumers do not
install Tailwind, run Tailwind, or scan node_modules/@mbsw/ui; importing the
aggregate stylesheet provides the generated utility, keyframes, and
reduced-motion variants. Tailwind remains a repository build-only dependency,
not an @mbsw/ui runtime or peer dependency.
This packaged path is the default for future Tailwind animation utilities. An
owned @keyframes implementation requires a documented exception showing why
the Tailwind output cannot preserve parity, accessibility, performance, or
bounded package CSS, plus focused regression coverage. Choosing the packaged
utility does not change the approved Component motion contract.
Button is the public React API for the approved Button source contract. It
supports solid, outline, and ghost variants; primary, secondary, and
destructive tones; lg, md, and sm text sizes; and an accessible
iconOnly form with a required aria-label.
import { Search } from 'lucide-react';
import { Button } from '@mbsw/ui';
<Button tone="primary" variant="solid">
저장
</Button>;
<Button aria-label="검색" icon={Search} iconOnly size="xs" />;
<Button aria-label="동기화" iconOnly>
<span aria-hidden data-spinner />
</Button>;Button follows the shadcn/Radix composition model instead of adding a
separate as="a" API. Use asChild with one ref-forwarding element to compose
an anchor, router link, or another semantic root:
import { createRef, forwardRef, type ComponentPropsWithoutRef } from 'react';
const Link = forwardRef<HTMLAnchorElement, ComponentPropsWithoutRef<'a'>>(
(props, ref) => <a {...props} ref={ref} />,
);
const buttonRef = createRef<HTMLButtonElement>();
const linkRef = createRef<HTMLAnchorElement>();
<Button ref={buttonRef}>저장</Button>;
<Button asChild ref={linkRef}>
<Link href="/search">검색</Link>
</Button>;The native branch exposes an HTMLButtonElement ref. The asChild branch
exposes the honest shared HTMLElement ref boundary because the immediate
child—not Button—determines the concrete runtime element. TypeScript cannot
prove that every dynamic child and specialized ref match; the child must
forward its ref, and consumers remain responsible for matching that ref to the
rendered element. The package intentionally does not restrict asChild to
anchors or add a second polymorphic discriminator.
Radix Slot merges Button props into the immediate child. When both define the
same event, the child's handler takes precedence. An explicitly supplied
type is forwarded; the native type="button" default is not imposed on a
slotted child. The approved disabled state is native-button-only, so asChild
cannot receive disabled; convenience icon props are also reserved for native
buttons. asChild requires one concrete ref-forwarding child, not a Fragment
or an empty value.
Tailwind CSS v4 owns utility/style composition, the installed shadcn source
supplies the owned-code Component baseline, and Radix Slot supplies asChild
runtime composition. MBSW still owns the approved visual result, semantic
props, state contract, and accessibility. MBSW variant styles are defaults in
the Tailwind components layer: consumers may freely compose children and
extend the supported root through className, style, native props, events,
ref, data-*, and aria-*. Consumer classes are merged after the MBSW
defaults. buttonVariants is exported for MBSW-compatible composition.
Text Button requires non-empty children. Icon-only Button requires exactly
one content channel: either the icon convenience prop or one renderable
custom-content item. Empty strings, empty Fragments, multiple content items and
icon plus children are rejected.
The 0.4.0 Component contract narrows inherited Radix composition where MBSW
owns additional root anatomy. Avatar, AgentAvatar, RuntimeAvatar,
SelectTrigger, SelectItem, DialogContent, PopoverContent, and
DropdownMenuSubTrigger do not accept asChild; JavaScript callers that bypass
the public types receive a named fail-closed error. Safe primitive triggers such
as DropdownMenuTrigger, PopoverTrigger, HoverCardTrigger, and
TooltipTrigger keep their Radix composition surface.
DropdownMenuLabel may end with one native button, link, or MBSW Button
action. The action is registered as a Radix menu item so arrow-key navigation,
Enter/Space activation, and disabled-item skipping remain intact. Disabled link
actions omit href and block primary or auxiliary activation even when consumer
Tailwind utilities override pointer-event styling. DropdownMenuLabel asChild
remains available for exactly one concrete non-action React element; combining
asChild with a Fragment, directly inspectable interactive content, the owned
final action, or multiple roots fails closed before Radix Slot.
AlertDialogContent always prevents Escape dismissal after invoking the
consumer onEscapeKeyDown callback. HoverCardContent is read-only and rejects
directly inspectable interactive descendants; opaque custom components remain a
consumer responsibility, and interactive content belongs in Popover.
SplitButton requires one SplitButtonAction followed by a DropdownMenu
containing one root SplitButtonMenuTrigger and optional
DropdownMenuContent. Raw DropdownMenuPortal or arbitrary controls are
rejected so the group-disabled state cannot be bypassed. The owned
DropdownMenu also rejects opaque same-root raw DropdownMenuTrigger
descendants at render time. Standalone and nested independent DropdownMenu
roots keep the ordinary Radix trigger contract.
The immutable @mbsw/[email protected] release ships @mbsw/ui/button.css, which
remains a compatibility-only export for consumers pinned to that Button
release. The published 0.3.0 release and the 0.4.0 final Component release
use @mbsw/ui/styles.css as the one aggregate Component stylesheet. Consumers
do not add node_modules/@mbsw/ui to Tailwind source scanning. Import
@mbsw/tokens/motion.css with the aggregate so the Foundation reduced-motion
override applies to Component transitions.
The generic Icon wrapper remains available. Consumers own their Lucide icon
imports and Tailwind Preflight:
import { Search } from 'lucide-react';
import { Icon } from '@mbsw/ui';
<Icon icon={Search} />;The @mbsw/[email protected] Release Train A package requires the published
@mbsw/[email protected], React 19 and React DOM 19 as peer dependencies. It does
not contain raw Figma handoff data. The source contract remains in
mbsw-design-source,
while this package owns its React implementation and package release.
