Files
netto/docs/superpowers/specs/2026-07-05-base-ui-shadcn-wrapper-design.md
Maofeng 08e293d5f7 docs: document architecture and usage
Explain installation, styles, exports, and validation.

Record the component wrapper design and implementation plan.
2026-07-25 06:04:19 +08:00

5.5 KiB

Base UI Shadcn Wrapper Design

Goal

Build an internal source-template component library that wraps Base UI React with Tailwind CSS styles. The library should feel like shadcn/ui for application developers while preserving a Base UI-style part API for people who prefer Base UI's composition model.

The first version is source-only. It will not publish to npm, provide a CLI, or maintain registry metadata.

Inputs

  • Base library: @base-ui/react
  • Styling: Tailwind CSS utility classes
  • Default visual style: Base UI React documentation examples
  • Consumer model: copy source files into internal React projects
  • Framework target: framework-neutral React source templates

Component Scope

The first version covers every component listed in the Base UI React component documentation:

  • Accordion
  • Alert Dialog
  • Autocomplete
  • Avatar
  • Button
  • Checkbox
  • Checkbox Group
  • Collapsible
  • Combobox
  • Context Menu
  • Dialog
  • Drawer
  • Field
  • Fieldset
  • Form
  • Input
  • Menu
  • Menubar
  • Meter
  • Navigation Menu
  • Number Field
  • OTP Field
  • Popover
  • Preview Card
  • Progress
  • Radio
  • Scroll Area
  • Select
  • Separator
  • Slider
  • Switch
  • Tabs
  • Toast
  • Toggle
  • Toggle Group
  • Toolbar
  • Tooltip

Directory Shape

Each component is a folder under components/ui. Composite components split every Base UI part into its own file.

Example:

components/ui/dialog/
  index.ts
  root.tsx
  trigger.tsx
  portal.tsx
  backdrop.tsx
  popup.tsx
  content.tsx
  title.tsx
  description.tsx
  close.tsx

Single-part components also use folders for consistency:

components/ui/button/
  index.ts
  root.tsx

Shared helpers live in:

lib/utils.ts

Export Model

Every component module supports two API styles from the same implementation.

Default export is the styled Base UI namespace:

import Dialog from "@/components/ui/dialog"

<Dialog.Root>
  <Dialog.Trigger>Open</Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Backdrop />
    <Dialog.Popup />
  </Dialog.Portal>
</Dialog.Root>

Named exports are the shadcn-style flattened API:

import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogTitle,
} from "@/components/ui/dialog"

<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogTitle>Title</DialogTitle>
  </DialogContent>
</Dialog>

For single-part components:

import Button from "@/components/ui/button"
import { Button as UIButton } from "@/components/ui/button"

Button as the default export is the styled primitive for Base UI-style import habits. Button as a named export is the shadcn-style import. For single-part components these can reference the same underlying styled component.

Part file names should mirror Base UI part names where possible. Additional shadcn-style convenience components, such as DialogContent, can live beside the primitive part files when they compose multiple parts.

Implementation Rules

  • Styled parts wrap Base UI parts directly.
  • Each part owns its own Tailwind classes.
  • Shadcn-style convenience components compose styled parts instead of duplicating styles.
  • The internal library does not re-export unstyled Base UI primitives. Consumers who need unstyled primitives can import from @base-ui/react directly.
  • Every styled part accepts className and merges it after the default classes using cn.
  • Forward refs where the underlying Base UI part supports refs.
  • Use Base UI data attributes and CSS variables for stateful styling.
  • Add "use client" to interactive component files so copied source works in Next.js projects while remaining harmless in ordinary React projects.

Styling Rules

Default styles should mirror Base UI documentation examples:

  • Neutral black/white visual language
  • Clear borders
  • Compact control sizing
  • focus-visible outlines
  • data-* selectors for open, highlighted, checked, disabled, selected, and similar states
  • Dark mode classes where Base UI examples provide them
  • Tailwind classes in source files, not CSS Modules

Components should not add shadcn-style variant systems unless Base UI's official examples require that abstraction. The default implementation should stay close to the official Base UI Tailwind demos, and local variation should happen through className overrides.

TypeScript Rules

  • Component props extend the corresponding Base UI part props.
  • Variant props are added only where the component defines variants.
  • Export prop types for public components where useful.
  • Avoid hiding Base UI capabilities such as render, nativeButton, focusableWhenDisabled, positioning props, and controlled-state props.

Documentation

Add a README that explains:

  • Required dependencies
  • Tailwind requirements
  • The default namespace export
  • The named shadcn-style export
  • The source-copy workflow
  • How to customize classes safely

The README should include a short example for a single-part component and a composite component.

Verification

The first implementation should verify:

  • TypeScript compiles.
  • Every component folder exports a default namespace or default primitive.
  • Every component folder provides shadcn-style named exports where appropriate.
  • Tailwind class strings are static enough for Tailwind scanning.
  • Representative examples compile for both a single-part component and a composite component.

Visual verification can be added after the component set exists, using a local demo page or Storybook-like fixture.

Non-Goals

  • No npm package publishing.
  • No CLI.
  • No registry metadata.
  • No attempt to create a separate design language.
  • No unstyled primitive re-export layer.