@tenminuteschool/design-system
v0.3.0
Published
10 Minute School UI component library & design system.
Readme
@tenminuteschool/design-system
10 Minute School's UI component library.
Built with React 19, TypeScript, Tailwind CSS v4, and shadcn/ui (Base UI
primitives). Published to npm as @tenminuteschool/design-system for consumption across
micro-frontends.
This repo contains only the component library source. Docs and the component workbench (Storybook) live in a separate repository.
Local development
pnpm installAdding components
pnpm dlx shadcn@latest add <component> # e.g. dialog, input, cardThen export it from src/components/index.ts (the published API surface).
Building & publishing
pnpm build:lib # tsup (JS + .d.ts, preserves "use client") + Tailwind CSS → dist/
npm version patch # bump version + create a vX.Y.Z git tag
git push --follow-tagsPushing a v*.*.* tag triggers .github/workflows/publish.yml, which builds and
publishes to npm. The workflow needs an NPM_TOKEN repo secret (npm automation token
with publish access to the @tenminuteschool org).
Consuming in a micro-frontend
pnpm add @tenminuteschool/design-systemimport { Button } from "@tenminuteschool/design-system";
import "@tenminuteschool/design-system/styles.css"; // pre-compiled design tokens + utilities
export default function Example() {
return <Button variant="secondary">Hello</Button>;
}react / react-dom are peer dependencies, so the host app provides them — no
duplicate React in the bundle. next is an optional peer dependency, needed only if
you use the components that wrap Next.js APIs (OptimizedImage, Upload,
BackButton).
Data-fetching components
AdminUserCombobox, BatchCombobox, BranchCombobox, and ProgramCombobox fetch
their own options list. This package ships no built-in API routes or auth — you
supply your own backend:
<BranchCombobox
endpoint="https://api.example.com/branches"
headers={{ Authorization: `Bearer ${token}` }}
value={branchId}
onValueChange={setBranchId}
/>endpoint(required) — full URL of the list endpoint. The component appends?page=1&limit=...&search=...query params and expects a JSON body shaped like{ data: { items: [{ id, name }] } }.headers(optional) — sent with every request; use it forAuthorizationor any other auth your API needs.byIdEndpoint(optional, onAdminUserCombobox/BatchCombobox/ProgramCombobox) — builds the "fetch one item by id" URL used to resolve a selected value that isn't on the current page. Defaults to`${endpoint}/${id}`.
If your API doesn't match this shape, pass options (and onSearch/isSearching)
instead to fully control data fetching yourself.
