{"_id":"@aether-zone/kosmos","_rev":"2-5fbe4f69bfb1de54ec41741d047aa2ea","name":"@aether-zone/kosmos","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.0":{"name":"@aether-zone/kosmos","version":"0.3.0","keywords":["design-system","react","components","design-tokens","tailwind"],"license":"MIT","_id":"@aether-zone/kosmos@0.3.0","maintainers":[{"name":"pascalwilbrink","email":"pascal.wilbrink@gmail.com"}],"homepage":"https://github.com/Aether-zone/kosmos#readme","bugs":{"url":"https://github.com/Aether-zone/kosmos/issues"},"dist":{"shasum":"991b556e61ccfde3095a25266f0ad421aa9aeddf","tarball":"https://registry.npmjs.org/@aether-zone/kosmos/-/kosmos-0.3.0.tgz","fileCount":8,"integrity":"sha512-CP/iqQW18ht7n85C/a1EnR5ujOnd8uYNfiisWOlI0YmKOO1Xa4yvsdh315I+IkvWTTzn/H5ZVZJfX6w/+wpiYg==","signatures":[{"sig":"MEUCIQDYIpqEksAc/cJ9R+KQEJzt8+CHD49htud+NfNlkFacZwIgCPhVEBYt1yFUVPLI6uvSTDRIocHJFBVU7c3fh366ZuA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":686721},"main":"./dist/index.mjs","_from":"file:aether-zone-kosmos-0.3.0.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./theme.css":"./dist/theme.css","./styles.css":"./dist/styles.css","./tokens.css":"./dist/tokens.css"},"private":false,"scripts":{"dev":"tsup --watch","lint":"oxlint --deny-warnings","test":"vitest run","build":"tsup && node build-styles.mjs && tailwindcss -i src/tailwind.css -o dist/styles.css --minify","typecheck":"tsc --noEmit"},"_npmUser":{"name":"pascalwilbrink","email":"pascal.wilbrink@gmail.com"},"_resolved":"/tmp/5cece74b0b6cfd84a61d770f7d53d4b4/aether-zone-kosmos-0.3.0.tgz","_integrity":"sha512-CP/iqQW18ht7n85C/a1EnR5ujOnd8uYNfiisWOlI0YmKOO1Xa4yvsdh315I+IkvWTTzn/H5ZVZJfX6w/+wpiYg==","repository":{"url":"git+https://github.com/Aether-zone/kosmos.git","type":"git","directory":"packages/react"},"_npmVersion":"10.9.8","description":"The Kosmos React component library, built on design tokens.","directories":{},"sideEffects":["*.css"],"_nodeVersion":"22.23.2","dependencies":{"react-icons":"^5.7.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","react":"^19.0.0","oxlint":"^1.79.0","vitest":"^3.0.0","react-dom":"^19.0.0","typescript":"^5.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@kosmos/tokens":"0.1.0","@tailwindcss/cli":"^4.3.3","@types/react-dom":"^19.0.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/kosmos_0.3.0_1788524050687_0.7074037875297003","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"_id":"@aether-zone/kosmos@0.3.1","bugs":{"url":"https://github.com/Aether-zone/kosmos/issues"},"dist":{"shasum":"ac1146475258f0f868048dc26b1dc07374873cf3","tarball":"https://registry.npmjs.org/@aether-zone/kosmos/-/kosmos-0.3.1.tgz","fileCount":8,"integrity":"sha512-mT6Vh2Nu7tlbieby0Zvyz5993BQ8C+976cV4YVPUbAk7Kdx2E+OZ3USNz5vxoJkyEvl4fiZvI+3chx06ssmnXQ==","signatures":[{"sig":"MEYCIQCo+//MTW9MH8yfuBBy/E6FHWtS1Ew9aVoLktJfh7vWWgIhAMVWRnyKvyhNdqQYX6M9Y5aDdBGoaaSAePdKOE8BGFK0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA8qddqU9n62oyTZptrOup/pc9Dk5yycAuUYMWMnjgjTAiB7fkIsvn1VXccIJOYIFrjCTmlGEMdvzFSFREi/LhimyA=="}],"unpackedSize":689838},"main":"./dist/index.mjs","name":"@aether-zone/kosmos","_from":"file:aether-zone-kosmos-0.3.1.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs"},"./theme.css":"./dist/theme.css","./styles.css":"./dist/styles.css","./tokens.css":"./dist/tokens.css"},"license":"MIT","private":false,"scripts":{"dev":"tsup --watch","lint":"oxlint --deny-warnings","test":"vitest run","build":"tsup && node build-styles.mjs && tailwindcss -i src/tailwind.css -o dist/styles.css --minify","typecheck":"tsc --noEmit"},"version":"0.3.1","_npmUser":{"name":"pascalwilbrink","email":"pascal.wilbrink@gmail.com"},"homepage":"https://github.com/Aether-zone/kosmos#readme","keywords":["design-system","react","components","design-tokens","tailwind"],"_resolved":"/tmp/8b03418588c88b80dc13e29b899a71af/aether-zone-kosmos-0.3.1.tgz","_integrity":"sha512-mT6Vh2Nu7tlbieby0Zvyz5993BQ8C+976cV4YVPUbAk7Kdx2E+OZ3USNz5vxoJkyEvl4fiZvI+3chx06ssmnXQ==","repository":{"url":"git+https://github.com/Aether-zone/kosmos.git","type":"git","directory":"packages/react"},"_npmVersion":"10.9.8","description":"The Kosmos React component library, built on design tokens.","directories":{},"maintainers":[{"name":"pascalwilbrink","email":"pascal.wilbrink@gmail.com"}],"sideEffects":["*.css"],"_nodeVersion":"22.23.2","dependencies":{"react-icons":"^5.7.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","react":"^19.0.0","oxlint":"^1.79.0","vitest":"^3.0.0","react-dom":"^19.0.0","typescript":"^5.0.0","tailwindcss":"^4.3.3","@types/react":"^19.0.0","@kosmos/tokens":"0.1.0","@tailwindcss/cli":"^4.3.3","@types/react-dom":"^19.0.0"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kosmos_0.3.1_1789290620375_0.6829347167755495"}}},"time":{"created":"2026-09-04T12:14:10.478Z","modified":"2026-09-13T09:10:20.629Z","0.3.0":"2026-09-04T12:14:10.842Z","0.3.1":"2026-09-13T09:10:20.464Z"},"bugs":{"url":"https://github.com/Aether-zone/kosmos/issues"},"license":"MIT","homepage":"https://github.com/Aether-zone/kosmos#readme","keywords":["design-system","react","components","design-tokens","tailwind"],"repository":{"url":"git+https://github.com/Aether-zone/kosmos.git","type":"git","directory":"packages/react"},"description":"The Kosmos React component library, built on design tokens.","maintainers":[{"name":"pascalwilbrink","email":"pascal.wilbrink@gmail.com"}],"readme":"# @aether-zone/kosmos\n\nThe Kosmos React component library. ESM-only, React 19, styled with Tailwind\nCSS v4 utilities that resolve to [`@kosmos/tokens`](../tokens/README.md).\n\n## Install\n\nThe package is published to **GitHub Packages**, so npm needs to be told where\nthe `@aether-zone` scope lives. In an `.npmrc` beside your `package.json`:\n\n```\n@aether-zone:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}\n```\n\n`GITHUB_TOKEN` is a personal access token with the `read:packages` scope —\nGitHub Packages requires authentication even for public packages.\n\n```bash\npnpm add @aether-zone/kosmos\n```\n\n`react` and `react-dom` are peer dependencies and stay external to the bundle.\n[`react-icons`](https://react-icons.github.io/react-icons/icons/io5/) is a\nregular dependency, installed for you.\n\n## Usage\n\n```tsx\nimport '@aether-zone/kosmos/styles.css';\n\nimport { Button, Card, CardContent } from '@aether-zone/kosmos';\n\nexport function Example() {\n    return (\n        <Card>\n            <CardContent>\n                <Button variant=\"primary\" size=\"md\">\n                    Save\n                </Button>\n            </CardContent>\n        </Card>\n    );\n}\n```\n\nThat single stylesheet is all you need. **You do not need Tailwind** — it is\nprecompiled and self-contained: token values, the light and dark themes, and\nevery utility the components use. It carries no preflight, so it will not\nreset your document.\n\nThis matters because Tailwind v4 refuses to scan `node_modules`. A library\nthat shipped only a theme mapping would leave a consumer's own Tailwind build\nunable to see any of its class names, and every component would render\nunstyled — no `@source` incantation on either side changes that.\n\n### Stylesheets\n\n| Import | Contents | Use when |\n| --- | --- | --- |\n| `@aether-zone/kosmos/styles.css` | Tokens, themes and all compiled utilities | Almost always |\n| `@aether-zone/kosmos/tokens.css` | Only the `--kosmos-*` variables | You want the tokens without any component CSS |\n| `@aether-zone/kosmos/theme.css` | Only the token → Tailwind `@theme inline` mapping | You compile your own Tailwind and want Kosmos tokens behind its utilities |\n\n`styles.css` already contains `tokens.css`, so importing both is redundant.\n\n### Dark mode\n\nPut `.dark` (or `[data-theme=\"dark\"]`) on any ancestor — usually `<html>`:\n\n```html\n<html class=\"dark\">\n```\n\nNo provider and no JavaScript: the tokens swap in CSS, and every component\nfollows.\n\n## Components\n\nTypography\n: `Blockquote`, `Code`, `Heading`, `Kbd`, `Link`, `List`/`ListItem`, `Text`\n\nForm controls\n: `Autocomplete`, `Checkbox`, `Combobox` (multi-select with chips),\n  `DatePicker`, `FileUpload`, `Form` (`Field`, `FieldLabel`,\n  `FieldDescription`, `FieldError`), `Input`, `Label`, `Otp`,\n  `Radio`/`RadioGroup`, `Rating`, `Select`, `Slider`, `Switch`, `Textarea`,\n  `ToggleGroup`\n\nActions and navigation\n: `AppBar`, `Breadcrumbs`, `Button`, `Command` (inline or modal palette),\n  `ContextMenu`, `Dropdown`, `Menubar`, `Pagination`, `Sidenav`, `Tabs`,\n  `Toolbar`, `TreeView`\n\nFeedback and overlay\n: `Alert`, `AlertDialog`, `Dialog`, `Drawer`, `Popover`, `Progress`,\n  `Skeleton`, `Spinner`, `Toast` (`ToastProvider`, `useToast`), `Tooltip`\n\nContent\n: `Accordion`, `Avatar`, `Badge`, `Card`, `Carousel`, `Chip`, `EmptyState`,\n  `Separator`, `Table`, `Timeline`\n\nEvery component and its props are documented in Storybook.\n\n## Hooks\n\nThe library exports the hooks its own components are built on.\n\n| Hook | For |\n| --- | --- |\n| `useMediaQuery(query)` | Any media query, read during render |\n| `useBreakpoint(name)` / `useIsMobile()` | Tailwind's breakpoints, in JavaScript |\n| `usePrefersReducedMotion()` | Motion a media query cannot switch off |\n| `useTheme(options)` | Reading and setting light/dark/system |\n| `useDisclosure(defaultOpen)` | Open/close state for an overlay |\n| `useControllableState(options)` | Building your own controlled/uncontrolled component |\n| `useCopyToClipboard(resetAfter)` | A copy button, with its \"Copied\" window |\n\nTwo are worth a note.\n\n`useMediaQuery` is built on `useSyncExternalStore`, so it reports the right\nanswer on the very first render. The usual state-plus-effect version claims\nthe query does not match, then corrects itself — which is a flash of the wrong\nlayout on every mount.\n\n`useTheme` treats `'system'` as a real choice, distinct from whatever it\nresolves to: it keeps following the OS when that changes. Render against\n`resolvedTheme`, and show `theme` in a theme picker.\n\n```tsx\nconst { theme, resolvedTheme, setTheme } = useTheme();\n```\n\nIt applies the theme the way Kosmos expects — a `dark` class on `<html>` —\nand remembers the choice in `localStorage` unless you pass\n`storageKey: null`. Storage failures are swallowed: a remembered theme is not\nworth failing a render over.\n\n## Conventions\n\nComponents are plain functions that join Tailwind class strings by hand —\na `baseStyles` string plus `Record<Variant, string>` lookup maps. There is no\n`cva` or `clsx` dependency.\n\nOne consequence is worth internalising: **competing Tailwind utilities resolve\nby stylesheet order, not by their order in the class string.** A base\n`border-transparent` will beat a conditional `border-destructive` appended\nafter it. Keep the conflicting property out of the base styles and put every\nbranch in the conditional:\n\n```tsx\nconst classes = [\n    baseStyles,                          // `border`, no colour\n    error ? 'border-destructive' : 'border-input',\n    className,\n].filter(Boolean).join(' ');\n```\n\n`className` always comes last so consumers can override.\n\n## Overlays\n\n`Dropdown`, `Tooltip`, `Autocomplete` and `DatePicker` render their panels\nthrough `src/internal/OverlayPanel`, which portals to `document.body` and\npositions against the anchor by measurement. Rendering in place is simpler,\nbut the panel is then clipped by any ancestor with `overflow: hidden` — a\nCard, a scrolling sidebar, a table cell. Build new overlays on the same\nprimitive rather than reaching for `absolute`.\n\nThe trade-off portalling brings is that the panel leaves its DOM subtree, so\n\"click outside\" has to consider two detached trees; `useDismiss` takes a list\nof refs for exactly that reason.\n\nModal surfaces go through `ModalOverlay`, which bundles the portal, backdrop,\nfocus trap, scroll lock and Escape handling. `Dialog`, `Drawer` and\n`AlertDialog` all share it: `aria-modal=\"true\"` asserts the rest of the page\nis inert, and a modal that lets focus or scrolling escape is lying about that,\nso it is worth having exactly one implementation of.\n\nWhich overlay to reach for:\n\n| | Portalled | Focus trapped | Page behind |\n| --- | --- | --- | --- |\n| `Tooltip`, `Dropdown`, `Popover` | yes | no | live |\n| `Dialog`, `Drawer` | yes | yes | inert |\n| `AlertDialog` | yes | yes | inert, and the backdrop does not dismiss |\n\n`AlertDialog` refuses backdrop dismissal and puts initial focus on the cancel\naction, so neither a stray click nor a reflexive Enter can confirm something\ndestructive.\n\n`OverlayPanel` anchors to anything that can report a rectangle, not just a DOM\nnode — `ContextMenu` passes a zero-size rect at the cursor.\n\n**Moving focus into an overlay opened by a click must be deferred a frame.**\nThe browser settles focus onto `<body>` as part of handling that click, and it\ndoes so *after* React has run its effects, so an immediate `.focus()` is\nsilently undone. `ContextMenu` and `Menubar` both wrap the move in\n`requestAnimationFrame` for this reason; without it their arrow keys and\nEscape appear to do nothing, because the panel that handles those keys was\nnever focused.\n\n## Typography\n\nThe text components consume the **semantic** type tokens — `text-body`,\n`text-label`, `text-heading`, `text-display` — rather than the raw `xs…5xl`\nscale, so a component asks for body text or a label rather than a size. Until\nthese components existed the semantic layer was defined but unused.\n\n`Heading` keeps `level` and `size` independent: the level sets the tag and so\nthe document outline, while the size sets the appearance. An `h2` can look\nsmall without breaking the outline to get there.\n\nOne Tailwind constraint shows up here. Utilities are generated only from\nclasses it can see in source, so a variable count cannot be interpolated into\na class name — `Text`'s `lineClamp` maps through a lookup of literal\n`line-clamp-N` classes for that reason.\n\n## React Server Components\n\nThe package declares `'use client'`, so importing a component into a Server\nComponent works and renders it on the client. Without that declaration the\nbuild fails with `createContext is not a function`, because most of these\ncomponents use state, refs or portals.\n\nThe whole bundle is one module, so the boundary covers all of it — including\nthe presentational components, which could in principle render on the server.\nSplitting them out would mean shipping the module graph unbundled, and\nextensionless relative imports in `.mjs` break under webpack's\n`fullySpecified` rule. Worth revisiting if server rendering the static\ncomponents turns out to matter.\n\n## Motion\n\nAnimation is wrapped in Tailwind's `motion-safe:` variant, so it stops for\nanyone whose system asks for reduced motion. No animation runtime is involved\n— this is a media query, and the rules ship inside `styles.css`.\n\nTwo things a media query cannot do, which components handle themselves:\n\n- **Content that moves on a timer** has to actually stop. `Carousel` reads the\n  preference through `usePrefersReducedMotion` and does not auto-advance.\n- **A spinner that freezes conveys nothing**, so it keeps turning at 2.5s\n  rather than 1s instead of stopping.\n\nAn auto-playing `Carousel` also renders a pause control, which WCAG 2.2.2\nrequires of anything that moves for more than five seconds. Pausing on hover\nis not enough: it does nothing on a touch screen, or for someone reading.\n\n## Icons\n\nIcons come from the Ionicons 5 set via `react-icons/io5`, always imported from\nthat subpath so bundlers can tree-shake — never from the `react-icons` root.\nThey are sized with Tailwind (`size-4`) and inherit `currentColor`, so an icon\npicks up the colour of whatever it sits in without any variant plumbing.\n`react-icons` stays external to the bundle.\n\nIonicons 5 has no text-formatting glyphs (no bold or italic), which is worth\nknowing before designing a rich-text toolbar against it.\n\nUse `*-foreground` tokens only on their matching **solid** fill —\n`text-success-foreground` is white, so it disappears on a `bg-success/10`\ntint. Tinted surfaces take `text-foreground`.\n\n## Adding a component\n\n```\nsrc/components/<name>/\n  <Name>.tsx     component and its exported prop types\n  index.ts       export * from './<Name>'\n```\n\nRe-export the directory from `src/components/index.ts`, then add a story in\n`apps/storybook/src/stories/components/<Name>.stories.tsx` importing from\n`@aether-zone/kosmos` — never by a relative path into the package.\n\n## Scripts\n\n| Command | What it does |\n| --- | --- |\n| `pnpm build` | `tsup` bundle plus a copy of `styles.css` into `dist`. |\n| `pnpm dev` | `tsup --watch`. |\n| `pnpm typecheck` | `tsc --noEmit`. |\n","readmeFilename":"README.md"}