{"_id":"@doeixd/scoped-focusgroup-polyfill","_rev":"2-46923551b0dc33fb6b33fe44deb0c001","name":"@doeixd/scoped-focusgroup-polyfill","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@doeixd/scoped-focusgroup-polyfill","version":"0.0.1","keywords":["focusgroup","polyfill","roving tabindex","focus management","keyboard navigation","accessibility","a11y","web components","open-ui","w3c","tablist","toolbar","menu","grid","shadow dom","pridepack"],"author":{"name":"Patrick Glenn"},"license":"MIT","_id":"@doeixd/scoped-focusgroup-polyfill@0.0.1","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"homepage":"https://github.com/doeixd/scoped-focusgroup-polyfill#README","bugs":{"url":"https://github.com/doeixd/scoped-focusgroup-polyfill/issues"},"dist":{"shasum":"1456a1cf0d6a9ade145fd7d5eaca05f31c95c3a7","tarball":"https://registry.npmjs.org/@doeixd/scoped-focusgroup-polyfill/-/scoped-focusgroup-polyfill-0.0.1.tgz","fileCount":12,"integrity":"sha512-/HLmTAiii6699e4tyeoNMUlNHbYJ0iq5eCEgKxCUoWw6s6s135XcrT7ePTXhBFLWHUNmvM7mg001QZt6snpoFw==","signatures":[{"sig":"MEQCIElRstUU1nnRAx3wuxp3BkHMr8LLONsr63A/F7jEwPIwAiBupKqciMKfflpPTCkOBtwy/ahJqA5aJD8dcAQn+MI89w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":151123},"main":"./dist/cjs/production/index.js","types":"./dist/types/index.d.ts","module":"./dist/esm/production/index.js","engines":{"node":">=16"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/production/index.js","require":"./dist/cjs/production/index.js","development":{"import":"./dist/esm/development/index.js","require":"./dist/cjs/development/index.js"}}},"gitHead":"f7d70de6c75fe42e3c2434648d02f9ff0b5828da","private":false,"scripts":{"dev":"pridepack dev","test":"vitest","build":"pridepack build","clean":"pridepack clean","start":"pridepack start","watch":"pridepack watch","release":"standard-version && git push --follow-tags origin main","type-check":"pridepack check","prepublishOnly":"pridepack clean && pridepack build"},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"repository":{"url":"git+https://github.com/doeixd/scoped-focusgroup-polyfill.git","type":"git"},"_npmVersion":"8.19.4","description":"A complete and robust polyfill for the Scoped Focusgroup proposal, enabling declarative roving tabindex and accessible keyboard navigation.","directories":{},"_nodeVersion":"16.20.2","publishConfig":{"access":"public"},"typesVersions":{"*":{}},"_hasShrinkwrap":false,"devDependencies":{"tslib":"^2.8.1","vitest":"^2.1.8","pridepack":"2.6.4","typescript":"^5.7.2","@types/node":"^22.10.2","standard-version":"^9.5.0"},"_npmOperationalInternal":{"tmp":"tmp/scoped-focusgroup-polyfill_0.0.1_1758551997066_0.7272484195188988","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@doeixd/scoped-focusgroup-polyfill","version":"0.0.2","engines":{"node":">=16"},"license":"MIT","keywords":["focusgroup","polyfill","roving tabindex","focus management","keyboard navigation","accessibility","a11y","web components","open-ui","w3c","tablist","toolbar","menu","grid","shadow dom","pridepack"],"devDependencies":{"@types/node":"^22.10.2","pridepack":"2.6.4","tslib":"^2.8.1","typescript":"^5.7.2","vitest":"^2.1.8","standard-version":"^9.5.0"},"scripts":{"release":"standard-version && git push --follow-tags origin main","prepublishOnly":"pridepack clean && pridepack build","build":"pridepack build","type-check":"pridepack check","clean":"pridepack clean","watch":"pridepack watch","start":"pridepack start","dev":"pridepack dev","test":"vitest"},"private":false,"description":"A complete and robust polyfill for the Scoped Focusgroup proposal, enabling declarative roving tabindex and accessible keyboard navigation.","repository":{"url":"git+https://github.com/doeixd/scoped-focusgroup-polyfill.git","type":"git"},"homepage":"https://github.com/doeixd/scoped-focusgroup-polyfill#README","bugs":{"url":"https://github.com/doeixd/scoped-focusgroup-polyfill/issues"},"author":{"name":"Patrick Glenn"},"publishConfig":{"access":"public"},"types":"./dist/types/index.d.ts","main":"./dist/cjs/production/index.js","module":"./dist/esm/production/index.js","exports":{".":{"types":"./dist/types/index.d.ts","development":{"require":"./dist/cjs/development/index.js","import":"./dist/esm/development/index.js"},"require":"./dist/cjs/production/index.js","import":"./dist/esm/production/index.js"}},"typesVersions":{"*":{}},"gitHead":"1f811bb262063605bb58e184b29eaf24305f6f24","_id":"@doeixd/scoped-focusgroup-polyfill@0.0.2","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-V5ScEewqG+m0jAck1Adr8lqagsn657qOVTXO4o1p7oA2QMl5I84fea2s+jsBYNOv5CnLhWol9Po2DQbmvsCsvw==","shasum":"bf4b2e53dac1c50d68a0fd7dd004a70ecd398e5d","tarball":"https://registry.npmjs.org/@doeixd/scoped-focusgroup-polyfill/-/scoped-focusgroup-polyfill-0.0.2.tgz","fileCount":12,"unpackedSize":160102,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEWgBkZA0nMFsSFfKCmUX6Oq8IM6PW3015mdfZ8PJ658AiBFkW2IY+9Ewu1rKVhutpa6wUKQu5pkVy1baNkwAB5+wQ=="}]},"_npmUser":{"name":"doeixd","email":"doeixd@gmail.com"},"directories":{},"maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scoped-focusgroup-polyfill_0.0.2_1765825149347_0.957878887029805"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-22T14:39:56.979Z","modified":"2025-12-15T18:59:09.708Z","0.0.1":"2025-09-22T14:39:57.234Z","0.0.2":"2025-12-15T18:59:09.494Z"},"bugs":{"url":"https://github.com/doeixd/scoped-focusgroup-polyfill/issues"},"author":{"name":"Patrick Glenn"},"license":"MIT","homepage":"https://github.com/doeixd/scoped-focusgroup-polyfill#README","keywords":["focusgroup","polyfill","roving tabindex","focus management","keyboard navigation","accessibility","a11y","web components","open-ui","w3c","tablist","toolbar","menu","grid","shadow dom","pridepack"],"repository":{"url":"git+https://github.com/doeixd/scoped-focusgroup-polyfill.git","type":"git"},"description":"A complete and robust polyfill for the Scoped Focusgroup proposal, enabling declarative roving tabindex and accessible keyboard navigation.","maintainers":[{"name":"doeixd","email":"doeixd@gmail.com"}],"readme":"\n[![npm version](https://badge.fury.io/js/@doeixd/scoped-focusgroup-polyfill.svg)](https://badge.fury.io/js/@doeixd/scoped-focusgroup-polyfill)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n# Scoped Focusgroup Polyfill\n\nA complete, production-ready, and robust polyfill for the [Open UI Scoped Focusgroup proposal](https://open-ui.org/components/scoped-focusgroup.explainer/).\n\nThis polyfill brings declarative roving `tabindex`, accessible keyboard navigation, and semantic role management to all modern browsers, allowing you to build complex composite widgets like toolbars, tablists, menus, and grids with a single HTML attribute.\n\nIt is lightweight, performant, and designed to work seamlessly with modern web frameworks and vanilla JavaScript projects.\n\n## Features\n\nThis polyfill implements the full feature set of the Scoped Focusgroup specification:\n\n-   **Declarative Roving Tabindex:** Automatically manages `tabindex` to ensure your widget has a single, reliable entry point.\n-   **Full Keyboard Navigation:**\n    -   Arrow key navigation (`Up`, `Down`, `Left`, `Right`).\n    -   `Home` and `End` key support to jump to the first and last items.\n-   **Last-Focused Memory:** Remembers the last focused item in a group, restoring focus to it when the user tabs back in.\n-   **Content Directionality Support:** Correctly handles `rtl` (right-to-left) layouts and vertical `writing-mode` for intuitive international navigation.\n-   **Advanced Grid Navigation:** Full support for 2D navigation in grids, including `wrap`, `flow`, `row-wrap`, `col-wrap`, and `row-flow` behaviors.\n-   **Semantic Role Inference (Opt-in):** Can automatically apply appropriate ARIA roles (`toolbar`, `tablist`, `tab`, etc.) to your markup for out-of-the-box accessibility.\n-   **Structural Awareness:**\n    -   Handles **nested focusgroups** correctly.\n    -   Supports opt-out subtrees with `focusgroup=\"none\"`.\n    -   Traverses into **Shadow DOM** with the `shadow-inclusive` token.\n-   **Dynamic Content Ready:** Uses a `MutationObserver` to automatically react to items being added, removed, or changed.\n-   **Robust and Performant:** Memory-safe architecture (`WeakMap`), debounced DOM updates (`requestAnimationFrame`), and a minimal performance footprint.\n\n## Quickstart\n\n### 1. Installation\n\nYou can install the polyfill via npm or include it directly from a CDN.\n\n**via npm:**\n\n```bash\nnpm install @doeixd/scoped-focusgroup-polyfill\n```\n\n```javascript\n// Import it into your project's entry point\nimport '@doeixd/scoped-focusgroup-polyfill';\n```\n\n**via CDN:**\n\nInclude the script in your HTML file. It will install itself automatically.\n\n```html\n<script src=\"https://esm.sh/@doeixd/scoped-focusgroup-polyfill\"></script>\n```\n\n### 2. Usage\n\nOnce the polyfill is included, simply add the `focusgroup` attribute to your container elements.\n\n#### Example: A Simple Toolbar\n\nThis example creates an accessible toolbar where users can navigate between buttons using arrow keys.\n\n**Before (Manual JavaScript):**\n\n```html\n<div role=\"toolbar\" aria-label=\"Text Formatting\">\n  <button type=\"button\">Bold</button>\n  <button type=\"button\" tabindex=\"-1\">Italic</button>\n  <button type=\"button\" tabindex=\"-1\">Underline</button>\n</div>\n<!-- Requires dozens of lines of JS to handle roving tabindex,\n     arrow keys, memory, and edge cases. -->\n```\n\n**After (with Polyfill):**\n\nJust add one attribute. That's it.\n\n```html\n<div focusgroup=\"toolbar wrap\" aria-label=\"Text Formatting\">\n  <button type=\"button\">Bold</button>\n  <button type=\"button\">Italic</button>\n  <button type=\"button\">Underline</button>\n</div>\n<!-- The polyfill handles all focus management and navigation automatically. -->\n```\n\n## Configuration\n\nYou can configure the polyfill during installation by calling `ScopedFocusPolyfill.install()` with an options object. This is useful if you include the script manually and want to customize its behavior.\n\n```javascript\n// In your main script file\nwindow.ScopedFocusPolyfill.install({\n  /**\n   * Enable automatic ARIA role inference. The polyfill will apply\n   * roles like `role=\"tablist\"` and `role=\"tab\"` based on the behavior\n   * token, without overwriting existing roles.\n   * Default: false\n   */\n  autoRoles: true,\n\n  /**\n   * Enable detailed console logging for debugging focusgroup behavior.\n   * Useful during development.\n   * Default: false\n   */\n  debug: true,\n\n  /**\n   * Force the polyfill to run even if native browser support is detected.\n   * Useful for testing and ensuring consistent behavior.\n   * Default: false\n   */\n  force: false\n});\n```\n\n## API Reference\n\n### `focusgroup` Attribute Tokens\n\nThe `focusgroup` attribute accepts a space-separated list of tokens. The first token defines the primary behavior.\n\n#### Behavior Tokens\n\n-   `toolbar`\n-   `tablist`\n-   `listbox`\n-   `menu`\n-   `menubar`\n-   `radiogroup`\n-   `grid`\n-   `none` (opts-out an element and its subtree)\n\n#### Modifier Tokens\n\n-   `wrap`: Enables focus to loop from the last item to the first.\n-   `no-memory`: Disables remembering the last focused item.\n-   `inline`: Restricts linear navigation to the inline (horizontal) axis.\n-   `block`: Restricts linear navigation to the block (vertical) axis.\n-   `shadow-inclusive`: Allows the focusgroup to include items inside open Shadow DOM roots.\n-   **Grid Modifiers:** `row-wrap`, `col-wrap`, `row-flow`, `col-flow`.\n\n### Programmatic API\n\nFor advanced use cases, you can interact with a focusgroup programmatically.\n\n```javascript\nconst myToolbar = document.querySelector('#my-toolbar');\nconst focusgroup = window.ScopedFocusPolyfill.getGroupFor(myToolbar);\n\nif (focusgroup) {\n  // Get the currently active element\n  console.log(focusgroup.activeItem);\n\n  // Programmatically move focus\n  focusgroup.focusFirst();\n  focusgroup.focusNext();\n\n  // Manually trigger a rebuild if you've made complex DOM changes\n  focusgroup.rebuild();\n}\n```\n\n### Custom Events\n\nThe polyfill dispatches custom events on the focusgroup container element, allowing you to hook into its lifecycle.\n\n-   **`scopedfocus:rebuild`**: Fired after the group's focusable items have been recalculated. `event.detail.items` contains the new list of items.\n-   **`scopedfocus:change`**: Fired when the active (focused) item within the group changes. `event.detail.relatedTarget` contains the newly focused item.\n\n```javascript\nmyToolbar.addEventListener('scopedfocus:change', (event) => {\n  console.log('New active item:', event.detail.relatedTarget);\n  // Example: Update application state based on the focused tab\n});\n```\n\n## Browser Support\n\nThis polyfill is designed to work in all modern, evergreen browsers that support `MutationObserver` and `WeakMap`.\n\n-   Chrome\n-   Firefox\n-   Safari\n-   Edge\n\nIt does not support Internet Explorer.\n\n## License\n\nThis project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.","readmeFilename":"README.md"}