@infyniche/ui
v1.3.1
Published
Infyniche design system — generic React primitives.
Maintainers
Readme
@infyniche/ui
Generic React component primitives for Infyniche apps. Domain-agnostic — no ecommerce, no app-specific logic.
import { Button, Input, Card, CardBody } from "@infyniche/ui";
import "@infyniche/ui/styles.css";What's inside (v0.1.0)
| Component | Variants |
|---|---|
| Button | primary · secondary · ghost · danger · outline · sizes sm/md/lg |
| Input | sizes sm/md/lg · invalid state |
| Card + CardHeader / CardTitle / CardBody / CardFooter | composable |
| cn() | tailwind-merge helper, exported for consumers |
| tokens | design tokens as JS values (radius, spacing, duration, easing) |
Theming via CSS custom properties — set --ui-primary, --ui-bg, etc. on any ancestor element.
Local development
pnpm install
pnpm storybook # http://localhost:6006
pnpm dev # tsup watch (rebuilds on save)Other scripts:
pnpm typecheck
pnpm build # produces dist/{index.mjs,index.js,index.d.ts,styles.css}
pnpm build-storybook # static site in storybook-static/
pnpm test # vitest (no tests yet — TODO)Consuming from another repo
.npmrcat repo root of the consumer:@infyniche:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN} always-auth=true- GitHub PAT with
read:packagesscope, exported in your shell:export NODE_AUTH_TOKEN=ghp_xxxxxxxx - Install:
pnpm add @infyniche/ui - Use (Next.js example, in your root layout):
import "@infyniche/ui/styles.css"; - Render:
import { Button } from "@infyniche/ui"; export default function Page() { return <Button variant="primary">Buy now</Button>; }
That's it. No Tailwind setup required in the consumer — the CSS is pre-compiled from this repo's Tailwind into dist/styles.css.
Theming per consumer
Override CSS variables on the root element:
:root {
--ui-primary: 16 185 129; /* emerald */
--ui-radius: 1rem;
--ui-font-sans: "Geist", system-ui;
}Releasing
We use Changesets + GitHub Actions.
Every PR
pnpm changesetPick a bump:
- patch — bug fix, internal refactor, doc-only
- minor — new component, new prop (additive), new variant
- major — removed/renamed prop, changed default, removed component
Commit the generated .changeset/*.md file with your PR.
How a release ships
- Merge PR (with changeset) into
main - The
releaseworkflow opens a "Version Packages" PR aggregating pending changesets - Merging that PR runs
pnpm release→ publishes tohttps://npm.pkg.github.comand tags
Manual release is not supported — always go through CI for an audit trail.
Architecture decisions
Why tsup, not rollup or vite-lib? Library is pure transpile + d.ts emit. tsup is esbuild + dts plugin — one config, ~10× faster than rollup, no dev-server overhead.
Why pre-compile Tailwind into a single CSS file? Consumers don't need Tailwind. Drop in a <link> (or a CSS import) and you're done. Same model as Mantine, Radix Themes.
Why CSS variables for tokens? Themability without rebuilds. Override --ui-primary per route, per tenant, per dark mode. No Sass variable rebuild step.
Why cva for variants? Compile-time variant typing, runtime class composition. Smallest API for "props → classes" mapping that doesn't reinvent CSS-in-JS.
Why no defaults exports? Default exports break tree-shaking and rename-refactors. Always named.
Adding a new component
mkdir src/components/Toggle- Write
Toggle.tsx,Toggle.stories.tsx,index.ts(barrel) - Re-export from
src/index.ts pnpm storybookto verifypnpm changeset→ chooseminor(additive)- Commit, PR, merge — release pipeline handles the rest
