A small, opinionated React component library by CoMake, built around a CSS-variable-driven design-token system and Tailwind CSS.
Components are thin React wrappers that emit semantic class names; the visual layer lives in a set of theme assets (themeProperties.css, twConfig.json, twComponentsConfig.json) that consumers wire into their own Tailwind configuration. Overlay primitives (popover, tooltip, context menu) are powered by Radix UI.
- Package:
standard-ui - License: MIT
- React: 18.3+
- Output: dual ESM + CJS bundle with bundled
.d.tstypes
- Installation
- Setup
- Usage
- Components
- Theming
- Project structure
- Development
- Build
- Peer dependencies
- Contributing
- License
npm install standard-ui
# or
yarn add standard-uiYou also need the peer dependencies:
npm install react react-dom tailwindcss tailwind-mergestandard-ui does not ship a precompiled stylesheet; it relies on Tailwind in the consuming app and on a set of theme assets distributed alongside the bundle.
Import the library's CSS once at the entry of your application (e.g. index.tsx / _app.tsx):
import "standard-ui/dist/esm/bundle.js"; // bundle import is automatic via package.json
// The CSS is bundled into the package and injected at the top of the document by Rollup's postcss inject.If you want to ship the design tokens explicitly, import the theme stylesheet:
import "standard-ui/dist/esm/assets/themeProperties.css";This file declares the --core-color-*, --brand-*, --system-*, --text-*, etc. CSS variables that components reference.
The token-to-Tailwind mapping ships as JSON. In your tailwind.config.js:
const twConfig = require("standard-ui/dist/esm/assets/twConfig.json");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,ts,jsx,tsx}",
"./node_modules/standard-ui/dist/**/*.js",
],
theme: {
extend: {
colors: twConfig.colors,
spacing: twConfig.spacing,
borderRadius: twConfig.borderRadius,
boxShadow: twConfig.boxShadow,
fontSize: twConfig.fontSize,
// ...spread the rest of twConfig as appropriate for your project
},
},
plugins: [],
};Component-level classes (.button.primary, .tabs.selected, .input.primary, etc.) live in twComponentsConfig.json. Register them with Tailwind via the addComponents plugin API:
const components = require("standard-ui/dist/esm/assets/twComponentsConfig.json");
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addComponents }) => {
addComponents(components);
}),
],
};import {
Button,
TextInput,
Select,
Typography,
Tooltip,
Switch,
SvgIcon,
} from "standard-ui";
export function Example() {
return (
<div className="flex flex-col gap-4">
<Typography variant="h2">Hello, standard-ui</Typography>
<Button variant="primary" startIcon="plus" onClick={() => console.log("clicked")}>
Create
</Button>
<TextInput
placeholder="Search"
endIcon="search"
onChange={(e) => console.log(e.target.value)}
/>
<Select
options={[
{ label: "One", value: "1" },
{ label: "Two", value: "2" },
]}
onChange={(v) => console.log(v)}
/>
<Tooltip content="Helpful hint">
<SvgIcon icon="help" />
</Tooltip>
</div>
);
}| Component | Description |
|---|---|
Avatar |
Circular image; single size variant (s). |
AvatarList |
Row of avatars with an optional overflow count + label. |
Button |
Primary / secondary / link variants, optional start/end icons, color variants, anchor mode (link), badge, custom LinkComponent (e.g. Next.js Link). |
ButtonsSet |
Horizontal group of Buttons with shared rounded corners. |
CardContent |
Card body with title/subtitle/description/icon and several layout variants, including a loading skeleton. |
Checkbox |
Controlled checkbox with label. |
CircularProgress |
Spinner SVG. |
ClickAwayListener |
Wrapper that fires onClickAway when a click lands outside its child. |
HorizontalSpacer / VerticalSpacer |
Token-sized spacing elements (xxxxs … xxxxl). |
NavigationBarHeader |
Section header with left/right content and optional icons. |
NavigationBarItem |
List/link row with icons, active state, automatic overflow tooltip, and accessible keyboard handling. |
NavigationBarItemWithDropdown |
NavigationBarItem plus a right-click context popover backed by SearchableOptionsList. |
Select |
Single or multi-select built on Radix Popover + SearchableOptionsList. |
Switch |
Segmented switch (icon + label per option), horizontal or vertical. |
TabButton |
Tab as either a button (with useTransition pending state) or a link. |
Tag |
Small labeled pill with optional icon. |
TextInput |
TextInputBase + ExpandableTextInput (collapses to its icon until clicked). |
Tooltip |
Radix tooltip with primary / secondary variants. |
Typography |
Headings (h1–h6), body (b1, b2), captions, and display sizes. |
SvgIcon |
Registry of ~63 inline SVG icons (search, chevronDown, plus, trash, …) plus an ExternalIcon escape hatch for custom components. |
SearchList |
SearchListLocal, SearchListLocalPopup, SearchListNetwork, and SearchableOptionsList for filterable lists with optional "create new" flow. |
SvgIcon accepts either a known icon key or an ExternalIcon component:
<SvgIcon icon="chevronDown" />
<SvgIcon ExternalIcon={MyCustomIcon} />The full key list is defined in IconMap in src/components/SvgIcon/index.tsx.
The design system is layered:
- CSS variables (
assets/themeProperties.css) — the single source of truth for color ramps, spacing, radii, typography, and shadows. - Tailwind theme (
assets/twConfig.json) — exposes those variables to Tailwind utility classes (bg-brand-base-colors-primary-500,w-system-horizontal-spacers-m, etc.). - Component classes (
assets/twComponentsConfig.json) — semantic component styles (.button.primary,.tabs.selected,.tooltip.primary) registered throughaddComponents.
To rebrand, override the relevant --core-color-* / --brand-* variables in your own stylesheet after importing themeProperties.css. To replace component styles wholesale, swap the twComponentsConfig.json payload before passing it to addComponents.
src/
├── index.ts # Library entry; imports CSS and re-exports components
├── assets/
│ ├── themeProperties.css # CSS variable design tokens
│ ├── twConfig.json # Tailwind theme mapping
│ └── twComponentsConfig.json # Per-component class definitions
└── components/
├── index.ts # Public component exports
├── index.css # @tailwind base/components/utilities
├── Button/ # Compound components live in folders
├── SearchList/
├── TextInput/
├── SvgIcon/ # Icon registry + individual icon components
└── *.tsx # Single-file components
nvm use # see .nvmrc
npm install
npm run dev # rollup -c -w
npm run lintnpm run buildThis runs eslint --fix, clears dist/, and produces:
dist/cjs/bundle.js— CommonJS bundledist/esm/bundle.js— ES module bundledist/index.d.ts— bundled type declarationsdist/{cjs,esm}/assets/— copiedthemeProperties.css,twConfig.json,twComponentsConfig.json
{
"react": ">=18.3.1",
"react-dom": ">=18.3.1",
"tailwindcss": "^3.4.4",
"tailwind-merge": "^2.3.0"
}Internal dependencies bundled with the library: @radix-ui/react-popover, @radix-ui/react-tooltip, @radix-ui/react-context-menu, lodash.
- Fork and create a feature branch.
- Make changes under
src/. - Run
npm run lintandnpm run build. - Open a pull request against
main.
MIT © CoMake