npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jlist/choiceui

v0.14.0

Published

Governance for design systems that live as code. Extract a manifest of your Tailwind tokens and components, enforce it for humans and AI, and generate browsable design-system pages.

Readme

choiceui

The command-line tool for ChoiceUI. It makes your design system, real Tailwind components in your repo, the enforced source of truth for every consumer of the codebase, human or AI.

You point it at a repo. It extracts a manifest of your tokens and components, generates enforcement (a Claude Code skill, CLAUDE.md rules, an ESLint gate, and a CI check), builds browsable design-system pages, and serves your components as a private shadcn registry. After that, the codebase cannot drift from the design system without CI catching it.

Prerequisites

ChoiceUI governs Tailwind design systems that live as code. Before you start, your project should have:

  • [ ] Tailwind CSS (v3 with a tailwind.config.*, or v4 configured in CSS with @import "tailwindcss" / @theme).
  • [ ] A global stylesheet where your tokens live, at one of: src/app/globals.css, app/globals.css, styles/globals.css, packages/ui/src/globals.css, src/globals.css, or globals.css. Colors are read from @theme as either numbered scales (--color-primary-500) or single-value semantic tokens (--color-primary, --color-muted, oklch/hsl/rgb, var() indirection). Files your stylesheet @imports are followed too, so a globals.css that imports a theme.css works.
  • [ ] A components directory (e.g. src/components/ui or components/ui). Components using class-variance-authority (cva) or tailwind-variants (tv) get their variants captured; others are still tracked. shadcn matches all of this out of the box, but it is not required.
  • [ ] A @/ path alias in tsconfig.json (recommended), since the default component import path is @/components/ui.

init checks all of these and tells you what is missing.

Install

npm install -g choiceui
# or run without installing
npx choiceui <command>

Quick start

From your project root, run these in order. Each step is independently useful.

# 1. Inspect the project and generate a real manifest from your tokens/components
npx choiceui init

# 2. (init already generated it, but regenerate any time your design changes)
npx choiceui manifest

# 3. Generate the enforcement kit: Claude skill, CLAUDE.md, ESLint config, CI
npx choiceui guard

# 4. Generate browsable design-system pages into your app (visit /design-system)
npx choiceui showcase

# 5. Generate an installable shadcn registry under public/r/
npx choiceui registry

# Anytime: scan for off-system code
npx choiceui doctor

After guard, wire the generated ESLint config into your root config and install the parser it needs:

npm install --save-dev typescript-eslint
// eslint.config.mjs
import choiceui from './choiceui.eslint.config.mjs'
export default [ ...yourConfig, ...choiceui ]

Commands

choiceui init

Inspect your project and set up ChoiceUI against your real design system.

npx choiceui init
npx choiceui init --yes --skip-link          # accept detected defaults
npx choiceui init --components src/ui         # point at a components directory
npx choiceui init --css src/styles/theme.css  # point at your token file directly
npx choiceui init --project-id abc123         # link a cloud project

