{"_id":"@banaris/design-system","name":"@banaris/design-system","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@banaris/design-system","version":"0.1.0","description":"Banaris design system — design tokens and React UI primitives built on Base UI and Tailwind CSS.","license":"MIT","type":"module","repository":{"type":"git","url":"git+https://github.com/banaris/design-system.git"},"homepage":"https://github.com/banaris/design-system","bugs":{"url":"https://github.com/banaris/design-system/issues"},"keywords":["design-system","design-tokens","react","base-ui","tailwindcss"],"sideEffects":["**/*.css"],"exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs","default":"./dist/index.mjs"},"./styles.css":"./dist/styles.css","./theme.css":"./src/styles/theme.css","./base.css":"./src/styles/base.css","./components.css":"./src/styles/components.css","./package.json":"./package.json"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"scripts":{"build":"run-s build:js build:css","build:js":"tsdown","build:css":"tailwindcss -i src/styles/styles.src.css -o dist/styles.css","storybook":"storybook dev -p 6006","build-storybook":"storybook build -o storybook-static","preview-storybook":"run-s build-storybook && wrangler dev","lint":"run-p --continue-on-error \"lint:*\"","lint:eslint":"eslint .","lint:knip":"knip","lint:prettier":"prettier --check .","lint:typecheck":"run-p --continue-on-error typecheck:src typecheck:node","typecheck:src":"tsc --noEmit","typecheck:node":"tsc --noEmit -p tsconfig.node.json","fix":"run-s --continue-on-error fix:eslint fix:prettier","fix:eslint":"eslint . --fix","fix:prettier":"prettier --write .","check:contrast":"node scripts/check-contrast.mjs","check:geometry":"node scripts/check-geometry.mjs","check:axe-live":"node scripts/check-axe-live.mjs","check:tw-merge":"node scripts/check-tw-merge.mjs","check:stories":"node scripts/check-stories.mjs","check:styles":"node scripts/check-styles.mjs","check:package":"node scripts/check-package.mjs","test":"vitest run --project=storybook","verify":"run-s lint check:contrast check:geometry check:tw-merge check:stories build check:styles check:package","verify:full":"run-s verify test check:axe-live","changeset":"changeset","prepare":"lefthook install"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"dependencies":{"@base-ui/react":"^1.7.0","tailwind-merge":"^3.6.0"},"devDependencies":{"@changesets/changelog-github":"^1.0.0","@changesets/cli":"^3.0.0","@eslint/compat":"^2.0.5","@eslint/js":"^10.0.1","@storybook/addon-a11y":"^10.5.8","@storybook/addon-docs":"^10.5.8","@storybook/addon-vitest":"^10.5.8","@storybook/react-vite":"^10.5.8","@tailwindcss/cli":"^4.3.3","@tailwindcss/vite":"^4.3.3","@types/node":"^24.0.0","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@vitest/browser-playwright":"^4.1.10","eslint":"^10.2.1","eslint-plugin-jsx-a11y":"^6.10.2","eslint-plugin-react-hooks":"^7.1.1","eslint-plugin-simple-import-sort":"^14.0.0","eslint-plugin-storybook":"^10.5.8","eslint-plugin-unused-imports":"^4.4.1","globals":"^17.5.0","knip":"^6.32.0","lefthook":"^2.1.6","npm-run-all2":"^9.0.0","playwright":"^1.59.1","prettier":"^3.8.3","prettier-plugin-tailwindcss":"^0.8.0","react":"^19.2.5","react-dom":"^19.2.5","storybook":"^10.5.8","tailwindcss":"^4.3.3","tsdown":"^0.22.14","typescript":"^6.0.3","typescript-eslint":"^8.59.1","vite":"^8.0.10","vitest":"^4.1.10","wrangler":"^4.110.0"},"gitHead":"43e77779048aa9f2532ddacb4f6de5a9fc5ac79f","_id":"@banaris/design-system@0.1.0","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-1R5wn4SbnZNoLObeewOkzoaRf3d9HAWDWC729UJSrud75f0X7YiNaynUc/m5I/P3J9fxbiA4aDEHgdTZBD7Q0w==","shasum":"8cce1f00334937645a3d95e826f655f2bda7d008","tarball":"https://registry.npmjs.org/@banaris/design-system/-/design-system-0.1.0.tgz","fileCount":11,"unpackedSize":86449,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQClLxDkKw6FoMEWzfE2Ix957GK5/ifxntvGbzna/2EvkgIgLsR/omETFdEngyjYqHKva80WueSC4tg2SYsJ9wLvJPI="}]},"_npmUser":{"name":"nemolize","email":"nemolize@gmail.com"},"directories":{},"maintainers":[{"name":"bangosensei","email":"masaru.nemoto@banaris.co.jp"},{"name":"nemolize","email":"nemolize@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/design-system_0.1.0_1786973364774_0.7859687627072878"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-17T13:29:24.642Z","0.1.0":"2026-08-17T13:29:24.934Z","modified":"2026-08-17T13:29:25.188Z"},"maintainers":[{"name":"bangosensei","email":"masaru.nemoto@banaris.co.jp"},{"name":"nemolize","email":"nemolize@gmail.com"}],"description":"Banaris design system — design tokens and React UI primitives built on Base UI and Tailwind CSS.","homepage":"https://github.com/banaris/design-system","keywords":["design-system","design-tokens","react","base-ui","tailwindcss"],"repository":{"type":"git","url":"git+https://github.com/banaris/design-system.git"},"bugs":{"url":"https://github.com/banaris/design-system/issues"},"license":"MIT","readme":"# @banaris/design-system\n\nDesign tokens and React UI primitives for Banaris, built on [Base UI](https://base-ui.com/) and Tailwind CSS v4.\n\nThe palette is drawn from the Japanese pond turtle (クサガメ): dark brown over warm paper, with the yellow-green of its neck stripe as the accent, and the hexagons of its carapace as the brand mark.\n\n- **Catalogue:** <https://banaris-design-system.banaris.workers.dev>\n- **Package:** `@banaris/design-system` on npm — requires React 19 as a peer dependency\n\n## Install\n\n```bash\npnpm add @banaris/design-system\n```\n\n```ts\nimport \"@banaris/design-system/styles.css\";\nimport { Button } from \"@banaris/design-system\";\n```\n\nThat one stylesheet is self-contained — tokens, component CSS, and the Tailwind utilities this package uses, all generated at publish time. **You do not need Tailwind in your project**, and if you do use it, no version coupling applies.\n\n> The class names that appear in the DOM (`inline-flex`, `bg-accent`, …) are not a public contract. Do not hook your own CSS to them.\n\n### The four CSS entry points\n\n| Entry point                             | Contents                                      | When                                                                                                               |\n| --------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| `@banaris/design-system/styles.css`     | Tokens, component CSS and utilities, prebuilt | **Default.** Everything, one import                                                                                |\n| `@banaris/design-system/theme.css`      | Design tokens only (`@theme`)                 | You use Tailwind and want DS tokens (`bg-accent`, `text-ink-dim`) in **your own** markup                           |\n| `@banaris/design-system/base.css`       | The minimum globals the components rely on    | With `theme.css` on the split path — without it components lose `box-sizing` and form controls lose the brand font |\n| `@banaris/design-system/components.css` | Component CSS and keyframes only              | Rarely — only alongside `theme.css` if you are generating utilities yourself                                       |\n\nUsing Tailwind and want the tokens in your own markup? Import both. The duplicate `:root` variables are harmless.\n\n```css\n@import \"tailwindcss\";\n@import \"@banaris/design-system/theme.css\";\n@import \"@banaris/design-system/base.css\";\n```\n\n```ts\nimport \"@banaris/design-system/styles.css\";\n```\n\n**Preflight is not shipped.** Resetting your page's global defaults is not this package's business; you already get preflight from your own `@import \"tailwindcss\"` if you want it. What ships is the minimum the components need to render correctly — `box-sizing` and font inheritance for form controls.\n\n### Overriding styles\n\nThe distributed CSS lives in `@layer theme, base, components, utilities`, which means:\n\n- Passing `className` wins over the component's own variant — no `!important`. Classes are merged with `tailwind-merge`, so conflicting utilities collapse and yours is the one that survives.\n- Any CSS of yours outside a layer beats everything here. You always have the last word.\n\n## Theming\n\n**Light is the default.** Dark activates in two ways:\n\n| `<html>` attribute   | Result                            |\n| -------------------- | --------------------------------- |\n| `data-theme=\"dark\"`  | Dark                              |\n| `data-theme=\"light\"` | Light, ignoring the OS preference |\n| none                 | Follows `prefers-color-scheme`    |\n\nProducts that never think about it get light. Products that offer a toggle set `data-theme` on `<html>` and nothing else — every token follows. The catalogue has a Theme control in its toolbar.\n\nTokens built with `color-mix()` against `--color-bg` — the status planes and their borders — follow automatically and are never restated per theme.\n\n## Using the tokens\n\nTokens are named for their **role**, never their colour: `--color-accent`, not `--color-lime`. A palette named for its hue survives exactly one rebrand.\n\nTwo conventions worth knowing before you reach for a colour:\n\n- **`accent` fills, `accent-soft` reads.** The yellow-green measures 1.08:1 on paper, so it can never be text on a light surface. Anything that has to be _read_ in the accent colour uses `--color-accent-soft`. Anything _filled_ uses `--color-accent`, with `--color-on-accent` for its label.\n- **`border` is decorative, `control-border` is not.** On an unchecked checkbox the border is the only thing announcing the control exists, so it is held to WCAG 1.4.11's 3:1 on every surface. Dividers and card edges are deliberately softer.\n- **Focus is two rings, not one.** Controls here sit on everything from paper to the dark shell to the accent fill, and no single colour clears 3:1 across that range. A light inner ring inside a dark outer one always leaves one of the two contrasting — worst case 8.13:1 across the eight surfaces a control can sit on, in both themes.\n\nTypography has two tiers. The **semantic** tier (`text-display`, `text-h1`, `text-body`, …) bakes in weight and line-height — one class produces the intended voice. The **utility** tier (`text-lg`, `text-base`, `text-sm`, `text-xs`) sets size only and leaves weight to you; use it inside controls. Sizes overlap between tiers on purpose.\n\nEvery token, rendered from the live cascade, is in the catalogue under **Foundations / Tokens**.\n\n## Development\n\n```bash\nmise install\npnpm install\npnpm exec playwright install chromium   # only needed for `verify:full`\npnpm run storybook                      # http://localhost:6006\n```\n\nThe catalogue imports the package by name, exactly as a consumer would, so stories double as usage examples. During development that self-reference resolves to source rather than `dist`.\n\n### Checks\n\nCI calls these scripts rather than listing the individual checks, so adding a check to `verify` reaches CI with no workflow edit. The one seam to know: CI runs `verify` and `test` as two separate jobs, so together they cover `verify:full` — a step added only to `verify:full` itself would not run.\n\n```bash\npnpm run verify        # lint, types, tokens, build, package (seconds)\npnpm run verify:full   # the above plus axe over every story in a real browser\n```\n\nBeyond the usual lint and type checks, seven deterministic guards cover failures that are otherwise **silent** — green build, green types, green lint, broken output:\n\n| Check            | Catches                                                                                                                                                                            |\n| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `check:contrast` | A token that no longer meets WCAG, in either theme. Measured, not asserted by hand — the ratios quoted in `theme.css` are kept honest by this.                                     |\n| `check:tw-merge` | A token missing from the merge config, which silently misgroups a class so two utilities annihilate each other.                                                                    |\n| `check:styles`   | A class that reaches the DOM with no CSS behind it — the classic Tailwind scanning failure, which raises no error anywhere.                                                        |\n| `check:stories`  | An exported component with no story. Absent from the catalogue means undiscoverable, which for this package means it may as well not exist.                                        |\n| `check:package`  | `exports` pointing at a file the build does not produce, and a stylesheet shipping preflight or missing tokens.                                                                    |\n| `check:geometry` | The brand mark's hexagons overlapping instead of tiling — valid SVG, passing build, visibly wrong render.                                                                          |\n| `check:axe-live` | The accessibility suite having stopped running. Seeds a violation and requires the suite to go red, because a suite that checks nothing reports the same green as one that passes. |\n\nThey exist because each of these was possible to ship without a single tool complaining, and they have already earned it — `check:contrast` caught the accent button's label measuring 1.08:1 against its own fill in dark, and `check:styles` caught three separate cases of a class reaching the DOM with no rule behind it.\n\n**A guard is only worth what it catches, so test it by breaking things.** Delete a rule from `dist/styles.css`, misspell a token, reformat a variant map — then confirm the guard fails. Mutating the guard's own assertions proves nothing; mutating the code it watches is the only evidence it has coverage. Three guards passed their first review clean and were still blind: the preflight regex could never match, template-literal classes were never scanned, and fractional utilities like `size-3.5` were silently skipped.\n\n### Writing a component\n\nThree rules the guards enforce, worth knowing before they fail on you:\n\n1. **Class names must be static string literals.** A class assembled by interpolation is invisible to Tailwind's scanner, so it generates nothing and the style silently disappears. Map props to whole literal strings.\n2. **Build the class string with `cn()`, caller's `className` last.** Argument order is priority order.\n3. **Each variant sets background, text and border colour exactly once**, and the shared base sets none of them. Two utilities for one property makes the outcome an ordering detail.\n\nDisabled state is written `data-disabled:`, never `disabled:` — Base UI switches to `aria-disabled` when a control stays keyboard-reachable, and `:disabled` stops matching at that point.\n\n## Releasing\n\nVersioning is driven by [changesets](https://github.com/changesets/changesets).\n\n```bash\npnpm run changeset\n```\n\nMerging that to `main` opens a version PR; merging the version PR publishes to npm. A release is therefore always a reviewed commit rather than a consequence of pushing.\n\nThe workflow decides between those two states — and doing nothing — with changesets' own `select-mode`, so a push with no changeset pending publishes nothing. Skipping that gate is how a package that has never shipped ends up pushing `0.0.0` on its first merge to `main`.\n\nPublishing uses npm's **trusted publishing**: npm verifies the workflow over OIDC and no long-lived token is stored anywhere. This requires registering `banaris/design-system` with the workflow file `release.yml` as a trusted publisher on npmjs.com; until that is done the package is catalogue-only, and the release job still passes because it has nothing to publish.\n\n## Publishing the catalogue\n\nEvery push to `main` deploys the catalogue to Cloudflare Workers, and every pull request uploads a preview version whose URL is written into the PR description. Both need a `CLOUDFLARE_API_TOKEN` repository secret with Workers deploy permission.\n\n## Licence\n\nMIT\n","readmeFilename":"README.md","_rev":"1-c635a28a9a9996a4c63bcf90c488d4d1"}