twico-ui
v1.17.1
Published
Twico UI — a free, modern, themeable React + Tailwind-friendly component library. 61 components with dark mode, motion, and accessibility built in.
Maintainers
Readme
Twico UI
A free, modern, themeable React component library — 61 components with dark mode, motion, and accessibility built in. No runtime CSS framework required: components are styled with CSS custom properties (design tokens), so they theme by overriding variables and work great alongside Tailwind or plain CSS.
📖 Documentation, live demos & prop reference → encryptionl.github.io/twico-ui — the docs site supersedes reading dist/index.d.ts. · Changelog
npm install twico-uiRequires React 18+ (
reactandreact-domare peer dependencies). Tree-shaking: the package is side-effect-free except*.css, soimport { Button } from "twico-ui"pulls only Button (heavy components like Datatable/Chart drop out) — verified by per-componentsize-limitbudgets in CI. SSR: on React 19 component CSS is hoisted into the SSR stream (no FOUC); on React 18 it injects client-side, so for zero-FOUC SSR use React 19 (see docs/ssr-styles.md).
📖 Documentation, live examples & API reference: https://encryptionl.github.io/twico-ui/
Framework setup
Twico UI works in any React 18+ app. Every component ships with a "use client" boundary, so it drops straight into Next.js App Router Server Components without extra wrapping.
React (Vite / CRA)
Import the stylesheet once at your entry, then use components anywhere:
// main.jsx
import "twico-ui/styles.css";// App.jsx
import { Button, Datatable } from "twico-ui";Next.js — App Router (app/)
Import the CSS once in the root layout. Components can be used directly in Server or Client Components (they self-mark as client):
// app/layout.tsx
import "twico-ui/styles.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}// app/page.tsx (a Server Component — no "use client" needed)
import { Stat, Button } from "twico-ui";
export default function Page() {
return (
<main>
<Stat label="Revenue" value="$48,200" delta="+12.5%" />
<Button>Get started</Button>
</main>
);
}For dark mode in the App Router, toggle the class on <html> from a Client Component (e.g. a theme button) or set it server-side via the className on <html>.
Next.js — Pages Router (pages/)
Import the CSS once in the custom _app:
// pages/_app.tsx
import "twico-ui/styles.css";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}SSR / hydration notes
- Components are SSR-safe — nothing touches
window/documentduring render; all browser access is inside effects and event handlers. - The global stylesheet (
twico-ui/styles.css) provides the design tokens, base reset, and self-hosted fonts at first paint. Each component also injects its own scoped CSS on mount, so styles settle immediately after hydration. - Overlays (Menu, Popover, Select, CommandPalette, Drawer, Dialog) render through React portals to
document.bodyonly while open, so they never run on the server.
TypeScript & JavaScript
Twico UI ships as a dual ESM + CommonJS package with bundled type declarations, so it works identically in a plain JavaScript project or a TypeScript one:
- TypeScript — every component and hook ships full types;
import { Button, type ButtonProps } from "twico-ui"resolves and is fully checked. Verified acrossnode16(CJS and ESM) andbundlerresolution with are-the-types-wrong (npm run check:exports). Thetwico-ui/colorssubpath usesexports, a Node 12.7+ feature, so it requiresnode16-style resolution (the main entry also resolves under legacynode10). - JavaScript (ESM) —
import { Button } from "twico-ui". - JavaScript (CommonJS) —
const { Button } = require("twico-ui").
// TypeScript — full prop types + autocomplete
import { Button } from "twico-ui";
import type { ButtonProps } from "twico-ui";
const props: ButtonProps = { variant: "soft", size: "sm" };Quick start
Import the stylesheet once at your app root, then use any component:
import "twico-ui/styles.css";
import { Button, Datatable, Input, Switch } from "twico-ui";
export default function App() {
return (
<div style={{ padding: 24 }}>
<Input label="Email" placeholder="[email protected]" />
<Button>Save changes</Button>
<Datatable
rows={[
{ id: 1, name: "Jane Cooper", role: "Admin", mrr: 480 },
{ id: 2, name: "Wade Warren", role: "Editor", mrr: 48 },
]}
rowKey={(r) => r.id}
columns={[
{ field: "name", headerName: "Member", width: 200 },
{ field: "role", headerName: "Role", valueOptions: ["Admin", "Editor"] },
{ field: "mrr", headerName: "MRR", type: "number", valueFormatter: (v) => `$${v}` },
]}
/>
</div>
);
}Dark mode
Dark tokens live on the document root. Add class="dark" (or data-theme="dark") to <html>:
document.documentElement.classList.toggle("dark");Everything — including portaled menus, popovers, and the command palette — re-themes together.
Theming
All visuals derive from CSS custom properties. Override them in your own CSS to rebrand without touching components:
:root {
--color-primary: #7c3aed; /* brand color */
--radius-md: 10px; /* corner radius */
--font-sans: "Inter", sans-serif;/* typeface */
}See styles/twico-ui.css for the full token set (colors, typography, spacing, radius, motion). The bundled stylesheet also self-hosts the default fonts (Plus Jakarta Sans + JetBrains Mono, OFL) under twico-ui/styles/fonts/.
Colors in JavaScript
For app theming prefer the semantic CSS variables above. When a token is awkward (charts, canvas, inline maths), the primitive scales are also available as JS — import { red } from '@mui/material/colors'–style:
import { indigo, slate, emerald, amber, rose, sky } from "twico-ui/colors";
const brand = indigo[500]; // "#6366f1"Each hue is an object keyed by shade — a full 50–950 ramp (50, 100, 200, … 900, 950). These are static values — unlike the --color-* aliases, they do not flip in dark mode.
Icons
Components take icons as React nodes (leftIcon, icon, …) — bring any icon set, or inline SVG. For convenience, twico-ui/icons re-exports the full Lucide set (the icon language Twico UI is designed around) plus a curated set of brand icons Lucide omits:
import { HomeIcon, SearchIcon, GithubIcon } from "twico-ui/icons";
<Button leftIcon={<HomeIcon size={16} />}>Home</Button>
<Button variant="outline" leftIcon={<GithubIcon size={16} />}>Star</Button>The Lucide icons need the optional peer lucide-react (npm install lucide-react) and are tree-shakeable; the 31 brand icons (GithubIcon, VercelIcon, FigmaIcon, XTwitterIcon, …) are zero-dependency inline SVG built right in — no peer needed. The core twico-ui package stays zero-dependency. Browse them on the Icons docs page.
Components
Buttons & actions: Button, IconButton Layout: AppShell, Box, Stack, Grid, Container, Divider Typography: Heading, Text, Code Inputs: Input, Textarea, Currency, CurrencyField, Select, MultiSelect, Combobox, Checkbox, Radio, Switch, Slider, Rating, FileUpload, DatePicker, DateRangePicker, ColorPicker Data display: Card, Avatar, AvatarMenu, Badge, Tag, Stat, List, Timeline, Chart, Table, Pagination, Datatable, Kanban Navigation: Tabs, Accordion, Breadcrumb, Stepper, Navbar, Sidebar, TreeView Overlay: Tooltip, Popover, Menu, Dialog, Drawer, CommandPalette Feedback: Alert, Spinner, Progress, Skeleton, Toast (+ ToastViewport), EmptyState, Divider, Carousel
Every component ships full TypeScript types (e.g. import type { DatatableProps } from "twico-ui").
Hooks
The same SSR-safe, fully-typed React hooks the components are built on are exported from the package:
import { useMediaQuery, useDisclosure, useColorScheme, useCopyToClipboard } from "twico-ui";State: useDisclosure, useToggle, useControllableState, useLocalStorage, usePrevious
Responsive & theme: useMediaQuery, usePrefersReducedMotion, useColorScheme, useWindowSize
Events & DOM: useEventListener, useClickOutside, useKeyPress, useHover, useIntersectionObserver, useScrollLock
Timing: useDebouncedValue, useDebouncedCallback, useInterval, useTimeout
Overlay: useFocusTrap, usePortal
Utilities: useCopyToClipboard, useId, useMounted, useIsomorphicLayoutEffect
Building & publishing (maintainers)
This repo is both the Twico UI design system and the source of the npm package. The published package contains only the built output (dist/ + styles/).
npm install # install build tooling (tsup, typescript)
npm run build # bundle src/index.ts -> dist (ESM + CJS + .d.ts)
npm publish # prepublishOnly runs the build automaticallysrc/index.tsis an auto-generated barrel that re-exports every component fromcomponents/.tsupbundles the JSX todist/index.mjs/dist/index.cjsand rolls the hand-written.d.tsprops contracts intodist/index.d.ts.- A post-build step (
scripts/add-use-client.mjs) prepends the"use client"directive to the bundles (a tsupbanneris stripped by esbuild), so the package imports cleanly into Next.js App Router Server Components. react/react-domstay external (peer deps).
See DESIGN-SYSTEM.md for the full design guide (tokens, voice, visual foundations, iconography). Contributor/developer docs (architecture, releases, security, the docs site) live in docs/; the security policy is in SECURITY.md.
Versioning & releases
Releases are fully automated with semantic-release following semantic versioning — there are no manual version bumps.
dev— the integration branch. Day-to-day work happens here or onfeat/*/fix/*branches. Every push and PR runs CI (.github/workflows/ci.yml): type-check, build, and a check that the"use client"banner survived bundling.main— the release branch. Every push tomainruns.github/workflows/release.yml, which computes the next version from the commit history and releases it.
Write Conventional Commits and the version takes care of itself:
| Commit | Example | Release |
| --- | --- | --- |
| fix: | fix: clamp datatable menu to viewport | patch — x.y.Z |
| feat: | feat: add Calendar component | minor — x.Y.0 |
| feat!: / BREAKING CHANGE: | feat!: rename Datatable props | major — X.0.0 |
| docs: / chore: / refactor: / test: | — | no release |
On each push to main, semantic-release picks the next version, publishes to npm (with provenance), tags vX.Y.Z, writes CHANGELOG.md, bumps package.json, and opens a GitHub Release. A push with no release-worthy commits does nothing.
Version stays in sync automatically. On every release the bot commits the bumped
package.json(andCHANGELOG.md) back tomain, so thepackage.jsonversion onmainalways matches the latest version published to npm — they never drift. Requires anNPM_TOKENrepository secret (an npm automation token). If you enable branch protection onmain, allow the release bot to push the changelog commit (or run semantic-release with a personal access token).
License
MIT © Twico UI. Free for any use — personal or commercial.
