Installation
The packages are published to the GitHub Packages npm registry under the @elirobinson scope.
Authenticate once, install two packages, import two stylesheets — that's the whole setup.
Requirements
- Node 24 or later
- React 19
- A package manager — the commands below use pnpm; npm and yarn work the same way
Authenticate with GitHub Packages
GitHub Packages requires a token even for public reads. Create a
personal access token with the read:packages scope,
then point the @elirobinson scope at the registry with an .npmrc next to your app's
package.json:
@elirobinson:registry=https://npm.pkg.github.comThe token itself goes in your user-level npmrc, not the one above — pnpm 10 ignores
registry credentials in a project .npmrc (that file is usually committed, so expanding a
token into it risks leaking the secret to another registry):
pnpm config set "//npm.pkg.github.com/:_authToken" <your-github-pat>That writes the token to ~/.npmrc, which stays out of your repo. In CI, prefer a step
that generates the npmrc from a secret — actions/setup-node with
registry-url: 'https://npm.pkg.github.com' does this for you, reading NODE_AUTH_TOKEN.
If you use npm or yarn instead, the older //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
line in a project .npmrc still works — only pnpm rejects it.
Install the packages
pnpm add @elirobinson/tokens @elirobinson/react@elirobinson/tokens carries the design tokens as CSS custom properties and JSON.
@elirobinson/react carries the 50 components and 7 interaction
hooks.
Two more are opt-in, and both are about building AI features.
@elirobinson/ai-patterns adds prompt patterns, machine-checkable UX contracts, and a
server entry that wraps AI SDK Core with the house voice —
Build with AI covers it. @elirobinson/ai-elements is the assistant tier:
conversation logs, prompt inputs and tool panels, vendored from Vercel's AI Elements at a
pinned release. It is a separate package because it requires Tailwind 4, which the two above
do not — see AI Elements.
Wire up the styles
Import both stylesheets once, in your app shell, in this order — tokens first, then component styles:
import '@elirobinson/tokens/tokens.css';
import '@elirobinson/react/styles.css';tokens.css defines every custom property on :root plus sensible base styles (focus
rings, selection color, reduced-motion handling). styles.css is the aggregate component
stylesheet. If you'd rather ship only what you use, each component's sheet is importable on
its own:
import '@elirobinson/react/styles/atoms/Button.css';Override a token
tokens.css is unlayered, on purpose. Unlayered declarations beat anything inside a
cascade layer whatever the order, so an override written inside @layer base — the
conventional place for base styles in a Next.js globals.css — will not apply, and
nothing will warn you. Write it in a plain :root block outside any layer:
:root {
--accent: oklch(72% 0.18 60);
}Fonts, when the framework supplies them
next/font never exposes a family under its real name — it generates a hashed one
(__Geist_e8ce0c) and hands it over in a CSS variable, so the literal 'Geist' in
tokens.css matches nothing it loaded and the page silently renders in the system font.
The three family tokens read an override first, so one line per family fixes it:
:root {
--ds-font-sans-override: var(--font-geist-sans);
--ds-font-mono-override: var(--font-geist-mono);
}Put the font's class on <html>, not <body>: the tokens resolve at :root, so that is
where the framework's variable has to be visible. These three hooks are the one thing you
can set from inside a layer — nothing in tokens.css declares them, so there is nothing
for your override to lose to.
Import components
There are no barrel files — every import names a subpath. A bare
import { Button } from '@elirobinson/react' does not resolve, by design: subpath imports
keep bundles honest and make every dependency visible.
import { Button } from '@elirobinson/react/components/atoms/Button';
import { Card, CardHeader, CardTitle } from '@elirobinson/react/components/molecules/Card';
import { useRovingFocus } from '@elirobinson/react/hooks/useRovingFocus';Start from scratch instead
If you don't have an app yet, the generator scaffolds a Next.js App Router project already
wired to the tokens, the components, and a GitHub Packages .npmrc:
npx github:EliRobinson/design-system/packages/create-elirobinson-design-system my-appNext step
Bringing an existing app onto the system? Read Adopting the system — it covers the order that works: primitives first, then token values, then keyboard and focus checks.