{"_id":"@asnewyla/unstyled-tabs","name":"@asnewyla/unstyled-tabs","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@asnewyla/unstyled-tabs","version":"0.2.0","description":"Unstyled tabs primitive with roving-focus keyboard navigation and accessibility","license":"MIT","repository":{"type":"git","url":"git+https://github.com/xavierDelaFuente/xd-components.git","directory":"packages/unstyled-tabs"},"publishConfig":{"access":"public"},"main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./test-utils":{"types":"./dist/test-utils.d.ts","import":"./dist/test-utils.mjs","require":"./dist/test-utils.js"}},"sideEffects":false,"keywords":["react","tabs","unstyled","accessible"],"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0","@testing-library/react":"^16.0.0","@testing-library/user-event":"^14.5.0"},"peerDependenciesMeta":{"@testing-library/react":{"optional":true},"@testing-library/user-event":{"optional":true}},"devDependencies":{"react":"^18.3.0","react-dom":"^18.3.0","tsup":"^8.1.0","vitest":"^2.0.0","@vitest/coverage-v8":"^2.0.0","@testing-library/react":"^16.0.0","@testing-library/user-event":"^14.5.0","@testing-library/jest-dom":"^6.4.0","jsdom":"^24.0.0","typescript":"^5.5.0"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","type-check":"tsc --noEmit"},"_nodeVersion":"24.19.0","_id":"@asnewyla/unstyled-tabs@0.2.0","dist":{"integrity":"sha512-U+WbSKbmz8xbnyqwhkJaMWuSNHQC3hDSyxiEAYA0BgpkhEHo9pcp2KBjkbI4T+dQ7cYU1gd3W33lDd41TWWIug==","shasum":"48b0b91598b235ad59440bbc76a6f32df1a05759","tarball":"https://registry.npmjs.org/@asnewyla/unstyled-tabs/-/unstyled-tabs-0.2.0.tgz","fileCount":15,"unpackedSize":45494,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDe2/OyYZrtngfIO8DcgXC9zX+1kA6VPoLsqlTHhKOfbwIgD9ouLGsrVmwWcwdtcIv+vrqXs4WHOSdO7bGUd2wzoqs="}]},"_npmUser":{"name":"asnewyla","email":"asnewyla@gmail.com"},"directories":{},"maintainers":[{"name":"asnewyla","email":"asnewyla@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/unstyled-tabs_0.2.0_1788128075295_0.8929317391294429"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-30T22:14:35.104Z","0.2.0":"2026-08-30T22:14:35.475Z","modified":"2026-08-30T22:14:35.672Z"},"maintainers":[{"name":"asnewyla","email":"asnewyla@gmail.com"}],"description":"Unstyled tabs primitive with roving-focus keyboard navigation and accessibility","keywords":["react","tabs","unstyled","accessible"],"repository":{"type":"git","url":"git+https://github.com/xavierDelaFuente/xd-components.git","directory":"packages/unstyled-tabs"},"license":"MIT","readme":"# @asnewyla/unstyled-tabs\n\nUnstyled, accessible tabs primitive. Compound components with roving-focus\nkeyboard navigation, no visual styling — the base layer under\n`@asnewyla/tabs`.\n\n## Install\n\n```bash\nnpm install @asnewyla/unstyled-tabs\n```\n\n## Usage\n\n```tsx\nimport { Tab, TabList, TabPanel, Tabs } from '@asnewyla/unstyled-tabs';\n\n// Uncontrolled\n<Tabs defaultValue=\"profile\">\n  <TabList aria-label=\"Account settings\">\n    <Tab value=\"profile\">Profile</Tab>\n    <Tab value=\"settings\">Settings</Tab>\n    <Tab value=\"billing\" disabled>Billing</Tab>\n  </TabList>\n  <TabPanel value=\"profile\">Profile content</TabPanel>\n  <TabPanel value=\"settings\">Settings content</TabPanel>\n  <TabPanel value=\"billing\">Billing content</TabPanel>\n</Tabs>\n\n// Controlled\n<Tabs value={active} onValueChange={setActive}>\n  ...\n</Tabs>\n```\n\n### Components\n\n| Component | Renders | Notes |\n|---|---|---|\n| `Tabs` | `<div>` | Owns active-value state (`value`/`defaultValue`/`onValueChange`), provides context to the rest |\n| `TabList` | `<div role=\"tablist\">` | Owns keyboard navigation |\n| `Tab` | `<button role=\"tab\">` | `value` (required), `disabled?` |\n| `TabPanel` | `<div role=\"tabpanel\">` | `value` (required) — only the active panel is rendered at all, not just hidden |\n\n### Keyboard\n\n- `ArrowRight`/`ArrowLeft` move focus to the next/previous **enabled** tab and activate it immediately (automatic activation — the common pattern; moving focus and switching panels happen together, no extra keystroke). Wraps at both ends.\n- `Home`/`End` jump to the first/last enabled tab.\n- Disabled tabs are real `<button disabled>` elements — skipped entirely by keyboard navigation and inert to clicks, not just visually dimmed.\n\nHorizontal orientation only for now — `ArrowUp`/`ArrowDown` aren't handled. A straightforward, backward-compatible addition if a vertical layout is ever needed.\n\n### Accessibility\n\n`Tab` and `TabPanel` cross-reference each other (`aria-controls`/`aria-labelledby`) automatically — both derive matching ids from the same `value`, no manual id wiring required. A `TabPanel`'s accessible name therefore comes from its associated tab's label, not its own body text — expected ARIA behavior for a tabpanel, not a bug.\n\nOnly the active tab has `tabIndex={0}`; every other tab is `-1` (roving tabindex) — the tablist itself is a single stop in the page's tab order, matching the WAI-ARIA Tabs pattern.\n\n## License\n\nMIT\n","readmeFilename":"","_rev":"1-9372fd43489b18e05d86ad0fb7bf4e36"}