A shadcn registry serving the Crowdin design tokens and a curated, pinned mirror of shadcn/ui components. Component source is pristine upstream shadcn; all Crowdin styling lives in the theme, style, and app-theme items.
Add the registry to your components.json:
{
"registries": {
"@crowdin": "https://crowdin.github.io/ui-registry/r/{name}.json"
}
}Install the Crowdin style (theme tokens + base CSS), then components:
npx shadcn@latest add @crowdin/style
npx shadcn@latest add @crowdin/button @crowdin/dialogThe registry serves the latest release - changes on main are not published until a release is made.
@crowdin/theme ships the canonical Crowdin design tokens as plain values - override them in your own CSS like any shadcn theme.
Apps that run inside the Crowdin iframe should also install @crowdin/app-theme. It re-points the tokens at the host --crowdin-* variables and keeps the canonical values as fallbacks so the app still looks right in local development outside the iframe:
npx shadcn@latest add @crowdin/style # standalone app
npx shadcn@latest add @crowdin/style @crowdin/app-theme # app embedded in CrowdinCSS variables never cross the iframe boundary, so @crowdin/app-theme also installs the host bridge that pulls the palette from the host: lib/crowdin-host.ts (framework-agnostic) and hooks/use-crowdin-theme.ts (React). Wire it once near your app root - it applies the host --crowdin-* variables to <html>, tracks host theme changes, and no-ops outside the iframe:
const mode = useCrowdinTheme() // "light" | "dark" - toggle your .dark class off thisThe app must load the Crowdin iframe SDK (https://cdn.crowdin.com/apps/dist/iframe.js) for the bridge to find the AP global; an async script tag is fine - the bridge probes for AP for ~10 seconds after mount before concluding it runs outside the iframe.
npx shadcn@latest add @crowdin/button --diff
npx shadcn@latest add @crowdin/button --overwriteThe registry works with the shadcn MCP server:
npx shadcn@latest mcp init --client claudepnpm install
pnpm test # unit tests for the sync helpers and theme/bridge invariants
pnpm sync # refresh registry/src/ from upstream shadcn (open the diff as a PR)
pnpm validate # schema-validate registry.json
pnpm build # emit r/*.json
pnpm smoke # install everything into a fixture app, typecheck, compile CSS
pnpm site:dev # run the component showcase locally (one-time: pnpm --dir site install)
pnpm site:build # typecheck and build the showcaseregistry/src/** is generated by pnpm sync - never edit it by hand. Crowdin styling changes go into the theme/style/app-theme items in registry.json. registry/crowdin/** is hand-written (the host-bridge sources shipped by app-theme) and survives sync.
To refresh the components from upstream shadcn, run the Sync upstream workflow from the Actions tab - it syncs, runs the full check suite, and opens a PR with the diff (only when something actually changed). New upstream components are opt-in: add the name to scripts/upstream-items.json first.
The showcase at crowdin.github.io/ui-registry is built from site/ and renders registry/src/** directly with the generated Crowdin theme - use its header toggles to preview dark mode and simulated Crowdin host variables.