A React component library of UI primitives, built with TypeScript and Tailwind CSS v4 on a three-tier design-token system.
npm install react-basics-ui
# or
yarn add react-basics-ui
# or
pnpm add react-basics-uiEvery component ships from the package root. The stylesheet is required — it carries the design tokens all components are styled against.
import { Button, Input, Card } from 'react-basics-ui';
import 'react-basics-ui/styles.css';
function App() {
return (
<Card>
<Card.Content>
<Input placeholder="Enter your name" />
<Button variant="primary">Submit</Button>
</Card.Content>
</Card>
);
}The stylesheet is not optional. Components reference
--semantic-*CSS variables defined inreact-basics-ui/styles.css. Without it they render unstyled — an undefinedvar()silently invalidates the whole declaration.
| Group | Components |
|---|---|
| Inputs | Button · Input · Textarea · SearchBar · BaseInputField · InputTrigger |
| Selection | Select · Checkbox · Radio · Switch · Slider · BaseSelectionControl |
| Date & time | DatePicker · TimePicker |
| Advanced | Autocomplete · FileInput |
| Structure | FormField · FormGroup |
Box · Flex · Grid · Stack · Container · Divider · AspectRatio · BaseCardContainer
Text · Heading · Label · TextList · BaseText
Table · Card · Accordion · Avatar · Badge · DataField · Timeline · Tree · EmptyState
Modal · Drawer · ConfirmDialog · Dropdown · Menu · List · Popover · Tooltip
Tabs · Breadcrumb · Pagination · Stepper · Navbar · Sidebar
Alert · Toast · Progress · Spinner · Skeleton · BaseAlertBox
Icon · Portal · VisuallyHidden · BaseOverlayDialog
useClickOutside · useClickOutsideWithExclusions · useEscapeKey · useBodyScrollLock
· useFocusTrap · useMergedRefs · useMenuKeyboardNavigation · useControlledState
· useDisclosureState · useDisclosureKeyboardNav · useResponsivePosition
· useAnchorPosition · useDebounce · useTheme
Components resolve their colors, spacing, and typography through a token funnel, each tier referencing only the one above it:
--primitive-* raw values #fcb711, 16px, 300ms
↓
--semantic-* intent --semantic-brand-primary-default
↓
components
Styling one intent at a time, semantic tokens are what components consume —
--semantic-surface-elevated, not --primitive-color-gray-100. That indirection
is what lets the dark theme redefine a token and have every component follow.
A third --component-* tier also exists in the stylesheet. It is legacy: only
Navbar and Sidebar still read from it, and new work should consume semantic
tokens directly.
Dark mode is opt-in via a data-theme attribute, which ThemeProvider manages:
import { ThemeProvider } from 'react-basics-ui';
function App() {
return (
<ThemeProvider theme="dark">
<YourApp />
</ThemeProvider>
);
}To re-skin the library, override semantic tokens after importing the stylesheet:
@import 'react-basics-ui/styles.css';
:root {
--semantic-brand-primary-default: #2563eb;
}src/
├── components/
│ ├── shared/ # Cross-component style fragments and type contracts
│ │ ├── styles/ # FOCUS_RING, TRANSITION_COLORS, DISABLED_CLASSES …
│ │ ├── types/ # ComponentSize, StatusVariant, SelectOption
│ │ └── hooks/ # useAnimatedPresence
│ ├── forms/
│ │ ├── inputs/ # Button, Input, Textarea, SearchBar, BaseInputField
│ │ ├── selection/ # Select, Checkbox, Radio, Switch, Slider
│ │ ├── date-time/ # DatePicker, TimePicker
│ │ ├── advanced/ # Autocomplete, FileInput
│ │ └── structure/ # FormField, FormGroup
│ ├── data-display/ feedback/ layout/
│ ├── navigation/ overlays/ typography/
│ ├── utility/ theme/
├── hooks/ # Reusable React hooks
├── lib/ # cn(), context factories, positioning helpers
├── tokens/ # Typed token exports
└── global.css # The --primitive-* / --semantic-* token system
Each component folder co-locates its implementation, .styles.ts class maps,
.types.ts contracts, tests, and Storybook stories.
- Styling — Tailwind utilities referencing tokens via arbitrary values:
bg-[color:var(--semantic-surface-elevated)]. No raw hex or px in components. - Class composition —
cn()(clsx + tailwind-merge) so consumerclassNameoverrides win. - Compound components — Sub-parts hang off the root:
Card.Header,Table.Row,Modal.Footer. - Controlled & uncontrolled — Interactive components accept either
valueordefaultValue, never both.
npm install
npx playwright install chromium # once — the story tests run in a real browser
npm run storybook # Component workshop on :6006
npm test # Vitest in watch mode
npm run test:run # Single run, both projects
npm run test:unit # Just the jsdom unit suite (fast, no browser)
npm run test:stories # Just the story play() functions, in headless Chromium
npm run build # Emit dist/ (ESM + CJS + types + CSS)
npm run lint # ESLint
npm run format # PrettierStories are tests. Every play() function runs under npm run test:run via
@storybook/addon-vitest, so a broken interaction fails the suite.
MIT
Contributions are welcome. Please open an issue before starting substantial work.