Monorepos: run it in the package that holds your components (e.g. packages/ui). If your tokens are in a non-standard file, pass --css; guard writes its CI workflow to the repo root using your package manager (pnpm/yarn/ npm), and showcase renders into a real app (a sibling apps/* if the current package has no app router).

Detects Tailwind, the global CSS, the @/ alias, and the components directory, then writes choiceui.config.ts and generates choiceui.manifest.json. Exits with a clear message on an unsupported setup (no Tailwind, or no global CSS).

choiceui manifest

Generate or validate choiceui.manifest.json, the source of truth.

npx choiceui manifest              # (re)generate from the codebase
npx choiceui manifest --check      # fail (exit 1) if code has drifted from it
npx choiceui manifest --push       # upload to the ChoiceUI cloud (needs login)

choiceui intake

Govern components that exist in the code but are not yet in the design system. When ChoiceUI sees an ungoverned component, it makes you decide: adopt it into the system, allow it as a deliberate one-off, or reject it (must be replaced). Decisions persist in .choiceui/decisions.json and stick.

npx choiceui intake              # interactive: decide each ungoverned component
npx choiceui intake --check      # CI: exit 1 if anything is undecided or rejected-but-present
npx choiceui intake --json       # emit the list for an agent to decide in bulk

Adopted components enter the manifest on the next choiceui manifest. This is the governance loop: nothing drifts off to the side unnoticed.

In a monorepo, run it from the repo root and point it at the design system's manifest, so components in the apps that consume the system get triaged too:

npx choiceui intake --check --manifest packages/ui/choiceui.manifest.json

The managed set still comes from the package's own manifest; only the scan widens. Decisions are read from both packages/ui/.choiceui/decisions.json and the repo root, and each new one is written beside the component it is about, so a repo that triaged its system before never loses those decisions. choiceui guard generates this form of the CI step automatically.

choiceui guard

Generate the enforcement kit from the manifest.

npx choiceui guard
npx choiceui guard --only eslint,ci          # a subset: skill, claudemd, eslint, ci
npx choiceui guard --dry-run                 # preview without writing

Writes .claude/skills/choiceui-design-system/SKILL.md, a marker-delimited block in CLAUDE.md, choiceui.eslint.config.mjs, .github/workflows/choiceui-guard.yml, and a .mcp.json entry pointing AI tools at the MCP server below (merged into any existing .mcp.json).

choiceui mcp

Run the design-system MCP server (stdio) so AI tools like Claude Code and Cursor can query the manifest directly while they generate code, instead of being caught by the gate afterward.

npx choiceui mcp

You normally do not run this by hand: choiceui guard writes the .mcp.json that starts it automatically. It exposes four tools: list_components, get_component, list_tokens, and check_classes (validate a Tailwind class string against the design system). All read the same choiceui.manifest.json.

choiceui showcase

Eject a complete, branded design-system page you own, then vibe-code it.

npx choiceui showcase                        # eject into <app>/design-system
npx choiceui showcase --route src/app/ds     # custom route
npx choiceui showcase --force                # re-eject from scratch (discards your edits)

Eject and own. The page, shell, live demos, and styles land in your repo and are yours from that moment. Re-running showcase never overwrites them (it prints kept (yours)); it only refreshes manifest.data.ts, the inventory the page maps over at runtime. So a newly captured token or component appears on its own, without touching anything you vibe-coded. The guard lints the page like any UI code, so you cannot drift off-token while restyling it. Want the original scaffold back? Re-run with --force.

Make it yours. Drop a .choiceui/showcase.json to brand the page with your logo, title, version, a light/dark toggle, and your own written descriptions. ChoiceUI carries your words; you write them. (A logo is also captured automatically from a brand/ Logo component or a public/logo.* asset; this config overrides it.)

{
  "title": "Acme Design System",
  "version": "v1.0",
  "logo": "/logo.svg",          // asset path or URL, shown in the header + hero
  "themeToggle": true,           // adds a light/dark toggle
  "lede": "Every component here is a live import from @acme/ui.",
  "descriptions": {              // per-section and per-component prose
    "color": "Grouped by purpose: surfaces, brand, semantic.",
    "typography": "Inter on the editorial scale.",
    "Button": "All variants and sizes."
  }
}

Start your dev server and visit /design-system. Every captured component gets a section with its variant table (read from the data module at runtime). Live demos live in the owned previews.tsx: simple primitives are scaffolded automatically, and common shadcn compounds (Dialog, Select, Tabs, DropdownMenu, Sheet, Popover, Tooltip) come from built-in demo recipes. Add or restyle any demo by editing previews.tsx directly, or seed one at eject time by dropping a TSX snippet at .choiceui/demos/<ComponentName>.tsx (it must reference only that component's own exports). A component with no demo still appears, with an import hint until you add one.

choiceui registry

Generate an installable shadcn-compatible registry into public/r/.

npx choiceui registry

Consumers then run npx shadcn@latest add https://your-app.com/r/<name>.json, or any AI tool with the shadcn MCP can read the registry index.

choiceui doctor and choiceui reconcile

npx choiceui doctor                # scan for off-system code
npx choiceui doctor --fix          # apply safe, token-aware fixes
npx choiceui reconcile             # guided 3-tier fix (deterministic, AI, ask)

Fixes follow your manifest's real token names. A value that cannot be mapped confidently is left for review rather than guessed.

Cloud (optional)

npx choiceui login                 # authenticate
npx choiceui link --project-id id  # link this repo to a cloud project

Login gates only the cloud sync and the drift dashboard. Everything above works fully offline.

Configuration (choiceui.config.ts)

import { defineConfig } from '@choiceui/core/preset';

export default defineConfig({
  // projectId: 'abc123',                  // optional cloud link
  css: 'src/app/globals.css',              // global stylesheet with your tokens
  components: {
    importPath: '@/components/ui',          // how consumers import your components
  },
  // Custom or monorepo layouts: where your components live.
  componentPaths: ['packages/ui/src/components', 'apps/web/components/ui'],
});

Tokens and components are extracted into choiceui.manifest.json. Treat that file as generated: regenerate it with choiceui manifest rather than editing it.

Framework support

The one hard requirement is Tailwind. shadcn is the smoothest case because it matches every convention, but any Tailwind project with a global stylesheet and a components directory works.

| Setup | Status | | --- | --- | | Next.js App Router + Tailwind + shadcn | Tested | | Tailwind app with semantic (oklch) tokens, no numbered scales | Tested | | Monorepo where globals.css imports a shared theme.css | Tested | | Tailwind v4 (CSS @theme) and v3 (tailwind.config.*) | Tested | | Next.js Pages Router | Should work (untested) | | Vite / Remix + Tailwind | Should work for manifest/guard/doctor; showcase emits Next.js pages (untested) | | Non-Tailwind projects | Not supported |

Troubleshooting

  • Empty manifest / "No design tokens". No global CSS was found, or it has no @theme colors. Confirm your stylesheet is at one of the prerequisite paths. Tokens can be numbered scales (--color-primary-500) or single-value semantic tokens (--color-primary); files you @import are followed. If your tokens live in a non-standard file, pass it: choiceui manifest --css <path>.
  • "No UI components discovered". Your components are not in a default location. Set componentPaths in choiceui.config.ts (the message lists every path that was searched).
  • ESLint gate crashes on .tsx / "Parsing error: Unexpected token <". The generated config needs a TypeScript parser: run npm install --save-dev typescript-eslint, and make sure the config is spread into your root eslint.config.mjs.
  • manifest --check fails in CI. A tracked component changed. Re-run choiceui manifest to re-baseline, review the diff, and commit it.

How it stays governed

  1. choiceui.manifest.json is the contract: tokens, components, and a source hash per component.
  2. At authoring time, the generated Claude skill and CLAUDE.md rules steer humans and AI agents to reuse components and semantic tokens, and the MCP server lets AI tools query the manifest directly (what exists, what is allowed) as they generate code.
  3. At PR time, the generated GitHub Actions workflow runs choiceui manifest --check (structural drift) and the ESLint gate (off-system classes), failing the build on drift.

License

MIT