{"_id":"@arbingerinstitute/ui","name":"@arbingerinstitute/ui","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@arbingerinstitute/ui","version":"0.0.1","description":"Arbinger's shared React component library and semantic theme system.","license":"MIT","type":"module","sideEffects":["**/*.css"],"repository":{"type":"git","url":"git+https://github.com/The-Arbinger-Institute/Arbinger.git","directory":"packages/ui"},"homepage":"https://github.com/The-Arbinger-Institute/Arbinger/tree/main/packages/ui#readme","bugs":{"url":"https://github.com/The-Arbinger-Institute/Arbinger/issues"},"keywords":["arbinger","design-system","react","tailwind","components","theming"],"publishConfig":{"access":"public"},"exports":{"./styles.css":"./dist/styles.css","./theme.css":"./dist/theme.css","./fonts.css":"./dist/fonts.css","./themes/index.json":"./dist/themes/index.json","./themes/*.css":"./dist/themes/*.css","./theme/*":{"types":"./dist/theme/*.d.ts","default":"./dist/theme/*.js"},"./utils":{"types":"./dist/lib/utils.d.ts","default":"./dist/lib/utils.js"},"./*":{"types":"./dist/components/*.d.ts","default":"./dist/components/*.js"}},"peerDependencies":{"react":">=19","react-dom":">=19"},"dependencies":{"@radix-ui/react-avatar":"^1.1.11","@radix-ui/react-checkbox":"^1.3.3","@radix-ui/react-dialog":"^1.1.15","@radix-ui/react-label":"^2.1.7","@radix-ui/react-popover":"^1.1.15","@radix-ui/react-progress":"^1.1.8","@radix-ui/react-radio-group":"^1.3.8","@radix-ui/react-separator":"^1.1.8","@radix-ui/react-slot":"^1.2.4","@radix-ui/react-switch":"^1.2.6","@radix-ui/react-tabs":"^1.1.13","@radix-ui/react-tooltip":"^1.2.8","@tanstack/react-virtual":"^3.13.18","class-variance-authority":"^0.7.1","clsx":"^2.1.1","tailwind-merge":"^3.5.0","zustand":"^5.0.11"},"devDependencies":{"@biomejs/biome":"2.4.4","@tailwindcss/cli":"^4.2.0","@tailwindcss/vite":"^4.2.0","@testing-library/dom":"^10.4.0","@testing-library/jest-dom":"^6.6.3","@testing-library/react":"^16.1.0","@testing-library/user-event":"^14.5.2","@types/node":"^25.3.0","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@vitejs/plugin-react":"^5.1.4","jsdom":"^29.0.1","tailwindcss":"^4.2.0","tsup":"^8.5.0","tsx":"^4.19.3","typescript":"^5.9.2","vite":"^8.2.0","vitest":"^4.1.10"},"scripts":{"build":"tsup && tsx scripts/buildAssets.ts && pnpm build:css","build:css":"tailwindcss --input scripts/styles.entry.css --output dist/styles.css --minify","fonts":"python3 scripts/fetchFonts.py","playground":"vite --config playground/vite.config.ts","playground:build":"vite build --config playground/vite.config.ts","check:contrast":"tsx scripts/checkContrast.ts","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"biome check","format":"biome check --write"},"_id":"@arbingerinstitute/ui@0.0.1","_integrity":"sha512-qV2MEGyV6ANqblVZnXbIlBHs7xBkTWWZvzB3qrYxoPj62IgJxbnd/zyLEfkvlhSYlTcBBLFKwkd/QV7Y/1/c7g==","_resolved":"/tmp/26bfec160fad55986717033c43397939/arbingerinstitute-ui-0.0.1.tgz","_from":"file:arbingerinstitute-ui-0.0.1.tgz","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-qV2MEGyV6ANqblVZnXbIlBHs7xBkTWWZvzB3qrYxoPj62IgJxbnd/zyLEfkvlhSYlTcBBLFKwkd/QV7Y/1/c7g==","shasum":"43415d3064ec922994ddfbbe250fd06da1e5809d","tarball":"https://registry.npmjs.org/@arbingerinstitute/ui/-/ui-0.0.1.tgz","fileCount":114,"unpackedSize":350855,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEgHKFjBv4IAIbCjpE3wZSrX/yseKGImOY6u7m2PuCZUAiAxf0IQIxpvMXvSSnHTRKInGj0w3SzDClVlFdeKwpWqXw=="}]},"_npmUser":{"name":"arbingeraustin","email":"awilcox@arbinger.com"},"directories":{},"maintainers":[{"name":"arbingeraustin","email":"awilcox@arbinger.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ui_0.0.1_1788280732068_0.1438692296398978"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-01T16:38:51.872Z","0.0.1":"2026-09-01T16:38:52.207Z","modified":"2026-09-01T16:38:52.428Z"},"maintainers":[{"name":"arbingeraustin","email":"awilcox@arbinger.com"}],"description":"Arbinger's shared React component library and semantic theme system.","homepage":"https://github.com/The-Arbinger-Institute/Arbinger/tree/main/packages/ui#readme","keywords":["arbinger","design-system","react","tailwind","components","theming"],"repository":{"type":"git","url":"git+https://github.com/The-Arbinger-Institute/Arbinger.git","directory":"packages/ui"},"bugs":{"url":"https://github.com/The-Arbinger-Institute/Arbinger/issues"},"license":"MIT","readme":"# @arbingerinstitute/ui\n\nArbinger's shared component library and theme system. One semantic token\ncontract, many themes, zero `dark:` classes.\n\n## Why this exists\n\nThe previous library (`@arbingerinstitute/ui-legacy`) uses 191 distinct color\nutilities named after positions on a palette ramp — `bg-neutral-700`,\n`text-primary-1000` — plus 1,658 hand-written `dark:` variants across the repo.\nThose names carry no meaning to translate, so a second theme has to be authored\none class at a time, and a third is impractical.\n\nThis package inverts that. Components reference **43 semantic tokens**. A theme\nis a flat list of those 34 values in a plain CSS file. Adding Gruvbox costs one\n57-line file and no component changes.\n\n## Quick start\n\n```bash\npnpm add @arbingerinstitute/ui\n```\n\nIf the app does **not** use Tailwind, that is the whole integration:\n\n```ts\nimport \"@arbingerinstitute/ui/styles.css\"\nimport { Button } from \"@arbingerinstitute/ui/Button\"\n```\n\n`styles.css` is precompiled and carries every utility the components use plus all\nfour themes. If the app **does** run Tailwind v4, import `theme.css` and the\nthemes you want instead, and add\n`@source \"../node_modules/@arbingerinstitute/ui/dist\";` — Tailwind does not scan\n`node_modules`, and without that line the components render unstyled.\n\nSee [AGENTS.md](./AGENTS.md) for the full component list, the token vocabulary,\nself-hosted fonts, and the flash-of-wrong-theme script. That file is written for\ncoding agents, and it is also the fastest reference for people.\n\n## How theming works\n\n`theme.css` declares the contract in a Tailwind v4 `@theme static` block, so\n`bg-surface` compiles to `background-color: var(--color-surface)`. Each theme\nfile overrides those variables under `html[data-theme=\"name\"]`, and the\n`data-theme` attribute is the only switch.\n\nTwo details that are easy to get wrong:\n\n- **Theme files must stay unlayered.** Tailwind emits `@theme` defaults inside\n  `@layer theme`, and unlayered rules beat layered ones regardless of\n  specificity. Wrapping a theme in `@layer` makes the override silently lose.\n- **Never use `@theme inline` for a themeable token.** It inlines the literal\n  value instead of emitting a `var()`, which kills runtime switching. shadcn's\n  v4 template uses `inline`, so this is easy to copy by accident.\n\nBecause a theme is plain CSS with no build step, a designer or a customer can\nauthor one without touching TypeScript.\n\n## Accessibility\n\n`pnpm check:contrast` validates every theme against the contract and against 17\nWCAG AA pairs — body text, secondary text, placeholders, links, button labels,\ncontrol borders, focus rings, and all four status tones in both solid and tinted\nform.\n\nIt is a build gate, not a linter, because a theme is data and data gets\ncontributed. It has already caught genuine problems in the existing brand\npalette — failing body text, unusable placeholder greys, and low-contrast control\nborders, all now fixed.\n\nIt also carries one **accepted exception**. White button labels on the brand cyan\nare 2.78:1, below the 4.5:1 AA needs for text. That is a deliberate brand\nrequirement, and it matches what the Outward app already ships, so it is recorded\nin `ACKNOWLEDGED_EXCEPTIONS` rather than silenced: the reason prints on every run,\nand the check still fails if the ratio ever drops *below* the accepted 2.78:1.\n\nAdding an entry to that list is a deliberate act requiring a reason. It is not a\nway to make a new theme pass.\n\n## Development\n\n```bash\npnpm playground        # every component, live theme switcher, port 5180\npnpm test              # 50 unit tests\npnpm check:contrast    # token contract + WCAG gate\npnpm typecheck\npnpm lint\npnpm build             # tsup ESM + .d.ts, assets, compiled styles.css\npnpm fonts             # re-fetch Montserrat from Google and regenerate fonts.css\n```\n\nThe playground is a falsification tool: if a component hardcodes a color,\nswitching to Gruvbox makes it obvious in one click.\n\n## Releasing\n\nBump the version in `package.json`, then tag `ui-v<version>`:\n\n```bash\ngit tag ui-v0.0.1 && git push origin ui-v0.0.1\n```\n\nThe tag prefix is deliberate — the repo already uses bare semver tags for the\nportal and API deploys, so an unprefixed tag would collide. It also extends to the\nother packages later (`feature-flags-v1.2.0`) without them sharing a namespace.\n\n`UI-Publish` checks the tag against the manifest, runs typecheck, lint, the\ncontrast gate and the tests, then builds, packs and publishes.\n`workflow_dispatch` offers a dry run that packs without publishing. Requires an\n`UI_NPM_TOKEN` secret — a granular npm token scoped to the `@arbingerinstitute`\norg. The workflow checks it is present before doing any work.\n\nThe token needs **Bypass 2FA** enabled, which npm leaves unchecked by default.\nWithout it the publish runs the whole build and then dies on `npm error code\nEOTP`, asking for an authenticator code no CI run can supply. Do not try to\nautomate the code itself — the TOTP seed would have to live in CI beside the\ntoken, which is the second factor, and npm is phasing TOTP out regardless.\n\nTwo clocks on this. npm caps write-capable granular tokens at 90 days, so the\nsecret needs re-cutting every quarter. And direct publishing from a bypass-2FA\ntoken is deprecated as of January 2027, after which such a token can only *stage*\na publish for a human to approve. The exit from both is npm trusted publishing —\nOIDC, no stored token — which needs `id-token: write` adding back to the workflow.\n\nProvenance is deliberately off. npm will only verify a provenance bundle whose\nsource repository is public, and rejects one from a private repo with `E422\nUnsupported GitHub Actions source repository visibility: \"private\"`. It stays off\nuntil this repo is public, so the published tarball carries no provenance\nattestation and no npm \"Built and signed\" badge.\n\n## Licensing\n\n**MIT** — see [LICENSE](./LICENSE). Copyright (c) 2026 The Arbinger Institute.\n\nThe licence covers this package only. The surrounding monorepo is not MIT, and\npublishing an MIT package from it does not relicense anything else in the repo.\n\nThird-party notices live in [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md)\nand ship in the published tarball. Two things there are worth knowing:\n\n- **Montserrat is redistributed.** The woff2 binaries are inside the package, so\n  the SIL Open Font License travels with them at `dist/fonts/OFL.txt`. Both the\n  build and CI fail if it goes missing.\n- **Radix is a dependency, not vendored.** Every Radix reference in `dist/` is a\n  bare import specifier, so no third-party source is inlined and each Radix\n  package delivers its own MIT notice on install. The component *structure* comes\n  from shadcn/ui, which is credited as the design origin.\n\n## Status\n\n`0.0.1` — early. The token names are the public API and are not yet frozen;\nexpect churn before `0.1.0`.\n\nVerified against real consumers: a Vite app with no Tailwind at all (precompiled\n`styles.css`), a Vite app running Tailwind v4, and a webpack + babel-loader app\nwith the conventional `exclude: /node_modules/`. Types resolve and prop unions are\nenforced in all three.\n","readmeFilename":"README.md","_rev":"1-26ad48a596a482055fcc4773c0761f59"}