bestax-mcp 1.18.39 → 1.18.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/data/catalog.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 2,
3
3
  "generatedFrom": {
4
4
  "package": "@allxsmith/bestax-bulma",
5
- "version": "5.67.1"
5
+ "version": "5.67.3"
6
6
  },
7
7
  "docsBase": "https://bestax.io/docs",
8
8
  "categories": [
@@ -535,7 +535,7 @@
535
535
  "import": "import { ContextMenu } from '@allxsmith/bestax-bulma';",
536
536
  "compound": true,
537
537
  "propCount": 10,
538
- "exampleCount": 9
538
+ "exampleCount": 8
539
539
  },
540
540
  {
541
541
  "name": "Control",
@@ -292,7 +292,7 @@
292
292
  "name": "appendToBody",
293
293
  "type": "boolean",
294
294
  "default": "false",
295
- "description": "Renders the panel through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
295
+ "description": "Renders the panel through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
296
296
  "required": false,
297
297
  "inherited": true,
298
298
  "deprecated": false
@@ -538,7 +538,7 @@
538
538
  "name": "appendToBody",
539
539
  "type": "boolean",
540
540
  "default": "false",
541
- "description": "Renders the panel through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
541
+ "description": "Renders the panel through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
542
542
  "required": false,
543
543
  "inherited": false,
544
544
  "deprecated": false
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "title": "Where the List Opens",
38
- "code": "<Modal active={active} onClose={close} aria-label=\"Edit shipping\">\n <Modal.Background onClick={close} />\n <Modal.Card>\n <Modal.Card.Body>\n <ConfigProvider portalContainer={overlay ?? undefined}>\n <Combobox\n label=\"Country\"\n data={countries}\n appendToBody\n position=\"auto\"\n />\n </ConfigProvider>\n </Modal.Card.Body>\n </Modal.Card>\n {/* In the modal and outside the card, so the card can't clip it. */}\n <div ref={setOverlay} />\n</Modal>"
38
+ "code": "<Modal active={active} onClose={close} aria-label=\"Edit shipping\">\n <Modal.Background onClick={close} />\n <Modal.Card>\n <Modal.Card.Body>\n <Combobox label=\"Country\" data={countries} appendToBody position=\"auto\" />\n </Modal.Card.Body>\n </Modal.Card>\n</Modal>"
39
39
  },
40
40
  {
41
41
  "title": "Building It Yourself",
@@ -322,7 +322,7 @@
322
322
  "name": "appendToBody",
323
323
  "type": "boolean",
324
324
  "default": "false",
325
- "description": "Renders the menu through `Portal`, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default, fixed to the viewport at the field, so a container that clips its overflow can't cut it off. It then shows from the commit after hydration, never in server markup. Theming variables set on an ancestor reach it only when the portal's container is inside that ancestor, as with Popover. In a `Modal`, point `portalContainer` inside the modal, so the list stays in the dialog for assistive tech.",
325
+ "description": "Renders the menu through `Portal`, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`, fixed to the viewport at the field, so a container that clips its overflow can't cut it off. It then shows from the commit after hydration, never in server markup. Theming variables set on an ancestor reach it only when the portal's container is inside that ancestor, as with Popover.",
326
326
  "required": false,
327
327
  "inherited": true,
328
328
  "deprecated": false
@@ -50,7 +50,7 @@
50
50
  "code": "function example() {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [ran, setRan] = useState<string | null>(null);\n const colors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n ] as const;\n const matches = colors.filter(c => c.includes(query.trim().toLowerCase()));\n\n return (\n <>\n <Button onClick={() => setOpen(true)}>Pick a color</Button>\n <Paragraph mt=\"3\">{ran ? `Picked ${ran}` : 'No color yet'}</Paragraph>\n <Command.Dialog\n open={open}\n onOpenChange={setOpen}\n label=\"Colors\"\n query={query}\n onQueryChange={setQuery}\n onSelect={setRan}\n >\n <Command.Input placeholder=\"Search colors\" />\n {matches.length > 0 ? (\n <Command.List>\n <Command.Group label=\"Bulma colors\">\n {matches.map(color => (\n <Command.Item\n key={color}\n value={color}\n hint={<Tag color={color}>is-{color}</Tag>}\n >\n {color}\n </Command.Item>\n ))}\n </Command.Group>\n </Command.List>\n ) : (\n <Command.Empty>No colors match</Command.Empty>\n )}\n </Command.Dialog>\n </>\n );\n}"
51
51
  }
52
52
  ],
53
- "accessibility": "- **The roles:** the field is `role=\"combobox\"` with `aria-autocomplete=\"list\"` and\n `aria-haspopup=\"listbox\"`, and the list is a `listbox` of `option`s, the ARIA 1.2 combobox the\n ARIA Authoring Practices describe. A group is a `group` named by its heading.\n- **Focus stays in the field:** the highlighted item is the field's `aria-activedescendant`, and\n the only item that's `aria-selected`. When a key moves it, it's scrolled into view. A click on an\n item keeps focus in the field too.\n- **`aria-expanded`** is `true` while the list has an item in it, with `aria-controls` pointing\n at it, and `false` while only the empty message shows.\n- **Names:** `label` names the field and the list, and in `Command.Dialog` the dialog as well.\n It's \"Commands\" unless you give one. An `aria-label` or `aria-labelledby` on `Command.Input`,\n `Command.List` or `Command.Dialog` names that one instead.\n- **An item is named by its label alone,** and described by its description and hint, so a\n screen reader says what it does first and the rest after. Its icon is hidden.\n- **Disabled items** are `aria-disabled`, reachable with the arrows, never run, and never\n highlighted when the list changes.\n- **The empty message** is announced through a polite status region when your typing brings it on\n screen, once each time the matches run out, and after `isLoading` ends. An empty search\n announces nothing.\n- **No count is announced,** as with [`Combobox`](https://bestax.io/docs/api/form/combobox#a-result-count): a screen\n reader reads how many items there are from the list itself, and a count read out on each\n keystroke talks over the typing.\n- **The dialog** is a modal `dialog` with `aria-modal`, around the Command's box, inside a\n `Modal` that's `role=\"presentation\"`. Focus moves to the search field when it opens, stays\n inside while it's open, and goes back to where it was when it closes. The page behind it\n doesn't scroll.\n- **The shortcut** never fires while someone is typing, so it can't swallow a letter, and it\n leaves a key the page has already used alone. Nothing announces it, so put it on a button with\n `aria-keyshortcuts`, and show it with `Kbd`.\n- **Forced colors:** in Windows high contrast mode the highlight is drawn as an outline in the\n system's highlight color, and a disabled item takes the system's color for disabled text.\n- **No motion:** nothing animates.",
53
+ "accessibility": "- **The roles:** the field is `role=\"combobox\"` with `aria-autocomplete=\"list\"` and\n `aria-haspopup=\"listbox\"`, and the list is a `listbox` of `option`s, the ARIA 1.2 combobox the\n ARIA Authoring Practices describe. A group is a `group` named by its heading.\n- **Focus stays in the field:** the highlighted item is the field's `aria-activedescendant`, and\n the only item that's `aria-selected`. When a key moves it, it's scrolled into view. A click on an\n item keeps focus in the field too.\n- **`aria-expanded`** is `true` while the list has an item in it, with `aria-controls` pointing\n at it, and `false` while only the empty message shows.\n- **Names:** `label` names the field and the list, and in `Command.Dialog` the dialog as well.\n It's \"Commands\" unless you give one. An `aria-label` or `aria-labelledby` on `Command.Input`,\n `Command.List` or `Command.Dialog` names that one instead.\n- **An item is named by its label alone,** and described by its description and hint, so a\n screen reader says what it does first and the rest after. Its icon is hidden.\n- **Disabled items** are `aria-disabled`, reachable with the arrows, never run, and never\n highlighted when the list changes.\n- **The empty message** is announced through a polite status region when your typing brings it on\n screen, once each time the matches run out, and after `isLoading` ends. An empty search\n announces nothing.\n- **No count is announced,** as with [`Combobox`](https://bestax.io/docs/api/form/combobox#a-result-count): a screen\n reader reads how many items there are from the list itself, and a count read out on each\n keystroke talks over the typing.\n- **The dialog** is a modal `dialog` with `aria-modal`, around the Command's box, inside a\n `Modal` that's `role=\"presentation\"`. Focus moves to the search field when it opens, stays\n inside while it's open, and goes back to where it was when it closes. The page behind it\n doesn't scroll. A popup opened inside it, such as a `Popover` with `appendToBody`, is portaled\n into the Command's box, the element with `aria-modal`, unless a\n [`ConfigProvider`](https://bestax.io/docs/api/helpers/config#where-portaled-content-goes) sets a `portalContainer`.\n- **The shortcut** never fires while someone is typing, so it can't swallow a letter, and it\n leaves a key the page has already used alone. Nothing announces it, so put it on a button with\n `aria-keyshortcuts`, and show it with `Kbd`.\n- **Forced colors:** in Windows high contrast mode the highlight is drawn as an outline in the\n system's highlight color, and a disabled item takes the system's color for disabled text.\n- **No motion:** nothing animates.",
54
54
  "related": [
55
55
  "Combobox",
56
56
  "Dropdown",
@@ -49,7 +49,7 @@
49
49
  "name": "portalContainer",
50
50
  "type": "string | HTMLElement",
51
51
  "default": null,
52
- "description": "Where portaled content goes when it has no target of its own: what a `Portal` without a `container` renders, and what the library's components portal, such as Popover's `appendToBody` panel, a `Modal` with `portal` set to `true`, or a `Toast`. An element, or a `document.querySelector` selector. Unset, or a selector that matches nothing, that content goes to `document.body`. A target a component is given, such as a `Toast`'s `container`, still wins, and nothing portals that would otherwise render in place. The overlays and panels this moves are `position: fixed`, so pick an element with no ancestor that has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`. That ancestor becomes their containing block, and they cover or line up against it instead of the viewport. An ancestor that starts a stacking context, with a `z-index` or `isolation: isolate`, keeps them inside it, under anything the page stacks above it. `NotificationContainer`'s status region moves too, so this also decides where its announcements come from, and an `aria-hidden` ancestor silences them. A selector can't find an element inside a shadow root, so pass the element itself there, held in state from a ref callback. Content that portals before the element is set goes to `document.body` and moves when it is, which remounts it, as `Portal`'s `container` describes.",
52
+ "description": "Where portaled content goes when it has no target of its own: what a `Portal` without a `container` renders, and what the library's components portal, such as Popover's `appendToBody` panel, a `Modal` with `portal` set to `true`, or a `Toast`. An element, or a `document.querySelector` selector. Unset, or a selector that matches nothing, that content goes to `document.body`, except a popup, such as Popover's panel or a picker's, opened inside a `Modal`, `Dialog` or `Command.Dialog`: that goes into the dialog, where screen readers that honor its `aria-modal` reach it. Set, it takes those popups too. A target a component is given, such as a `Toast`'s `container`, still wins, and nothing portals that would otherwise render in place. The overlays and panels this moves are `position: fixed`, so pick an element with no ancestor that has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`. That ancestor becomes their containing block, and they cover or line up against it instead of the viewport. An ancestor that starts a stacking context, with a `z-index` or `isolation: isolate`, keeps them inside it, under anything the page stacks above it. `NotificationContainer`'s status region moves too, so this also decides where its announcements come from, and an `aria-hidden` ancestor silences them. A selector can't find an element inside a shadow root, so pass the element itself there, held in state from a ref callback. Content that portals before the element is set goes to `document.body` and moves when it is, which remounts it, as `Portal`'s `container` describes.",
53
53
  "required": false,
54
54
  "inherited": false,
55
55
  "deprecated": false
@@ -61,5 +61,5 @@
61
61
  }
62
62
  ],
63
63
  "cssVars": [],
64
- "doc": "# ConfigProvider\n\n## Overview\n\nThe `ConfigProvider` component provides a React context for configuring global settings across all Bulma UI components. Its `classPrefix` is applied automatically to **all Bulma CSS classes**, including both main component classes (like `button`, `box`, `title`) and helper/modifier classes (like `is-primary`, `has-text-centered`, `m-4`), allowing you to namespace Bulma classes to avoid conflicts with other CSS frameworks or customize the class naming convention.\n\n---\n\n## Import\n\n```tsx\nimport { ConfigProvider, useConfig } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Hooks\n\n### useConfig\n\nReturns the current configuration context values.\n\n```tsx\nconst { classPrefix } = useConfig();\n```\n\n### useClassPrefix\n\nConvenience hook that returns just the classPrefix string (or empty string if not set).\n\n```tsx\nconst classPrefix = useClassPrefix();\n```\n\n---\n\n## Usage Examples\n\n### Basic Usage with Class Prefix\n\nUse `ConfigProvider` to add a prefix to all Bulma CSS classes within its scope:\n\n```tsx\nimport {\n ConfigProvider,\n Box,\n Title,\n Button,\n Notification,\n} from '@allxsmith/bestax-bulma';\n\nfunction App() {\n return (\n <div>\n <Title size=\"3\">Prefixed Bulma Components</Title>\n <p>\n This demonstrates using ConfigProvider with{' '}\n <code>classPrefix=\"bestax-\"</code> to add the \"bestax-\" prefix to all\n component CSS classes. This is useful when integrating Bulma with other\n CSS frameworks or when you need to namespace Bulma classes to avoid\n conflicts.\n </p>\n\n <Notification color=\"info\" mt=\"4\">\n <strong>Tip:</strong> Open your browser's developer tools and inspect\n the DOM elements below. You'll see that all Bulma CSS classes have the\n \"bestax-\" prefix applied.\n </Notification>\n\n <ConfigProvider classPrefix=\"bestax-\">\n <Box mt=\"4\" p=\"4\">\n {/* \n This Box will have classes:\n - bestax-box (main class)\n - bestax-mt-4 (helper class)\n - bestax-p-4 (helper class)\n */}\n <Title size=\"4\">Prefixed Components</Title>\n {/* \n This Title will have classes:\n - bestax-title (main class)\n - bestax-is-4 (modifier class)\n */}\n <p>\n All components inside this ConfigProvider will have their CSS\n classes prefixed with \"bestax-\". This allows you to use Bulma\n alongside other CSS frameworks without class name conflicts.\n </p>\n <Button color=\"primary\" mt=\"3\" isRounded>\n {/* \n This Button will have classes:\n - bestax-button (main class)\n - bestax-is-primary (modifier class)\n - bestax-is-rounded (modifier class)\n - bestax-mt-3 (helper class)\n */}\n Bulma-Prefixed Button\n </Button>\n </Box>\n </ConfigProvider>\n\n <Box mt=\"4\" p=\"4\">\n <Title size=\"4\">Standard Components (No Prefix)</Title>\n <p>\n Components outside the ConfigProvider use standard Bulma classes\n without any prefix.\n </p>\n <Button color=\"info\">Standard Button</Button>\n </Box>\n </div>\n );\n}\n```\n\n### Nested ConfigProviders\n\nConfigProviders can be nested. An inner provider replaces the outer one's settings rather than adding to them, so inside it any setting it leaves out is back to its default:\n\n```tsx\nfunction NestedExample() {\n return (\n <ConfigProvider classPrefix=\"outer-\">\n <Box p=\"4\">\n <Title>Outer Prefix (outer-)</Title>\n\n <ConfigProvider classPrefix=\"inner-\">\n <Box p=\"4\" mt=\"3\">\n <Title>Inner Prefix (inner-)</Title>\n <Button color=\"primary\">Inner Button</Button>\n </Box>\n </ConfigProvider>\n\n <Button color=\"info\" mt=\"3\">\n Outer Button\n </Button>\n </Box>\n </ConfigProvider>\n );\n}\n```\n\n### Where Portaled Content Goes\n\n`portalContainer` sets where portaled content goes when it has no target of its own. That covers a [`Portal`](https://bestax.io/docs/api/helpers/portal) with no `container`, and what the library's components portal, such as the panel `Popover` renders with `appendToBody`, a `Modal` or `Dialog` with `portal` set to `true`, a `Toast`, or a `Sidebar`. It takes an element or a `document.querySelector` selector. Unset, that content goes to `document.body`.\n\nA target a component is given still wins, such as a `Modal`'s `portal` selector or a `Toast`'s `container`. And `portalContainer` only changes where portaled content goes: a component that renders in place, like a `Modal` without `portal` or a `Toast` with `inline`, still does.\n\n`document.body` is outside any styles scoped to where the content was declared: a scoped `Theme`'s variables, or a shadow root's stylesheets. To keep them, render an element inside that area and make it the `portalContainer`, held in state from a ref callback. Below, the panel keeps the purple primary its `Theme` sets, which it would lose at the end of the page:\n\n```tsx live\nfunction ThemedOverlays() {\n const [overlays, setOverlays] = useState<HTMLElement | null>(null);\n\n return (\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <ConfigProvider portalContainer={overlays ?? undefined}>\n <Popover\n appendToBody\n ariaLabel=\"Themed panel\"\n trigger={<Button color=\"primary\">Open</Button>}\n >\n <Popover.Body>\n <Button color=\"primary\">Still purple</Button>\n </Popover.Body>\n </Popover>\n </ConfigProvider>\n <div ref={setOverlays} />\n </Theme>\n );\n}\n```\n\nA selector can't find an element inside a shadow root, since `document.querySelector` doesn't look there, so in a shadow root pass the element itself, as above. Content that portals before the element is set goes to `document.body` and moves when it is, which remounts it and resets its state, as [`Portal`'s `container`](https://bestax.io/docs/api/helpers/portal#into-a-container) describes. A nested provider like this one needs the class prefix and icon library repeated if your app sets them (see [Nested ConfigProviders](https://bestax.io/docs/api/helpers/config#nested-configproviders)).\n\nWhat gets portaled is `position: fixed`, so put the element where no ancestor has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`. Any of those becomes the containing block for the overlays and panels, which then cover or line up against that ancestor instead of the viewport. An ancestor that starts a stacking context, with a `z-index` or `isolation: isolate`, keeps them inside it, under anything the page stacks above it.\n\n`NotificationContainer` portals its status region along with its notifications, so the `portalContainer` also decides where announcements come from. Keep it out of any `aria-hidden` ancestor, which takes the region out of the accessibility tree and silences them.\n\n### Using Configuration in Custom Components\n\nAccess the configuration in your own components using the `useConfig` hook:\n\n```tsx\nimport { useConfig } from '@allxsmith/bestax-bulma';\n\nfunction CustomComponent() {\n const { classPrefix } = useConfig();\n const className = classPrefix\n ? `${classPrefix}custom-component`\n : 'custom-component';\n\n return (\n <div className={className}>\n My custom component with prefix: {classPrefix || 'none'}\n </div>\n );\n}\n```\n\n---\n\n## Use Cases\n\n### Framework Integration\n\nWhen integrating Bulma with other CSS frameworks like Bootstrap or Tailwind CSS:\n\n```tsx\n<ConfigProvider classPrefix=\"bestax-\">\n <App />\n</ConfigProvider>\n```\n\nThis ensures all Bulma classes are prefixed with `bestax-`, preventing conflicts with other framework classes.\n\n### Multi-tenant Applications\n\nFor applications that need to support different styling themes or brands:\n\n```tsx\n<ConfigProvider classPrefix={`${tenantName}-`}>\n <TenantApp />\n</ConfigProvider>\n```\n\n### Component Library Wrapping\n\nWhen wrapping Bulma UI components in your own component library:\n\n```tsx\n<ConfigProvider classPrefix=\"mylib-\">\n <MyComponentLibrary />\n</ConfigProvider>\n```\n\n---\n\n## Notes\n\n- The `classPrefix` is applied to **all Bulma classes**:\n - Main component classes (e.g., `button` → `bestax-button`, `box` → `bestax-box`)\n - Helper/modifier classes (e.g., `is-primary` → `bestax-is-primary`, `has-text-centered` → `bestax-has-text-centered`)\n - Spacing classes (e.g., `m-4` → `bestax-m-4`, `p-2` → `bestax-p-2`)\n - All other Bulma utility classes\n- Changes to the `classPrefix` will affect all child components immediately\n- If no `classPrefix` is provided, components use standard Bulma class names\n- The ConfigProvider must wrap all components that should inherit the configuration\n\n---\n\n## TypeScript Support\n\nFull TypeScript support is included:\n\n```tsx\ninterface ConfigContextProps {\n classPrefix?: string;\n iconLibrary?: 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols';\n portalContainer?: string | HTMLElement;\n}\n\ninterface ConfigProviderProps {\n children: ReactNode;\n classPrefix?: string;\n iconLibrary?: 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols';\n portalContainer?: string | HTMLElement;\n}\n```\n\n---\n\n## Props\n\n| Prop | Type | Description |\n| ----------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `children` | `ReactNode` | The child components that will inherit the configuration settings. |\n| `classPrefix` | `string` | Optional prefix to add to all Bulma CSS classes. Useful for namespacing when integrating with other frameworks. |\n| `iconLibrary` | `'fa' \\| 'mdi' \\| 'ion' \\| 'material-icons' \\| 'material-symbols'` | Default icon library for all child components. |\n| `portalContainer` | `string` \\| `HTMLElement` | Where portaled content goes when it has no target of its own: what a `Portal` without a `container` renders, and what the library's components portal, such as `Popover`'s `appendToBody` panel or a `Modal` with `portal`. An element, or a `document.querySelector` selector. Unset, or a selector that matches nothing, it goes to `document.body`. A target a component is given still wins. |"
64
+ "doc": "# ConfigProvider\n\n## Overview\n\nThe `ConfigProvider` component provides a React context for configuring global settings across all Bulma UI components. Its `classPrefix` is applied automatically to **all Bulma CSS classes**, including both main component classes (like `button`, `box`, `title`) and helper/modifier classes (like `is-primary`, `has-text-centered`, `m-4`), allowing you to namespace Bulma classes to avoid conflicts with other CSS frameworks or customize the class naming convention.\n\n---\n\n## Import\n\n```tsx\nimport { ConfigProvider, useConfig } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Hooks\n\n### useConfig\n\nReturns the current configuration context values.\n\n```tsx\nconst { classPrefix } = useConfig();\n```\n\n### useClassPrefix\n\nConvenience hook that returns just the classPrefix string (or empty string if not set).\n\n```tsx\nconst classPrefix = useClassPrefix();\n```\n\n---\n\n## Usage Examples\n\n### Basic Usage with Class Prefix\n\nUse `ConfigProvider` to add a prefix to all Bulma CSS classes within its scope:\n\n```tsx\nimport {\n ConfigProvider,\n Box,\n Title,\n Button,\n Notification,\n} from '@allxsmith/bestax-bulma';\n\nfunction App() {\n return (\n <div>\n <Title size=\"3\">Prefixed Bulma Components</Title>\n <p>\n This demonstrates using ConfigProvider with{' '}\n <code>classPrefix=\"bestax-\"</code> to add the \"bestax-\" prefix to all\n component CSS classes. This is useful when integrating Bulma with other\n CSS frameworks or when you need to namespace Bulma classes to avoid\n conflicts.\n </p>\n\n <Notification color=\"info\" mt=\"4\">\n <strong>Tip:</strong> Open your browser's developer tools and inspect\n the DOM elements below. You'll see that all Bulma CSS classes have the\n \"bestax-\" prefix applied.\n </Notification>\n\n <ConfigProvider classPrefix=\"bestax-\">\n <Box mt=\"4\" p=\"4\">\n {/* \n This Box will have classes:\n - bestax-box (main class)\n - bestax-mt-4 (helper class)\n - bestax-p-4 (helper class)\n */}\n <Title size=\"4\">Prefixed Components</Title>\n {/* \n This Title will have classes:\n - bestax-title (main class)\n - bestax-is-4 (modifier class)\n */}\n <p>\n All components inside this ConfigProvider will have their CSS\n classes prefixed with \"bestax-\". This allows you to use Bulma\n alongside other CSS frameworks without class name conflicts.\n </p>\n <Button color=\"primary\" mt=\"3\" isRounded>\n {/* \n This Button will have classes:\n - bestax-button (main class)\n - bestax-is-primary (modifier class)\n - bestax-is-rounded (modifier class)\n - bestax-mt-3 (helper class)\n */}\n Bulma-Prefixed Button\n </Button>\n </Box>\n </ConfigProvider>\n\n <Box mt=\"4\" p=\"4\">\n <Title size=\"4\">Standard Components (No Prefix)</Title>\n <p>\n Components outside the ConfigProvider use standard Bulma classes\n without any prefix.\n </p>\n <Button color=\"info\">Standard Button</Button>\n </Box>\n </div>\n );\n}\n```\n\n### Nested ConfigProviders\n\nConfigProviders can be nested. An inner provider replaces the outer one's settings rather than adding to them, so inside it any setting it leaves out is back to its default:\n\n```tsx\nfunction NestedExample() {\n return (\n <ConfigProvider classPrefix=\"outer-\">\n <Box p=\"4\">\n <Title>Outer Prefix (outer-)</Title>\n\n <ConfigProvider classPrefix=\"inner-\">\n <Box p=\"4\" mt=\"3\">\n <Title>Inner Prefix (inner-)</Title>\n <Button color=\"primary\">Inner Button</Button>\n </Box>\n </ConfigProvider>\n\n <Button color=\"info\" mt=\"3\">\n Outer Button\n </Button>\n </Box>\n </ConfigProvider>\n );\n}\n```\n\n### Where Portaled Content Goes\n\n`portalContainer` sets where portaled content goes when it has no target of its own. That covers a [`Portal`](https://bestax.io/docs/api/helpers/portal) with no `container`, and what the library's components portal, such as the panel `Popover` renders with `appendToBody`, a `Modal` or `Dialog` with `portal` set to `true`, a `Toast`, or a `Sidebar`. It takes an element or a `document.querySelector` selector. Unset, that content goes to `document.body`.\n\nA popup opened inside a `Modal`, `Dialog` or `Command.Dialog`, such as a `Popover`, `HoverCard` or `ContextMenu` panel, or a `Combobox` menu or picker popover with `appendToBody`, goes into that dialog instead of `document.body`. Each of those sets `aria-modal`, and a screen reader that honors it hides everything outside the dialog. A `portalContainer` you set still takes those popups, so with one set outside the dialog, point a `ConfigProvider` inside the dialog at an element there. A `Sidebar` doesn't do this: its open panel is transformed, which would place a fixed popup against the panel rather than the viewport.\n\nA target a component is given still wins, such as a `Modal`'s `portal` selector or a `Toast`'s `container`. And `portalContainer` only changes where portaled content goes: a component that renders in place, like a `Modal` without `portal` or a `Toast` with `inline`, still does.\n\n`document.body` is outside any styles scoped to where the content was declared: a scoped `Theme`'s variables, or a shadow root's stylesheets. To keep them, render an element inside that area and make it the `portalContainer`, held in state from a ref callback. Below, the panel keeps the purple primary its `Theme` sets, which it would lose at the end of the page:\n\n```tsx live\nfunction ThemedOverlays() {\n const [overlays, setOverlays] = useState<HTMLElement | null>(null);\n\n return (\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <ConfigProvider portalContainer={overlays ?? undefined}>\n <Popover\n appendToBody\n ariaLabel=\"Themed panel\"\n trigger={<Button color=\"primary\">Open</Button>}\n >\n <Popover.Body>\n <Button color=\"primary\">Still purple</Button>\n </Popover.Body>\n </Popover>\n </ConfigProvider>\n <div ref={setOverlays} />\n </Theme>\n );\n}\n```\n\nA selector can't find an element inside a shadow root, since `document.querySelector` doesn't look there, so in a shadow root pass the element itself, as above. Content that portals before the element is set goes to `document.body` and moves when it is, which remounts it and resets its state, as [`Portal`'s `container`](https://bestax.io/docs/api/helpers/portal#into-a-container) describes. A nested provider like this one needs the class prefix and icon library repeated if your app sets them (see [Nested ConfigProviders](https://bestax.io/docs/api/helpers/config#nested-configproviders)).\n\nWhat gets portaled is `position: fixed`, so put the element where no ancestor has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`. Any of those becomes the containing block for the overlays and panels, which then cover or line up against that ancestor instead of the viewport. An ancestor that starts a stacking context, with a `z-index` or `isolation: isolate`, keeps them inside it, under anything the page stacks above it.\n\n`NotificationContainer` portals its status region along with its notifications, so the `portalContainer` also decides where announcements come from. Keep it out of any `aria-hidden` ancestor, which takes the region out of the accessibility tree and silences them.\n\n### Using Configuration in Custom Components\n\nAccess the configuration in your own components using the `useConfig` hook:\n\n```tsx\nimport { useConfig } from '@allxsmith/bestax-bulma';\n\nfunction CustomComponent() {\n const { classPrefix } = useConfig();\n const className = classPrefix\n ? `${classPrefix}custom-component`\n : 'custom-component';\n\n return (\n <div className={className}>\n My custom component with prefix: {classPrefix || 'none'}\n </div>\n );\n}\n```\n\n---\n\n## Use Cases\n\n### Framework Integration\n\nWhen integrating Bulma with other CSS frameworks like Bootstrap or Tailwind CSS:\n\n```tsx\n<ConfigProvider classPrefix=\"bestax-\">\n <App />\n</ConfigProvider>\n```\n\nThis ensures all Bulma classes are prefixed with `bestax-`, preventing conflicts with other framework classes.\n\n### Multi-tenant Applications\n\nFor applications that need to support different styling themes or brands:\n\n```tsx\n<ConfigProvider classPrefix={`${tenantName}-`}>\n <TenantApp />\n</ConfigProvider>\n```\n\n### Component Library Wrapping\n\nWhen wrapping Bulma UI components in your own component library:\n\n```tsx\n<ConfigProvider classPrefix=\"mylib-\">\n <MyComponentLibrary />\n</ConfigProvider>\n```\n\n---\n\n## Notes\n\n- The `classPrefix` is applied to **all Bulma classes**:\n - Main component classes (e.g., `button` → `bestax-button`, `box` → `bestax-box`)\n - Helper/modifier classes (e.g., `is-primary` → `bestax-is-primary`, `has-text-centered` → `bestax-has-text-centered`)\n - Spacing classes (e.g., `m-4` → `bestax-m-4`, `p-2` → `bestax-p-2`)\n - All other Bulma utility classes\n- Changes to the `classPrefix` will affect all child components immediately\n- If no `classPrefix` is provided, components use standard Bulma class names\n- The ConfigProvider must wrap all components that should inherit the configuration\n\n---\n\n## TypeScript Support\n\nFull TypeScript support is included:\n\n```tsx\ninterface ConfigContextProps {\n classPrefix?: string;\n iconLibrary?: 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols';\n portalContainer?: string | HTMLElement;\n}\n\ninterface ConfigProviderProps {\n children: ReactNode;\n classPrefix?: string;\n iconLibrary?: 'fa' | 'mdi' | 'ion' | 'material-icons' | 'material-symbols';\n portalContainer?: string | HTMLElement;\n}\n```\n\n---\n\n## Props\n\n| Prop | Type | Description |\n| ----------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `children` | `ReactNode` | The child components that will inherit the configuration settings. |\n| `classPrefix` | `string` | Optional prefix to add to all Bulma CSS classes. Useful for namespacing when integrating with other frameworks. |\n| `iconLibrary` | `'fa' \\| 'mdi' \\| 'ion' \\| 'material-icons' \\| 'material-symbols'` | Default icon library for all child components. |\n| `portalContainer` | `string` \\| `HTMLElement` | Where portaled content goes when it has no target of its own: what a `Portal` without a `container` renders, and what the library's components portal, such as `Popover`'s `appendToBody` panel or a `Modal` with `portal`. An element, or a `document.querySelector` selector. Unset, or a selector that matches nothing, it goes to `document.body`. A target a component is given still wins. |"
65
65
  }
@@ -33,10 +33,6 @@
33
33
  "title": "Turning It Off",
34
34
  "code": "function example() {\n const [disabled, setDisabled] = useState(false);\n\n return (\n <>\n <Switch\n mb=\"3\"\n checked={disabled}\n onChange={event => setDisabled(event.target.checked)}\n >\n Use the browser's menu\n </Switch>\n <ContextMenu\n aria-label=\"Actions for notes.txt\"\n disabled={disabled}\n trigger={\n <Box tabIndex={0} p=\"6\" textAlign=\"centered\">\n notes.txt\n </Box>\n }\n >\n <ContextMenu.Item as=\"button\">Rename</ContextMenu.Item>\n <ContextMenu.Item as=\"button\">Delete</ContextMenu.Item>\n </ContextMenu>\n </>\n );\n}"
35
35
  },
36
- {
37
- "title": "Where the Menu Goes",
38
- "code": "function RenameDialog({ active, onClose }) {\n const [overlays, setOverlays] = useState<HTMLElement | null>(null);\n\n return (\n <Modal active={active} onClose={onClose}>\n <ConfigProvider portalContainer={overlays ?? undefined}>\n <ContextMenu\n aria-label=\"Actions for report.pdf\"\n trigger={<Box tabIndex={0}>report.pdf</Box>}\n >\n <ContextMenu.Item as=\"button\">Rename</ContextMenu.Item>\n </ContextMenu>\n </ConfigProvider>\n <div ref={setOverlays} />\n </Modal>\n );\n}"
39
- },
40
36
  {
41
37
  "title": "Compound (dot-notation) usage",
42
38
  "code": "<ContextMenu\n aria-label=\"Actions for notes.txt\"\n trigger={\n <Box tabIndex={0} p=\"6\" textAlign=\"centered\">\n notes.txt\n </Box>\n }\n>\n <ContextMenu.Item as=\"button\">Open</ContextMenu.Item>\n <ContextMenu.Item as=\"button\">Rename</ContextMenu.Item>\n <ContextMenu.Divider />\n <ContextMenu.Item as=\"button\">Delete</ContextMenu.Item>\n</ContextMenu>"
@@ -52,14 +48,14 @@
52
48
  "useBulmaClasses"
53
49
  ],
54
50
  "storybook": "https://bestax.io/storybook/?path=/story/components-contextmenu--default",
55
- "summary": "The `ContextMenu` component opens a menu of actions where you right-click an element, such as a table row or a card, or long press it on a touch screen. Shift+F10 and the context menu key open it too, where the browser fires `contextmenu` for them, which Windows and Linux do. The menu is Dropdown's markup, portaled into the `portalContainer` a `ConfigProvider` sets, or the end of the trigger's own document's `<body>`, so it never has server markup. It opens below and to the right of the point, flips above or to the left where there's no room, moves in where it fits neither way, and scrolls inside when it's taller than the viewport. It doesn't follow the trigger, since scrolling closes it. Inside a `Modal` or a shadow root, point `portalContainer` inside it, so screen readers that honor `aria-modal` reach the menu and the shadow root's styles apply. Opening moves focus to the first enabled item. The arrow keys, Home, End and typing move between items, and Enter and Space activate one. Escape, Tab, an item, a press outside, scrolling, resizing and the window losing focus close it, handing focus back to what had it unless an item moved it somewhere else. For a menu opened from a button use `Dropdown`.",
51
+ "summary": "The `ContextMenu` component opens a menu of actions where you right-click an element, such as a table row or a card, or long press it on a touch screen. Shift+F10 and the context menu key open it too, where the browser fires `contextmenu` for them, which Windows and Linux do. The menu is Dropdown's markup, portaled, so it never has server markup: into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, where screen readers that honor the dialog's `aria-modal` reach it, else the end of the trigger's own document's `<body>`. It opens below and to the right of the point, flips above or to the left where there's no room, moves in where it fits neither way, and scrolls inside when it's taller than the viewport. It doesn't follow the trigger, since scrolling closes it. Inside a shadow root, point `portalContainer` inside it, so the shadow root's styles apply. Opening moves focus to the first enabled item. The arrow keys, Home, End and typing move between items, and Enter and Space activate one. Escape, Tab, an item, a press outside, scrolling, resizing and the window losing focus close it, handing focus back to what had it unless an item moved it somewhere else. For a menu opened from a button use `Dropdown`.",
56
52
  "import": "import { ContextMenu } from '@allxsmith/bestax-bulma';",
57
53
  "sourceFile": "bulma-ui/src/components/ContextMenu.tsx",
58
54
  "rootClass": "context-menu",
59
55
  "parts": [
60
56
  {
61
57
  "path": "ContextMenu",
62
- "summary": "The `ContextMenu` component opens a menu of actions where you right-click an element, such as a table row or a card, or long press it on a touch screen. Shift+F10 and the context menu key open it too, where the browser fires `contextmenu` for them, which Windows and Linux do. The menu is Dropdown's markup, portaled into the `portalContainer` a `ConfigProvider` sets, or the end of the trigger's own document's `<body>`, so it never has server markup. It opens below and to the right of the point, flips above or to the left where there's no room, moves in where it fits neither way, and scrolls inside when it's taller than the viewport. It doesn't follow the trigger, since scrolling closes it. Inside a `Modal` or a shadow root, point `portalContainer` inside it, so screen readers that honor `aria-modal` reach the menu and the shadow root's styles apply. Opening moves focus to the first enabled item. The arrow keys, Home, End and typing move between items, and Enter and Space activate one. Escape, Tab, an item, a press outside, scrolling, resizing and the window losing focus close it, handing focus back to what had it unless an item moved it somewhere else. For a menu opened from a button use `Dropdown`.",
58
+ "summary": "The `ContextMenu` component opens a menu of actions where you right-click an element, such as a table row or a card, or long press it on a touch screen. Shift+F10 and the context menu key open it too, where the browser fires `contextmenu` for them, which Windows and Linux do. The menu is Dropdown's markup, portaled, so it never has server markup: into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, where screen readers that honor the dialog's `aria-modal` reach it, else the end of the trigger's own document's `<body>`. It opens below and to the right of the point, flips above or to the left where there's no room, moves in where it fits neither way, and scrolls inside when it's taller than the viewport. It doesn't follow the trigger, since scrolling closes it. Inside a shadow root, point `portalContainer` inside it, so the shadow root's styles apply. Opening moves focus to the first enabled item. The arrow keys, Home, End and typing move between items, and Enter and Space activate one. Escape, Tab, an item, a press outside, scrolling, resizing and the window losing focus close it, handing focus back to what had it unless an item moved it somewhere else. For a menu opened from a button use `Dropdown`.",
63
59
  "component": null,
64
60
  "props": [
65
61
  {
@@ -178,7 +178,7 @@
178
178
  "code": "function example() {\n return (\n <Field\n horizontal\n label=\"Date\"\n labelProps={{ htmlFor: 'date-field-control' }}\n >\n <Field.Body>\n <Field>\n <Control iconLeftName=\"calendar-alt\">\n <DateInput id=\"date-field-control\" placeholder=\"YYYY-MM-DD\" />\n </Control>\n </Field>\n </Field.Body>\n </Field>\n );\n}"
179
179
  }
180
180
  ],
181
- "accessibility": "- Trigger uses `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded`, and `aria-controls`.\n- Popover panel has `role=\"dialog\"` with an accessible name. Opening it puts focus on the focused date, month or year.\n- Closing the popover with `Escape` or a pick returns focus to the input, whether the input or the launcher opened it. Under `openOnFocus` that returning focus leaves the popover closed; focusing or clicking the input again opens it. Dismissing without a pick commits nothing: an empty field stays empty, and leaving afterwards commits only what you typed.\n- Calendar uses `role=\"grid\"` with cells as `role=\"gridcell\"`.\n- Cells expose `aria-selected`, `aria-disabled`, and `aria-current=\"date\"` for today. On a server-rendered page, today's day, month and year get it once the page hydrates, so the reader's clock decides which.\n- Roving `tabindex` keeps a single grid cell focusable at a time, and focus moves with it, inline as in the popover. When the focused date is disabled, that cell is the nearest enabled day of the month.\n- The month grid is a `role=\"grid\"` of `role=\"row\"`s, labelled by the year in its header. Each month cell carries the full month name as its accessible name, `aria-current=\"date\"` for this month and `aria-disabled` for a month with no selectable day.\n- The year list is a `role=\"listbox\"` named by the `chooseYear` label. As the selection surface its options take `aria-selected` from the value's year rather than from focus, which roves through the list. When the focused year is disabled, the nearest enabled year takes the tab stop, so `Tab` still reaches the list.\n- The launcher and popover are named \"Choose month\" or \"Choose year\" to match the granularity; override them, and the month grid's \"Previous year\" / \"Next year\" buttons, through `labels` (`chooseMonth`, `chooseYear`, `prevYear`, `nextYear`).\n- Honors `prefers-reduced-motion` (skip popover fade-in).\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start, and the previous-month chevron points right. The month names follow the page.",
181
+ "accessibility": "- Trigger uses `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded`, and `aria-controls`.\n- Popover panel has `role=\"dialog\"` with an accessible name. Opening it puts focus on the focused date, month or year.\n- Closing the popover with `Escape` or a pick returns focus to the input, whether the input or the launcher opened it. Under `openOnFocus` that returning focus leaves the popover closed; focusing or clicking the input again opens it. Dismissing without a pick commits nothing: an empty field stays empty, and leaving afterwards commits only what you typed.\n- Calendar uses `role=\"grid\"` with cells as `role=\"gridcell\"`.\n- Cells expose `aria-selected`, `aria-disabled`, and `aria-current=\"date\"` for today. On a server-rendered page, today's day, month and year get it once the page hydrates, so the reader's clock decides which.\n- A `disabled` inline calendar puts `aria-disabled=\"true\"` on its grid or year list and disables every button in it. A `readOnly` one puts `aria-readonly=\"true\"` on the grid or year list it picks from, and keeps its tab stop and keys.\n- Roving `tabindex` keeps a single grid cell focusable at a time, and focus moves with it, inline as in the popover. When the focused date is disabled, that cell is the nearest enabled day of the month.\n- The month grid is a `role=\"grid\"` of `role=\"row\"`s, labelled by the year in its header. Each month cell carries the full month name as its accessible name, `aria-current=\"date\"` for this month and `aria-disabled` for a month with no selectable day.\n- The year list is a `role=\"listbox\"` named by the `chooseYear` label. As the selection surface its options take `aria-selected` from the value's year rather than from focus, which roves through the list. When the focused year is disabled, the nearest enabled year takes the tab stop, so `Tab` still reaches the list.\n- The launcher and popover are named \"Choose month\" or \"Choose year\" to match the granularity; override them, and the month grid's \"Previous year\" / \"Next year\" buttons, through `labels` (`chooseMonth`, `chooseYear`, `prevYear`, `nextYear`).\n- Honors `prefers-reduced-motion` (skip popover fade-in).\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start, and the previous-month chevron points right. The month names follow the page.",
182
182
  "related": ["DateTimeInput", "Input", "TimeInput"],
183
183
  "storybook": "https://bestax.io/storybook/?path=/story/form-dateinput",
184
184
  "summary": "The `DateInput` component is a form input that opens a popover calendar for date selection.",
@@ -429,7 +429,7 @@
429
429
  "name": "disabled",
430
430
  "type": "boolean",
431
431
  "default": "false",
432
- "description": "Disable the input.",
432
+ "description": "Disable the input. An `inline` calendar disables its days and header buttons, but its hidden input still submits the value, where a disabled input submits nothing.",
433
433
  "required": false,
434
434
  "inherited": true,
435
435
  "deprecated": false
@@ -438,7 +438,7 @@
438
438
  "name": "readOnly",
439
439
  "type": "boolean",
440
440
  "default": "false",
441
- "description": "Make the input read-only.",
441
+ "description": "Make the input read-only. An `inline` calendar still moves through the dates by key and by its header, but a click, Enter or Space picks nothing.",
442
442
  "required": false,
443
443
  "inherited": true,
444
444
  "deprecated": false
@@ -555,7 +555,7 @@
555
555
  "name": "appendToBody",
556
556
  "type": "boolean",
557
557
  "default": "false",
558
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
558
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
559
559
  "required": false,
560
560
  "inherited": true,
561
561
  "deprecated": false
@@ -791,7 +791,7 @@
791
791
  "name": "disabled",
792
792
  "type": "boolean",
793
793
  "default": "false",
794
- "description": "Disable the input.",
794
+ "description": "Disable the input. An `inline` calendar disables its days and header buttons, but its hidden input still submits the value, where a disabled input submits nothing.",
795
795
  "required": false,
796
796
  "inherited": false,
797
797
  "deprecated": false
@@ -800,7 +800,7 @@
800
800
  "name": "readOnly",
801
801
  "type": "boolean",
802
802
  "default": "false",
803
- "description": "Make the input read-only.",
803
+ "description": "Make the input read-only. An `inline` calendar still moves through the dates by key and by its header, but a click, Enter or Space picks nothing.",
804
804
  "required": false,
805
805
  "inherited": false,
806
806
  "deprecated": false
@@ -917,7 +917,7 @@
917
917
  "name": "appendToBody",
918
918
  "type": "boolean",
919
919
  "default": "false",
920
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
920
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
921
921
  "required": false,
922
922
  "inherited": false,
923
923
  "deprecated": false
@@ -46,7 +46,7 @@
46
46
  "code": "<Field label=\"Leave\">\n <Control iconLeftName=\"calendar-alt\">\n <DateRangeInput />\n </Control>\n</Field>"
47
47
  }
48
48
  ],
49
- "accessibility": "- The root is a `role=\"group\"` named by `label` through `aria-labelledby`, since the label names two inputs rather than one. Inside a labeled `Field`, that `Field`'s label names the group the same way, whether the `Field` holds `DateRangeInput` or a `DateRangeInputBase` you composed in a `Control` yourself, `inline` or not. An `aria-label` or `aria-labelledby` you give the group wins over either label.\n- Each input is a `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded` and `aria-controls`, and its own name: \"Start date\" or \"End date\", from the `labels` keys `rangeStart` and `rangeEnd`. The separator between them is hidden from assistive technology.\n- The popover is a `role=\"dialog\"` named \"Choose date range\" (`labels.chooseDateRange`). Opening it puts focus on the calendar's focused day, and closing it returns focus to the input you were last in, or to the start input.\n- The day grid is `aria-multiselectable`, and its days sit in a `role=\"row\"` per week. Every day of the range is `aria-selected`, both ends included. While the end is still being picked, only the start is: the preview is not a selection.\n- The start and end days are described as \"Start date\" and \"End date\", so moving onto either says which it is. While the end is still being picked, the day the range would end on is described as \"Choose as end date\" (`labels.rangePreviewEnd`).\n- A polite live region in the calendar states the range in words, such as \"Start date: June 10, 2024, End date: June 13, 2024\", and announces each pick. The grid points at it too, so moving into the grid reads the current range.\n- Everything else is the calendar `DateInput` uses: a roving `tabindex`, `aria-disabled` on disabled days and `aria-current=\"date\"` on today.\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start, and the previous-month chevron points right.",
49
+ "accessibility": "- The root is a `role=\"group\"` named by `label` through `aria-labelledby`, since the label names two inputs rather than one. Inside a labeled `Field`, that `Field`'s label names the group the same way, whether the `Field` holds `DateRangeInput` or a `DateRangeInputBase` you composed in a `Control` yourself, `inline` or not. An `aria-label` or `aria-labelledby` you give the group wins over either label.\n- Each input is a `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded` and `aria-controls`, and its own name: \"Start date\" or \"End date\", from the `labels` keys `rangeStart` and `rangeEnd`. The separator between them is hidden from assistive technology.\n- The popover is a `role=\"dialog\"` named \"Choose date range\" (`labels.chooseDateRange`). Opening it puts focus on the calendar's focused day, and closing it returns focus to the input you were last in, or to the start input.\n- The day grid is `aria-multiselectable`, and its days sit in a `role=\"row\"` per week. Every day of the range is `aria-selected`, both ends included. While the end is still being picked, only the start is: the preview is not a selection.\n- A `disabled` inline calendar puts `aria-disabled=\"true\"` on its grid and disables every button in it. A `readOnly` one puts `aria-readonly=\"true\"` there instead, keeps its tab stop and keys, and picks nothing.\n- The start and end days are described as \"Start date\" and \"End date\", so moving onto either says which it is. While the end is still being picked, the day the range would end on is described as \"Choose as end date\" (`labels.rangePreviewEnd`).\n- A polite live region in the calendar states the range in words, such as \"Start date: June 10, 2024, End date: June 13, 2024\", and announces each pick. The grid points at it too, so moving into the grid reads the current range.\n- Everything else is the calendar `DateInput` uses: a roving `tabindex`, `aria-disabled` on disabled days and `aria-current=\"date\"` on today.\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start, and the previous-month chevron points right.",
50
50
  "related": ["DateInput", "DateTimeInput", "Field"],
51
51
  "storybook": "https://bestax.io/storybook/?path=/story/form-daterangeinput",
52
52
  "summary": "The `DateRangeInput` component is a form input for a start and end date, picked from one popover calendar or typed into two segmented inputs.",
@@ -297,7 +297,7 @@
297
297
  "name": "disabled",
298
298
  "type": "boolean",
299
299
  "default": "false",
300
- "description": "Disable both inputs and the launcher.",
300
+ "description": "Disable both inputs and the launcher, or an `inline` calendar's days and header buttons. The hidden inputs still submit the range, in every mode, where a disabled input submits nothing.",
301
301
  "required": false,
302
302
  "inherited": true,
303
303
  "deprecated": false
@@ -306,7 +306,7 @@
306
306
  "name": "readOnly",
307
307
  "type": "boolean",
308
308
  "default": "false",
309
- "description": "Make both inputs read-only and keep the popover closed.",
309
+ "description": "Make both inputs read-only and keep the popover closed. An `inline` calendar still moves through the dates by key and by its header, but a click, Enter or Space picks nothing.",
310
310
  "required": false,
311
311
  "inherited": true,
312
312
  "deprecated": false
@@ -414,7 +414,7 @@
414
414
  "name": "appendToBody",
415
415
  "type": "boolean",
416
416
  "default": "false",
417
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
417
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
418
418
  "required": false,
419
419
  "inherited": true,
420
420
  "deprecated": false
@@ -686,7 +686,7 @@
686
686
  "name": "disabled",
687
687
  "type": "boolean",
688
688
  "default": "false",
689
- "description": "Disable both inputs and the launcher.",
689
+ "description": "Disable both inputs and the launcher, or an `inline` calendar's days and header buttons. The hidden inputs still submit the range, in every mode, where a disabled input submits nothing.",
690
690
  "required": false,
691
691
  "inherited": false,
692
692
  "deprecated": false
@@ -695,7 +695,7 @@
695
695
  "name": "readOnly",
696
696
  "type": "boolean",
697
697
  "default": "false",
698
- "description": "Make both inputs read-only and keep the popover closed.",
698
+ "description": "Make both inputs read-only and keep the popover closed. An `inline` calendar still moves through the dates by key and by its header, but a click, Enter or Space picks nothing.",
699
699
  "required": false,
700
700
  "inherited": false,
701
701
  "deprecated": false
@@ -803,7 +803,7 @@
803
803
  "name": "appendToBody",
804
804
  "type": "boolean",
805
805
  "default": "false",
806
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
806
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
807
807
  "required": false,
808
808
  "inherited": false,
809
809
  "deprecated": false
@@ -170,7 +170,7 @@
170
170
  "code": "<DateTimeInput label=\"Input only\" popover={false} defaultValue={new Date()} />"
171
171
  }
172
172
  ],
173
- "accessibility": "- Trigger uses `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded`, and `aria-controls`.\n- Popover panel has `role=\"dialog\"` with an accessible name. Opening it puts focus on the focused date.\n- Closing the popover with `Escape`, the ✓ button or `Enter` on a time wheel returns focus to the input, whether the input or the launcher opened it. Under `openOnFocus` that returning focus leaves the popover closed; focusing or clicking the input again opens it. Closing it commits nothing by itself: an empty field stays empty, and leaving afterwards commits only what you typed, so seconds the display leaves out are kept.\n- Calendar uses `role=\"grid\"`; cells expose `aria-selected`, `aria-disabled`, and `aria-current=\"date\"` for today. On a server-rendered page, today gets it once the page hydrates, so the reader's clock decides which day.\n- Roving `tabindex` keeps a single day focusable at a time, and focus moves with it, inline as in the popover. When the focused date is disabled, that cell is the nearest enabled day of the month.\n- Each time wheel uses `role=\"spinbutton\"` with `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, and `aria-valuetext`. The values drawn above and below are click targets hidden from assistive technology, which reads the value from the spinbutton.\n- In a 12-hour picker, AM/PM is one of those wheels, reading `AM` or `PM`. Its accessible name is `AM/PM` unless `labels.ampm` sets another.\n- The footer's confirm button exposes an accessible label (`Done`); the Reset button reverts your edits to the value the popover opened with.\n- Tab order walks from the calendar to the footer (Reset / ✓), and from the time wheels to the footer while they are open.\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start. The time wheels keep `dir=\"ltr\"`.",
173
+ "accessibility": "- Trigger uses `role=\"combobox\"` with `aria-haspopup=\"dialog\"`, `aria-expanded`, and `aria-controls`.\n- Popover panel has `role=\"dialog\"` with an accessible name. Opening it puts focus on the focused date.\n- Closing the popover with `Escape`, the ✓ button or `Enter` on a time wheel returns focus to the input, whether the input or the launcher opened it. Under `openOnFocus` that returning focus leaves the popover closed; focusing or clicking the input again opens it. Closing it commits nothing by itself: an empty field stays empty, and leaving afterwards commits only what you typed, so seconds the display leaves out are kept.\n- Calendar uses `role=\"grid\"`; cells expose `aria-selected`, `aria-disabled`, and `aria-current=\"date\"` for today. On a server-rendered page, today gets it once the page hydrates, so the reader's clock decides which day.\n- Roving `tabindex` keeps a single day focusable at a time, and focus moves with it, inline as in the popover. When the focused date is disabled, that cell is the nearest enabled day of the month.\n- A `disabled` inline panel puts `aria-disabled=\"true\"` on its grid and disables every button in it. A `readOnly` one puts `aria-readonly=\"true\"` there instead, and keeps its tab stop and keys.\n- Each time wheel uses `role=\"spinbutton\"` with `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, and `aria-valuetext`. The values drawn above and below are click targets hidden from assistive technology, which reads the value from the spinbutton.\n- In a 12-hour picker, AM/PM is one of those wheels, reading `AM` or `PM`. Its accessible name is `AM/PM` unless `labels.ampm` sets another.\n- The footer's confirm button exposes an accessible label (`Done`); the Reset button reverts your edits to the value the popover opened with.\n- Tab order walks from the calendar to the footer (Reset / ✓), and from the time wheels to the footer while they are open.\n- On a right-to-left page the launcher sits at the end of the line and the calendar icon at the start. The time wheels keep `dir=\"ltr\"`.",
174
174
  "related": ["DateInput", "Input", "TimeInput"],
175
175
  "storybook": "https://bestax.io/storybook/?path=/story/form-datetimeinput",
176
176
  "summary": "The `DateTimeInput` combines a calendar and a time **wheel spinner** in a single popover — an iOS-style layout.",
@@ -421,7 +421,7 @@
421
421
  "name": "disabled",
422
422
  "type": "boolean",
423
423
  "default": "false",
424
- "description": "Disable the input.",
424
+ "description": "Disable the input. An `inline` panel disables its days, header buttons, Time, Reset and ✓, but its hidden input still submits the value, where a disabled input submits nothing.",
425
425
  "required": false,
426
426
  "inherited": true,
427
427
  "deprecated": false
@@ -430,7 +430,7 @@
430
430
  "name": "readOnly",
431
431
  "type": "boolean",
432
432
  "default": "false",
433
- "description": "Read-only input.",
433
+ "description": "Read-only input. An `inline` panel still moves through the dates by key and by its header, but a click, Enter or Space picks nothing, and Time and Reset are disabled, so the time can't change either.",
434
434
  "required": false,
435
435
  "inherited": true,
436
436
  "deprecated": false
@@ -538,7 +538,7 @@
538
538
  "name": "appendToBody",
539
539
  "type": "boolean",
540
540
  "default": "false",
541
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
541
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
542
542
  "required": false,
543
543
  "inherited": true,
544
544
  "deprecated": false
@@ -846,7 +846,7 @@
846
846
  "name": "disabled",
847
847
  "type": "boolean",
848
848
  "default": "false",
849
- "description": "Disable the input.",
849
+ "description": "Disable the input. An `inline` panel disables its days, header buttons, Time, Reset and ✓, but its hidden input still submits the value, where a disabled input submits nothing.",
850
850
  "required": false,
851
851
  "inherited": false,
852
852
  "deprecated": false
@@ -855,7 +855,7 @@
855
855
  "name": "readOnly",
856
856
  "type": "boolean",
857
857
  "default": "false",
858
- "description": "Read-only input.",
858
+ "description": "Read-only input. An `inline` panel still moves through the dates by key and by its header, but a click, Enter or Space picks nothing, and Time and Reset are disabled, so the time can't change either.",
859
859
  "required": false,
860
860
  "inherited": false,
861
861
  "deprecated": false
@@ -963,7 +963,7 @@
963
963
  "name": "appendToBody",
964
964
  "type": "boolean",
965
965
  "default": "false",
966
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default.",
966
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
967
967
  "required": false,
968
968
  "inherited": false,
969
969
  "deprecated": false
@@ -24,7 +24,7 @@
24
24
  },
25
25
  {
26
26
  "title": "In a Notification",
27
- "code": "<Notification color=\"primary\">\n <Delete bgColor=\"warning\" ariaLabel=\"Close notification\" />\n <Paragraph>This is a primary notification with a delete button.</Paragraph>\n</Notification>"
27
+ "code": "<Notification color=\"primary\">\n <Delete ariaLabel=\"Close notification\" />\n <Paragraph>This is a primary notification with a delete button.</Paragraph>\n</Notification>"
28
28
  },
29
29
  {
30
30
  "title": "In a Message Header",
@@ -30,7 +30,7 @@
30
30
  "code": "function example() {\n const [showDialog, setShowDialog] = useState(false);\n return (\n <Block>\n <Button onClick={() => setShowDialog(true)}>Show Portaled Dialog</Button>\n <Dialog\n isOpen={showDialog}\n title=\"Portaled Dialog\"\n message=\"This dialog is portaled, so it escapes any ancestor with overflow: hidden.\"\n onConfirm={() => setShowDialog(false)}\n showCancel={false}\n portal\n />\n </Block>\n );\n}"
31
31
  }
32
32
  ],
33
- "accessibility": "- Uses `role=\"alertdialog\"` for proper screen reader announcement\n- Has `aria-modal=\"true\"` to indicate modal behavior — note this does not make background content inert; no `inert` attribute is applied, so background content stays reachable by pointer\n- `title` is wired up as `aria-labelledby` and `message` as `aria-describedby`\n- Focus moves to the confirm (or cancel, with `focusCancel`) button when opened\n- Escape key closes the dialog (when `canCancel` is true). The underlying [`Modal`](https://bestax.io/docs/api/components/modal#accessibility) handles it, so one Escape closes the topmost overlay only: a dialog opened over a Modal or a [Sidebar](https://bestax.io/docs/api/components/sidebar) closes first, and the one under it on the next Escape\n- Tab and Shift+Tab cycle within the dialog while it is the topmost modal overlay, also from the underlying [`Modal`](https://bestax.io/docs/api/components/modal)\n- The page doesn't scroll while the dialog is open, through the [`Modal`](https://bestax.io/docs/api/components/modal#accessibility)'s ref-counted lock, shared with `Sidebar` and `Loading`\n- Confirm/cancel buttons are keyboard accessible",
33
+ "accessibility": "- Uses `role=\"alertdialog\"` for proper screen reader announcement\n- Has `aria-modal=\"true\"` to indicate modal behavior — note this does not make background content inert; no `inert` attribute is applied, so background content stays reachable by pointer\n- `title` is wired up as `aria-labelledby` and `message` as `aria-describedby`\n- Focus moves to the confirm (or cancel, with `focusCancel`) button when opened\n- Escape key closes the dialog (when `canCancel` is true). The underlying [`Modal`](https://bestax.io/docs/api/components/modal#accessibility) handles it, so one Escape closes the topmost overlay only: a dialog opened over a Modal or a [Sidebar](https://bestax.io/docs/api/components/sidebar) closes first, and the one under it on the next Escape\n- Tab and Shift+Tab cycle within the dialog while it is the topmost modal overlay, also from the underlying [`Modal`](https://bestax.io/docs/api/components/modal)\n- A popup in the message, such as a `Popover` or `HoverCard` with `appendToBody`, is portaled into the `alertdialog`, the element with `aria-modal`, so a screen reader that honors it reaches the popup. A `portalContainer` a [`ConfigProvider`](https://bestax.io/docs/api/helpers/config#where-portaled-content-goes) sets still takes it\n- The page doesn't scroll while the dialog is open, through the [`Modal`](https://bestax.io/docs/api/components/modal#accessibility)'s ref-counted lock, shared with `Sidebar` and `Loading`\n- Confirm/cancel buttons are keyboard accessible",
34
34
  "related": ["Modal", "Toast"],
35
35
  "storybook": "https://bestax.io/storybook/?path=/story/components-dialog",
36
36
  "summary": "The `Dialog` component provides ready-made confirm and alert dialogs, so a destructive action stays one `await dialog.confirm()` call away.",
@@ -30,7 +30,7 @@
30
30
  "code": "function example() {\n const [open, setOpen] = useState(false);\n const [following, setFollowing] = useState(false);\n\n return (\n <>\n <Paragraph mb=\"3\">\n The card is {open ? 'open' : 'closed'}\n {following ? ', and you follow Ada' : ''}.\n </Paragraph>\n <HoverCard\n open={open}\n onOpenChange={setOpen}\n trigger={<Link href=\"#ada\">Ada Lovelace</Link>}\n >\n <Paragraph mb=\"3\">Analytical Engine team</Paragraph>\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => {\n setFollowing(true);\n setOpen(false);\n }}\n >\n Follow\n </Button>\n </HoverCard>\n </>\n );\n}"
31
31
  }
32
32
  ],
33
- "accessibility": "`HoverCard` is built to\n[WCAG's Content on Hover or Focus](https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html)\ncriterion, condition by condition:\n\n- **Dismissible:** <kbd>Escape</kbd> closes the card wherever focus is, without you moving the\n pointer or focus. In controlled use it asks to close through `onOpenChange`, so the card closes\n when you set `open` to `false`. The keypress belongs to whatever has focus. When that's the card,\n its trigger or nothing, the card takes it, so a `Popover` or `Modal` around it stays open. When\n it's something else, such as a field elsewhere on the page or a `Modal` opened from the card, the\n card still closes and the key goes on to it. With one card inside another, the inner one closes\n first. Focus that was in the card goes back to the trigger, and the card stays closed until the\n pointer or focus leaves and comes back, or, with `openOnTap`, until the next tap on the trigger.\n- **Hoverable:** the pointer can move from the trigger onto the card. `closeDelay` covers the gap\n between them, and the card takes pointer events.\n- **Persistent:** no timer closes a card that's hovered or focused. Focus inside the card holds it\n however it got there. Whether a click focuses what it lands on is up to the browser, though:\n Safari and Firefox on macOS don't focus a button on click, so there a clicked button doesn't keep\n the card open once the pointer moves away.\n\nAnd around it:\n\n- Keyboard focus on the trigger opens the card, as hover does. Focus that a press brings doesn't,\n and neither does a touch pointer, so tapping the trigger on a phone just does what the trigger\n does. Clicking the trigger never toggles the card.\n- By default, then, a phone or tablet never opens the card, so whatever it shows has to be\n reachable from the trigger as well. `openOnTap` opens it with a tap, for a trigger with no tap\n action of its own. While a tap holds it open, a tap outside the trigger and the card closes it.\n- The trigger gets `aria-expanded` and `aria-controls`, which say it's expanded and name the card\n only while the card is on the page. It gets no role, no tab stop and no `aria-haspopup`, so the trigger element itself has to\n take focus, such as a link or a button. A link inside a `<span>` doesn't do, since focus comes\n back to the trigger and its ARIA is on it. A development build warns when the trigger can't take\n focus.\n- The card is a plain element with an `id` and no role. It isn't a dialog, since focus doesn't move\n into it, and it isn't a tooltip, since it can hold links. In place, a screen reader meets it\n right after the trigger, Tab goes from the trigger into its links and buttons, and tabbing past\n them closes it.\n- With `appendToBody` the card sits in the Tab order where its container is. By default that's\n the end of `<body>`, so it comes last: Tab reaches it only after the rest of the page, and focus\n leaving the trigger on the way closes it. Keyboard users can open it, read it and close it, so\n whatever it links to should be reachable from the page as well.\n- Opening the card moves no focus, and nothing traps it. Links and buttons belong in a card; a form\n belongs in a `Popover`, which opens on purpose and holds focus.\n- The fade-in is turned off under `prefers-reduced-motion: reduce`.",
33
+ "accessibility": "`HoverCard` is built to\n[WCAG's Content on Hover or Focus](https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html)\ncriterion, condition by condition:\n\n- **Dismissible:** <kbd>Escape</kbd> closes the card wherever focus is, without you moving the\n pointer or focus. In controlled use it asks to close through `onOpenChange`, so the card closes\n when you set `open` to `false`. The keypress belongs to whatever has focus. When that's the card,\n its trigger or nothing, the card takes it, so a `Popover` or `Modal` around it stays open. When\n it's something else, such as a field elsewhere on the page or a `Modal` opened from the card, the\n card still closes and the key goes on to it. With one card inside another, the inner one closes\n first. Focus that was in the card goes back to the trigger, and the card stays closed until the\n pointer or focus leaves and comes back, or, with `openOnTap`, until the next tap on the trigger.\n- **Hoverable:** the pointer can move from the trigger onto the card. `closeDelay` covers the gap\n between them, and the card takes pointer events.\n- **Persistent:** no timer closes a card that's hovered or focused. Focus inside the card holds it\n however it got there. Whether a click focuses what it lands on is up to the browser, though:\n Safari and Firefox on macOS don't focus a button on click, so there a clicked button doesn't keep\n the card open once the pointer moves away.\n\nAnd around it:\n\n- Keyboard focus on the trigger opens the card, as hover does. Focus that a press brings doesn't,\n and neither does a touch pointer, so tapping the trigger on a phone just does what the trigger\n does. Clicking the trigger never toggles the card.\n- By default, then, a phone or tablet never opens the card, so whatever it shows has to be\n reachable from the trigger as well. `openOnTap` opens it with a tap, for a trigger with no tap\n action of its own. While a tap holds it open, a tap outside the trigger and the card closes it.\n- The trigger gets `aria-expanded` and `aria-controls`, which say it's expanded and name the card\n only while the card is on the page. It gets no role, no tab stop and no `aria-haspopup`, so the trigger element itself has to\n take focus, such as a link or a button. A link inside a `<span>` doesn't do, since focus comes\n back to the trigger and its ARIA is on it. A development build warns when the trigger can't take\n focus.\n- The card is a plain element with an `id` and no role. It isn't a dialog, since focus doesn't move\n into it, and it isn't a tooltip, since it can hold links. In place, a screen reader meets it\n right after the trigger, Tab goes from the trigger into its links and buttons, and tabbing past\n them closes it.\n- With `appendToBody` the card sits in the Tab order where its container is. By default that's\n the end of `<body>`, or of the `Modal`, `Dialog` or `Command.Dialog` it's in, so it comes last:\n Tab reaches it only after the rest of the page or the dialog, and focus leaving the trigger on\n the way closes it. In a dialog, a screen reader that honors its `aria-modal` reaches it there. Keyboard users can open it, read it and close it, so\n whatever it links to should be reachable from the page as well.\n- Opening the card moves no focus, and nothing traps it. Links and buttons belong in a card; a form\n belongs in a `Popover`, which opens on purpose and holds focus.\n- The fade-in is turned off under `prefers-reduced-motion: reduce`.",
34
34
  "related": ["Media", "Popover", "Portal", "Tooltip", "useBulmaClasses"],
35
35
  "storybook": "https://bestax.io/storybook/?path=/story/components-hovercard--default",
36
36
  "summary": "The `HoverCard` component shows a card previewing what's behind a link or a button while the pointer rests on it or keyboard focus is on it. Nothing moves focus when the card opens, and nothing traps it. In place, the card comes right after the trigger, so Tab goes from the trigger into its links and tabbing past them closes it. The pointer can move onto the card, Escape closes it from anywhere, and by default touch doesn't open it, so tapping the trigger just does what the trigger does. `openOnTap` opens it with a tap, for a trigger with no tap action of its own. For hints with nothing to click use `Tooltip`, and for content opened on purpose, such as a form, use `Popover`.",
@@ -119,7 +119,7 @@
119
119
  "name": "appendToBody",
120
120
  "type": "boolean",
121
121
  "default": "false",
122
- "description": "Render the card through `Portal`, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default, fixed to the viewport, so an ancestor's `overflow`, `transform` or stacking context can't clip it. A trigger in running text needs it too, since the card's block content can't sit inside a `<p>`. The card then shows from the commit after hydration, never in server markup, and it sits in the Tab order where its container is. At the end of `<body>`, the default, Tab reaches it only after the rest of the page, and focus leaving the trigger on the way closes it, so keyboard users can open it, read it and close it with Escape, and whatever it links to has to be reachable from the page as well. Theming variables set on an ancestor of the trigger reach it only when its container is inside that ancestor.",
122
+ "description": "Render the card through `Portal`, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`, fixed to the viewport, so an ancestor's `overflow`, `transform` or stacking context can't clip it. A trigger in running text needs it too, since the card's block content can't sit inside a `<p>`. The card then shows from the commit after hydration, never in server markup, and it sits in the Tab order where its container is. At the end of `<body>` or of a dialog, Tab reaches it only after the rest of the page or the dialog, and focus leaving the trigger on the way closes it, so keyboard users can open it, read it and close it with Escape, and whatever it links to has to be reachable from the page as well. Theming variables set on an ancestor of the trigger reach it only when its container is inside that ancestor.",
123
123
  "required": false,
124
124
  "inherited": false,
125
125
  "deprecated": false
@@ -113,5 +113,5 @@
113
113
  }
114
114
  ],
115
115
  "cssVars": [],
116
- "doc": "# LocaleProvider\n\n## Overview\n\n`LocaleProvider` sets the language and writing direction for the part of the app inside it, on a wrapper `<div>` and on what the library portals out of it.\n\nIt takes a `locale` and a `dir`, and each works alone. A `locale` such as `\"ar-EG\"` becomes the wrapper's `lang`, and its script decides the direction, so `dir=\"rtl\"` comes with it. A `dir` sets the direction whatever the locale, for a right-to-left layout with no particular language, or a locale whose script reads one way on a page laid out the other. A nested provider keeps whatever it leaves unset.\n\nThe direction lives on the wrapper as a `dir` attribute, so CSS follows it, and so do the components that read the direction they're laid out in, as they follow `<html dir>`. A page whose `<html>` already has `dir=\"rtl\"` needs no provider for that. What the provider adds is the rest:\n\n- a section of the page in another language or direction than the page around it;\n- the content the library portals to the end of `document.body`, such as a `Popover` panel, a `Modal` with `portal`, a `Toast` or a `Portal`'s children, which leaves the section it came from and gets the provider's `dir` and `lang` back;\n- the date and time pickers inside, which take its `locale` and start the week on that locale's first day;\n- the numbers the library writes, such as page numbers, badge counts and a slider's output, and the filtering in `Combobox`, `Autocomplete`, `Taginput` and `Command`, which follow the `locale` too.\n\nWithout a provider none of this changes: a picker uses the runtime's own locale and starts the week on Sunday, numbers are written as they always have been, and filtering ignores case only.\n\n---\n\n## Import\n\n```tsx\nimport { LocaleProvider, useLocale } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Hooks\n\n### useLocale\n\nReturns the `locale` and `dir` the nearest provider sets, both `undefined` outside one. The locale is in its canonical form, so `\"en-us\"` comes back as `\"en-US\"`. Use it for your own `Intl` calls, so they match the components around them, or for `dir` and `lang` on content you portal yourself.\n\n```tsx\nfunction Price({ amount }: { amount: number }) {\n const { locale } = useLocale();\n return (\n <span>\n {new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: 'EUR',\n }).format(amount)}\n </span>\n );\n}\n```\n\nFor numbers, [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter) and its `useNumberFormat` hook do this for you, and build one formatter for each set of options rather than one on every render.\n\nIt isn't the direction of the page: with no provider, or one that sets no direction, `<html dir>` still decides. To act on the direction an element is laid out in, read `getComputedStyle(element).direction`.\n\n---\n\n## Usage\n\n### A Right-to-Left Section\n\nA `locale` alone sets the direction of its script. Arabic reads right to left, so the wrapper gets `dir=\"rtl\"` and `lang=\"ar-EG\"`, and the steps lay out from the right.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <Steps\n value={1}\n items={[{ label: 'السلة' }, { label: 'الشحن' }, { label: 'الدفع' }]}\n />\n</LocaleProvider>\n```\n\nThe wrapper is a plain `<div>` with no class of its own. It takes the helper props and any other attributes, and forwards its `ref`.\n\n### Direction Only\n\n`dir` sets the direction and leaves the language alone. It also wins over a `locale`'s own direction.\n\n```tsx live\n<LocaleProvider dir=\"rtl\">\n <Buttons>\n <Button color=\"primary\">First</Button>\n <Button>Second</Button>\n </Buttons>\n</LocaleProvider>\n```\n\n### Nested Providers\n\nAn inner provider keeps whatever it leaves unset. One that sets only a `locale` takes that locale's direction, so a French quote inside a Hebrew section reads left to right, and one that sets only a `dir` keeps the outer locale.\n\n```tsx live\n<LocaleProvider locale=\"he\">\n <Content>\n <p>הציטוט האהוב עליי:</p>\n <LocaleProvider locale=\"fr\">\n <blockquote>La simplicité est la sophistication suprême.</blockquote>\n </LocaleProvider>\n </Content>\n</LocaleProvider>\n```\n\n### Date Pickers\n\nThe date and time pickers take the provider's `locale` unless they have their own, for their day and month names and any `Intl` format. Inside a provider, the week starts on the first day of the locale the picker uses: Monday for `\"de\"`, Saturday for `\"ar-EG\"`, Sunday for `\"en-US\"`. A picker's own `firstDayOfWeek` still wins, and a picker with its own `dayNames` keeps Sunday unless it sets `firstDayOfWeek` too, since it shows those names exactly as given.\n\n```tsx live\n<LocaleProvider locale=\"de\">\n <DateInput inline label=\"Liefertermin\" />\n</LocaleProvider>\n```\n\nOutside a provider a picker keeps Sunday, even with a `locale` of its own, so pages that never asked for a different first day don't get one. The first day comes from a copy of [CLDR](https://cldr.unicode.org/)'s week data that the library carries, rather than from each browser's, because browsers disagree about a few regions, China among them, and a calendar rendered on the server has to come out the same in the browser. A `-u-fw-` extension in the tag, such as `\"en-US-u-fw-mon\"`, picks the day outright.\n\n### Numbers and Matching\n\nWith a `locale`, the numbers the library writes itself are written the way that locale writes them, with its digits and separators: `Pagination`'s page numbers, `Steps` markers, a `Badge` count, the `Avatars` surplus, a `RingProgress` in sections, `Rate`'s score, the `Slider` output, `Taginput`'s counter and the count of files `File` shows. Where an accessible name repeats a number on screen, as a page link's does, it's written the same way, so the name still holds the text. A label function you pass, such as `getPageLabel` or `formatOutput`, takes over as before.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <Pagination total={12} current={5} />\n</LocaleProvider>\n```\n\nNumbers of your own follow it through [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter), and through its `useNumberFormat` hook for text outside JSX, such as an `aria-label`. Outside a provider, those write in the runtime's locale, while the library's own numbers stay as they always were.\n\nTyping in `Combobox`, `Autocomplete`, `Taginput` and `Command` filters the list the way the language compares letters: case and accents are ignored where it treats them as the same letter, so \"cafe\" finds \"Café\", while in Swedish \"a\" doesn't find \"å\", a letter of its own there. Vowel marks the language leaves out of comparisons, such as Arabic harakat and Hebrew niqqud, don't count on either side, so a name typed without them finds one written with them, and the other way round.\n\n```tsx live\n<LocaleProvider locale=\"fr\">\n <Combobox\n label=\"Boisson\"\n data={['Café', 'Thé', 'Chocolat chaud']}\n placeholder=\"Tapez « the »\"\n />\n</LocaleProvider>\n```\n\nThe pickers' digits stay as they are: their typed text is a token format of ASCII digits, and a calendar in another set of digits would sit under it. Numbers inside the library's English labels that aren't on screen, such as a carousel slide's \"1 of 3\", stay as they are too, since they get translated together with their words.\n\n### Portaled Content\n\nWhat the library portals renders outside the provider's wrapper, at the end of `document.body` or in the `portalContainer` a [`ConfigProvider`](https://bestax.io/docs/api/helpers/config) sets. Inside a provider, that content keeps the provider's `dir` and `lang`. That covers a `Portal`'s children, a `Popover`, `HoverCard` or `ContextMenu` panel, a `Combobox` menu or picker popover with `appendToBody`, a `Modal` or `Dialog` with `portal`, a `Sidebar`, and toasts and notifications.\n\nA container inside the provider's own wrapper takes the content as it is, and the content follows the provider through the page. Any other, `document.body` included, gets it inside one `<div>` carrying the values, with `display: contents`, so it adds no box and changes no layout. It is still an element, though, so content that must be its container's direct child, such as a `<tr>` portaled into a `<tbody>`, belongs in a container inside the provider. Which of the two is settled when the content mounts: switching the locale while a dialog or a toast is open updates that `<div>` in place, so what's open keeps its typed text, focus and timers.\n\n```tsx live\n<LocaleProvider locale=\"fa\">\n <Popover trigger={<Button>جزئیات</Button>} appendToBody ariaLabel=\"جزئیات\">\n <Popover.Body>\n این پنل بیرون از بخش رندر می‌شود و همچنان راست‌به‌چپ است.\n </Popover.Body>\n </Popover>\n</LocaleProvider>\n```\n\nA date or time picker with a `locale` of its own is read the way a nested provider is: its popover, portaled or not, and an inline calendar take its `lang`, and the direction of its locale unless it has a `dir` of its own. So a French picker in an Arabic section shows a French calendar laid out left to right. The `Control` the picker renders takes that direction too, so its icon stays at the start of the field and the launcher at the end. In a `Control` of your own, give the `Control` the picker's `dir`.\n\nFor content you portal yourself without `Portal`, put the values from `useLocale()` on it.\n\n### Server Rendering\n\nThe wrapper's `dir` and `lang` come from its props, so the server and the browser write the same ones, and portaled content only renders after hydration.\n\nSet a `locale` when an `inline` picker renders on the server. With none, `Intl` names its days in the runtime's own language, which is the server's on the server and the reader's in the browser, and the two renders don't match when those differ. With a locale they match, as long as the server's `Intl` data and the browser's spell the names alike. They don't always: Node and Safari write the short name for Saturday differently in Arabic. Where that matters, pass the picker its own `dayNames`, and with them the `firstDayOfWeek` they start on. The calendar shows `dayNames` exactly as given, from the first column, so a picker that brings its own keeps the week on Sunday, as it did before the provider, unless `firstDayOfWeek` says otherwise.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <DateInput\n inline\n label=\"تاريخ التسليم\"\n firstDayOfWeek={6}\n dayNames={[\n 'السبت',\n 'الأحد',\n 'الاثنين',\n 'الثلاثاء',\n 'الأربعاء',\n 'الخميس',\n 'الجمعة',\n ]}\n />\n</LocaleProvider>\n```\n\nFor an app that's in one language throughout, also set `lang` and `dir` on `<html>` in your page shell. That reaches what sits outside any provider, and the provider still carries them onto portaled content.\n\nIn the Next.js App Router, render it straight from the root layout, which stays a Server Component. `LocaleProvider` holds React context, so it's a client component, and bestax-bulma marks it one, so the layout can render it without a `'use client'` wrapper of its own. The `locale` and `children` here are values a Server Component can pass. It also takes the wrapper `<div>`'s attributes, and an event handler among them can't cross from a Server Component, so set one from a client component:\n\n```tsx title=\"app/layout.tsx\"\nimport type { ReactNode } from 'react';\nimport { LocaleProvider } from '@allxsmith/bestax-bulma';\n\nexport default function RootLayout({ children }: { children: ReactNode }) {\n return (\n <html lang=\"ar-EG\" dir=\"rtl\">\n <body>\n <LocaleProvider locale=\"ar-EG\">{children}</LocaleProvider>\n </body>\n </html>\n );\n}\n```\n\n### A Locale Intl Doesn't Accept\n\nA tag `Intl` refuses, such as `\"en_US\"` with an underscore, is ignored: the provider writes no `lang`, keeps an outer provider's locale, and warns once in development. A `NumberFormatter`'s or `useNumberFormat`'s own `locale` is checked the same way: a tag `Intl` refuses is ignored with a warning, and the number is written in the provider's locale. A picker's own `locale` isn't checked this way, and `Intl` throws on such a tag when the picker formats with it, as it always has.\n\n---\n\n## Accessibility\n\n- `lang` tells a screen reader which voice and pronunciation to use, and a translation tool what it's reading. The provider puts it on its wrapper and on portaled content, so a dialog or a notification announced from the end of the page comes in the same language.\n- `dir` decides the reading order of the content inside, and which side text starts on. Set the one the content is written in: a left-to-right quote in a right-to-left page needs its own provider or `dir`.\n- The provider changes no focus order and adds no role. The element it wraps portaled content in has `display: contents`, so it adds no box and no landmark.\n\n---\n\n## Related Components\n\n- [`ConfigProvider`](https://bestax.io/docs/api/helpers/config): The class prefix, icon library and portal container. A nested one replaces the outer one's settings, which is why the locale lives here instead.\n- [`Portal`](https://bestax.io/docs/api/helpers/portal): Inside a `LocaleProvider`, its children keep the provider's `dir` and `lang`.\n- [`DateInput`](https://bestax.io/docs/api/form/datetime/dateinput): Takes the provider's locale and first day of the week.\n- [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter): Writes a number in the provider's locale, and `useNumberFormat` does the same for text outside JSX.\n- [`Theme`](https://bestax.io/docs/api/helpers/theme): Its scoped wrapper is the same idea for CSS variables.\n\n---\n\n## Additional Resources\n\n- [Storybook: LocaleProvider Stories](https://bestax.io/storybook/?path=/story/helpers-localeprovider--default)\n- [W3C: Structural markup and right-to-left text in HTML](https://www.w3.org/International/questions/qa-html-dir)\n- [MDN: the `lang` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)\n- [MDN: `Intl.Locale.prototype.getWeekInfo()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo)\n\n---\n\n## Props\n\n| Prop | Type | Default | Description |\n| ---------- | ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `locale` | `string` | — | A BCP 47 language tag, such as `\"de\"` or `\"ar-EG\"`: the wrapper's `lang`, the pickers' default `locale`, and, with no `dir`, the direction of its script. Unset, a nested provider keeps the outer one's. A tag `Intl` refuses is ignored. |\n| `dir` | `'ltr'` \\| `'rtl'` | — | The direction, whatever the locale. Unset, it follows this provider's `locale`, else an outer provider's direction, else the page's. |\n| `children` | `ReactNode` | — | The part of the app it covers. |\n\nThe helper props, the other attributes of a `<div>` and the `ref` land on the wrapper."
116
+ "doc": "# LocaleProvider\n\n## Overview\n\n`LocaleProvider` sets the language and writing direction for the part of the app inside it, on a wrapper `<div>` and on what the library portals out of it.\n\nIt takes a `locale` and a `dir`, and each works alone. A `locale` such as `\"ar-EG\"` becomes the wrapper's `lang`, and its script decides the direction, so `dir=\"rtl\"` comes with it. A `dir` sets the direction whatever the locale, for a right-to-left layout with no particular language, or a locale whose script reads one way on a page laid out the other. A nested provider keeps whatever it leaves unset.\n\nThe direction lives on the wrapper as a `dir` attribute, so CSS follows it, and so do the components that read the direction they're laid out in, as they follow `<html dir>`. A page whose `<html>` already has `dir=\"rtl\"` needs no provider for that. What the provider adds is the rest:\n\n- a section of the page in another language or direction than the page around it;\n- the content the library portals to the end of `document.body`, such as a `Popover` panel, a `Modal` with `portal`, a `Toast` or a `Portal`'s children, which leaves the section it came from and gets the provider's `dir` and `lang` back;\n- the date and time pickers inside, which take its `locale` and start the week on that locale's first day;\n- the numbers the library writes, such as page numbers, badge counts and a slider's output, and the filtering in `Combobox`, `Autocomplete`, `Taginput` and `Command`, which follow the `locale` too.\n\nWithout a provider none of this changes: a picker uses the runtime's own locale and starts the week on Sunday, numbers are written as they always have been, and filtering ignores case only.\n\n---\n\n## Import\n\n```tsx\nimport { LocaleProvider, useLocale } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Hooks\n\n### useLocale\n\nReturns the `locale` and `dir` the nearest provider sets, both `undefined` outside one. The locale is in its canonical form, so `\"en-us\"` comes back as `\"en-US\"`. Use it for your own `Intl` calls, so they match the components around them, or for `dir` and `lang` on content you portal yourself.\n\n```tsx\nfunction Price({ amount }: { amount: number }) {\n const { locale } = useLocale();\n return (\n <span>\n {new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: 'EUR',\n }).format(amount)}\n </span>\n );\n}\n```\n\nFor numbers, [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter) and its `useNumberFormat` hook do this for you, and build one formatter for each set of options rather than one on every render.\n\nIt isn't the direction of the page: with no provider, or one that sets no direction, `<html dir>` still decides. To act on the direction an element is laid out in, read `getComputedStyle(element).direction`.\n\n---\n\n## Usage\n\n### A Right-to-Left Section\n\nA `locale` alone sets the direction of its script. Arabic reads right to left, so the wrapper gets `dir=\"rtl\"` and `lang=\"ar-EG\"`, and the steps lay out from the right.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <Steps\n value={1}\n items={[{ label: 'السلة' }, { label: 'الشحن' }, { label: 'الدفع' }]}\n />\n</LocaleProvider>\n```\n\nThe wrapper is a plain `<div>` with no class of its own. It takes the helper props and any other attributes, and forwards its `ref`.\n\n### Direction Only\n\n`dir` sets the direction and leaves the language alone. It also wins over a `locale`'s own direction.\n\n```tsx live\n<LocaleProvider dir=\"rtl\">\n <Buttons>\n <Button color=\"primary\">First</Button>\n <Button>Second</Button>\n </Buttons>\n</LocaleProvider>\n```\n\n### Nested Providers\n\nAn inner provider keeps whatever it leaves unset. One that sets only a `locale` takes that locale's direction, so a French quote inside a Hebrew section reads left to right, and one that sets only a `dir` keeps the outer locale.\n\n```tsx live\n<LocaleProvider locale=\"he\">\n <Content>\n <p>הציטוט האהוב עליי:</p>\n <LocaleProvider locale=\"fr\">\n <blockquote>La simplicité est la sophistication suprême.</blockquote>\n </LocaleProvider>\n </Content>\n</LocaleProvider>\n```\n\n### Date Pickers\n\nThe date and time pickers take the provider's `locale` unless they have their own, for their day and month names and any `Intl` format. Inside a provider, the week starts on the first day of the locale the picker uses: Monday for `\"de\"`, Saturday for `\"ar-EG\"`, Sunday for `\"en-US\"`. A picker's own `firstDayOfWeek` still wins, and a picker with its own `dayNames` keeps Sunday unless it sets `firstDayOfWeek` too, since it shows those names exactly as given.\n\n```tsx live\n<LocaleProvider locale=\"de\">\n <DateInput inline label=\"Liefertermin\" />\n</LocaleProvider>\n```\n\nOutside a provider a picker keeps Sunday, even with a `locale` of its own, so pages that never asked for a different first day don't get one. The first day comes from a copy of [CLDR](https://cldr.unicode.org/)'s week data that the library carries, rather than from each browser's, because browsers disagree about a few regions, China among them, and a calendar rendered on the server has to come out the same in the browser. A `-u-fw-` extension in the tag, such as `\"en-US-u-fw-mon\"`, picks the day outright.\n\n### Numbers and Matching\n\nWith a `locale`, the numbers the library writes itself are written the way that locale writes them, with its digits and separators: `Pagination`'s page numbers, `Steps` markers, a `Badge` count, the `Avatars` surplus, a `RingProgress` in sections, `Rate`'s score, the `Slider` output, `Taginput`'s counter and the count of files `File` shows. Where an accessible name repeats a number on screen, as a page link's does, it's written the same way, so the name still holds the text. A label function you pass, such as `getPageLabel` or `formatOutput`, takes over as before.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <Pagination total={12} current={5} />\n</LocaleProvider>\n```\n\nNumbers of your own follow it through [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter), and through its `useNumberFormat` hook for text outside JSX, such as an `aria-label`. Outside a provider, those write in the runtime's locale, while the library's own numbers stay as they always were.\n\nTyping in `Combobox`, `Autocomplete`, `Taginput` and `Command` filters the list the way the language compares letters: case and accents are ignored where it treats them as the same letter, so \"cafe\" finds \"Café\", while in Swedish \"a\" doesn't find \"å\", a letter of its own there. Vowel marks the language leaves out of comparisons, such as Arabic harakat and Hebrew niqqud, don't count on either side, so a name typed without them finds one written with them, and the other way round.\n\n```tsx live\n<LocaleProvider locale=\"fr\">\n <Combobox\n label=\"Boisson\"\n data={['Café', 'Thé', 'Chocolat chaud']}\n placeholder=\"Tapez « the »\"\n />\n</LocaleProvider>\n```\n\nThe pickers' digits stay as they are: their typed text is a token format of ASCII digits, and a calendar in another set of digits would sit under it. Numbers inside the library's English labels that aren't on screen, such as a carousel slide's \"1 of 3\", stay as they are too, since they get translated together with their words.\n\n### Portaled Content\n\nWhat the library portals renders at the end of `document.body`, in the `portalContainer` a [`ConfigProvider`](https://bestax.io/docs/api/helpers/config) sets, or, for a popup opened inside a `Modal`, `Dialog` or `Command.Dialog`, in that dialog, any of which can be outside the provider's wrapper. Inside a provider, that content keeps the provider's `dir` and `lang`. That covers a `Portal`'s children, a `Popover`, `HoverCard` or `ContextMenu` panel, a `Combobox` menu or picker popover with `appendToBody`, a `Modal` or `Dialog` with `portal`, a `Sidebar`, and toasts and notifications.\n\nA container inside the provider's own wrapper takes the content as it is, and the content follows the provider through the page. Any other, `document.body` included, gets it inside one `<div>` carrying the values, with `display: contents`, so it adds no box and changes no layout. It is still an element, though, so content that must be its container's direct child, such as a `<tr>` portaled into a `<tbody>`, belongs in a container inside the provider. Which of the two is settled when the content mounts: switching the locale while a dialog or a toast is open updates that `<div>` in place, so what's open keeps its typed text, focus and timers.\n\n```tsx live\n<LocaleProvider locale=\"fa\">\n <Popover trigger={<Button>جزئیات</Button>} appendToBody ariaLabel=\"جزئیات\">\n <Popover.Body>\n این پنل بیرون از بخش رندر می‌شود و همچنان راست‌به‌چپ است.\n </Popover.Body>\n </Popover>\n</LocaleProvider>\n```\n\nA date or time picker with a `locale` of its own is read the way a nested provider is: its popover, portaled or not, and an inline calendar take its `lang`, and the direction of its locale unless it has a `dir` of its own. So a French picker in an Arabic section shows a French calendar laid out left to right. The `Control` the picker renders takes that direction too, so its icon stays at the start of the field and the launcher at the end. In a `Control` of your own, give the `Control` the picker's `dir`.\n\nFor content you portal yourself without `Portal`, put the values from `useLocale()` on it.\n\n### Server Rendering\n\nThe wrapper's `dir` and `lang` come from its props, so the server and the browser write the same ones, and portaled content only renders after hydration.\n\nSet a `locale` when an `inline` picker renders on the server. With none, `Intl` names its days in the runtime's own language, which is the server's on the server and the reader's in the browser, and the two renders don't match when those differ. With a locale they match, as long as the server's `Intl` data and the browser's spell the names alike. They don't always: Node and Safari write the short name for Saturday differently in Arabic. Where that matters, pass the picker its own `dayNames`, and with them the `firstDayOfWeek` they start on. The calendar shows `dayNames` exactly as given, from the first column, so a picker that brings its own keeps the week on Sunday, as it did before the provider, unless `firstDayOfWeek` says otherwise.\n\n```tsx live\n<LocaleProvider locale=\"ar-EG\">\n <DateInput\n inline\n label=\"تاريخ التسليم\"\n firstDayOfWeek={6}\n dayNames={[\n 'السبت',\n 'الأحد',\n 'الاثنين',\n 'الثلاثاء',\n 'الأربعاء',\n 'الخميس',\n 'الجمعة',\n ]}\n />\n</LocaleProvider>\n```\n\nFor an app that's in one language throughout, also set `lang` and `dir` on `<html>` in your page shell. That reaches what sits outside any provider, and the provider still carries them onto portaled content.\n\nIn the Next.js App Router, render it straight from the root layout, which stays a Server Component. `LocaleProvider` holds React context, so it's a client component, and bestax-bulma marks it one, so the layout can render it without a `'use client'` wrapper of its own. The `locale` and `children` here are values a Server Component can pass. It also takes the wrapper `<div>`'s attributes, and an event handler among them can't cross from a Server Component, so set one from a client component:\n\n```tsx title=\"app/layout.tsx\"\nimport type { ReactNode } from 'react';\nimport { LocaleProvider } from '@allxsmith/bestax-bulma';\n\nexport default function RootLayout({ children }: { children: ReactNode }) {\n return (\n <html lang=\"ar-EG\" dir=\"rtl\">\n <body>\n <LocaleProvider locale=\"ar-EG\">{children}</LocaleProvider>\n </body>\n </html>\n );\n}\n```\n\n### A Locale Intl Doesn't Accept\n\nA tag `Intl` refuses, such as `\"en_US\"` with an underscore, is ignored: the provider writes no `lang`, keeps an outer provider's locale, and warns once in development. A `NumberFormatter`'s or `useNumberFormat`'s own `locale` is checked the same way: a tag `Intl` refuses is ignored with a warning, and the number is written in the provider's locale. A picker's own `locale` isn't checked this way, and `Intl` throws on such a tag when the picker formats with it, as it always has.\n\n---\n\n## Accessibility\n\n- `lang` tells a screen reader which voice and pronunciation to use, and a translation tool what it's reading. The provider puts it on its wrapper and on portaled content, so a dialog or a notification announced from the end of the page comes in the same language.\n- `dir` decides the reading order of the content inside, and which side text starts on. Set the one the content is written in: a left-to-right quote in a right-to-left page needs its own provider or `dir`.\n- The provider changes no focus order and adds no role. The element it wraps portaled content in has `display: contents`, so it adds no box and no landmark.\n\n---\n\n## Related Components\n\n- [`ConfigProvider`](https://bestax.io/docs/api/helpers/config): The class prefix, icon library and portal container. A nested one replaces the outer one's settings, which is why the locale lives here instead.\n- [`Portal`](https://bestax.io/docs/api/helpers/portal): Inside a `LocaleProvider`, its children keep the provider's `dir` and `lang`.\n- [`DateInput`](https://bestax.io/docs/api/form/datetime/dateinput): Takes the provider's locale and first day of the week.\n- [`NumberFormatter`](https://bestax.io/docs/api/elements/numberformatter): Writes a number in the provider's locale, and `useNumberFormat` does the same for text outside JSX.\n- [`Theme`](https://bestax.io/docs/api/helpers/theme): Its scoped wrapper is the same idea for CSS variables.\n\n---\n\n## Additional Resources\n\n- [Storybook: LocaleProvider Stories](https://bestax.io/storybook/?path=/story/helpers-localeprovider--default)\n- [W3C: Structural markup and right-to-left text in HTML](https://www.w3.org/International/questions/qa-html-dir)\n- [MDN: the `lang` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang)\n- [MDN: `Intl.Locale.prototype.getWeekInfo()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo)\n\n---\n\n## Props\n\n| Prop | Type | Default | Description |\n| ---------- | ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `locale` | `string` | — | A BCP 47 language tag, such as `\"de\"` or `\"ar-EG\"`: the wrapper's `lang`, the pickers' default `locale`, and, with no `dir`, the direction of its script. Unset, a nested provider keeps the outer one's. A tag `Intl` refuses is ignored. |\n| `dir` | `'ltr'` \\| `'rtl'` | — | The direction, whatever the locale. Unset, it follows this provider's `locale`, else an outer provider's direction, else the page's. |\n| `children` | `ReactNode` | — | The part of the app it covers. |\n\nThe helper props, the other attributes of a `<div>` and the `ref` land on the wrapper."
117
117
  }
@@ -38,7 +38,7 @@
38
38
  "code": "<Menu>\n <Menu.Label>General</Menu.Label>\n <Menu.List>\n <Menu.Item active href=\"#dashboard\">\n Dashboard\n </Menu.Item>\n <Menu.Item href=\"#customers\">Customers</Menu.Item>\n </Menu.List>\n <Menu.Section label=\"Transactions\">\n <Menu.List>\n <Menu.Item href=\"#payments\">Payments</Menu.Item>\n <Menu.Item href=\"#transfers\">Transfers</Menu.Item>\n </Menu.List>\n </Menu.Section>\n</Menu>"
39
39
  }
40
40
  ],
41
- "accessibility": "- The root `Menu` renders as `<aside class=\"menu\">`. A menu that's navigation is better inside a\n `<nav>` with an `aria-label`, which gives assistive tech a navigation landmark to jump to.\n- Every `Menu.List` has `role=\"list\"`, nested ones included. That's the role a `<ul>` already has,\n set again for Safari, whose WebKit stops exposing a `<ul>` as a list once its markers are hidden,\n as Bulma's reset hides them, unless the role is written out.\n- `Menu.Item active` sets `aria-current=\"page\"` on the element `as` renders, the link by\n default, so the current item isn't only a color. An `aria-current` you pass wins: a menu of\n sections on one page can say `\"location\"`, and `aria-current={false}` turns it off.\n- Use semantic nesting (`Menu.List` inside `Menu.Item`) for submenus.\n- Give each item an `href`, a router link through `as`, or `as=\"button\"`, so the keyboard can\n reach it.\n- `Menu.Item as=\"button\"` defaults to `type=\"button\"`, so a menu item inside a form doesn't\n submit it. Pass `type=\"submit\"` or `type=\"reset\"` and yours is used instead; any other value\n renders `type=\"button\"`.\n- In forced colors (Windows' contrast themes), the browser replaces Bulma's fill and text color\n for the active item along with every other color, so bestax's stylesheet marks an\n `is-active` or `is-selected` item with a bar in the system's highlight color along its start\n edge there instead. The item's text, and an icon or a tag inside it, keep the colors your\n contrast theme gives them. That rule ships in `bestax.css` and `extras.css`, not in Bulma's\n own stylesheet.\n\nFor `Menu.Section`, a section that opens and closes:\n\n- It follows APG's\n [Disclosure Navigation Menu example](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/).\n The heading is a `<button type=\"button\">` with `aria-expanded`, and `aria-controls` naming the\n wrapper around its list. Enter and Space open and close it, as they do any button, and it\n doesn't submit a form it sits in.\n- The links under it are ordinary links. Each is its own tab stop, and there's no roving focus or\n arrow-key handling, which is a tree's model rather than navigation's. They keep their `href`,\n so a link still opens in a new tab, and `active` still sets `aria-current=\"page\"`.\n- A closed section's list is `hidden` and `inert`, so it isn't drawn, Tab skips its links, and\n assistive tech and find-in-page don't reach them. `inert` keeps the links out of reach even if a\n stylesheet sets `display` on the list and shows it. The list stays in the page, so\n `aria-controls` always names an element that exists, and a section that starts closed is\n already closed in the server's HTML, before any script runs.\n- Open the section that holds the current page, with `defaultOpen` or `open`, so the link marked\n `aria-current` can be seen and reached.\n- When the app closes a section while focus is on one of its links, as a sidebar that keeps only\n the current route's section open does after a link is followed, focus moves to that section's\n heading. Without that, the browser drops focus to the page, and the next Tab starts from the\n top.\n- `label` is the button's content and its accessible name. Keep links and other buttons out of\n it.\n- In forced colors the chevron stays visible, since it's drawn as a border, and the focus ring\n takes the system's text color. In a right-to-left page the chevron sits at the left end of the\n heading and points left while closed. It turns with a short transition, which stops when the\n reader asks for reduced motion.\n\n:::note\nFor router integrations, pass the correct `as` and `to`/`href` props to `Menu.Item`.\n:::",
41
+ "accessibility": "- The root `Menu` renders as `<aside class=\"menu\">`. A menu that's navigation is better inside a\n `<nav>` with an `aria-label`, which gives assistive tech a navigation landmark to jump to.\n- Every `Menu.List` has `role=\"list\"`, nested ones included. That's the role a `<ul>` already has,\n set again for Safari, whose WebKit stops exposing a `<ul>` as a list once its markers are hidden,\n as Bulma's reset hides them, unless the role is written out.\n- `Menu.Item active` sets `aria-current=\"page\"` on the element `as` renders, the link by\n default, so the current item isn't only a color. An `aria-current` you pass wins: a menu of\n sections on one page can say `\"location\"`, and `aria-current={false}` turns it off.\n- Use semantic nesting (`Menu.List` inside `Menu.Item`) for submenus.\n- Give each item an `href`, a router link through `as`, or `as=\"button\"`, so the keyboard can\n reach it.\n- `Menu.Item as=\"button\"` defaults to `type=\"button\"`, so a menu item inside a form doesn't\n submit it. Pass `type=\"submit\"` or `type=\"reset\"` and yours is used instead; any other value\n renders `type=\"button\"`.\n- In forced colors (Windows' contrast themes), the browser replaces Bulma's fill and text color\n for the active item along with every other color, so bestax's stylesheet marks an\n `is-active` or `is-selected` item with a bar in the system's highlight color along its start\n edge there instead. The item's text, and an icon or a tag inside it, keep the colors your\n contrast theme gives them. That rule ships in `bestax.css` and `extras.css`, not in Bulma's\n own stylesheet.\n- On a right-to-left page, each item's text starts at the right, in line with a `Menu.Label` and\n a section heading. Bulma's own stylesheet aligns items to the left, so this needs `bestax.css`\n or `extras.css` too.\n\nFor `Menu.Section`, a section that opens and closes:\n\n- It follows APG's\n [Disclosure Navigation Menu example](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/).\n The heading is a `<button type=\"button\">` with `aria-expanded`, and `aria-controls` naming the\n wrapper around its list. Enter and Space open and close it, as they do any button, and it\n doesn't submit a form it sits in.\n- The links under it are ordinary links. Each is its own tab stop, and there's no roving focus or\n arrow-key handling, which is a tree's model rather than navigation's. They keep their `href`,\n so a link still opens in a new tab, and `active` still sets `aria-current=\"page\"`.\n- A closed section's list is `hidden` and `inert`, so it isn't drawn, Tab skips its links, and\n assistive tech and find-in-page don't reach them. `inert` keeps the links out of reach even if a\n stylesheet sets `display` on the list and shows it. The list stays in the page, so\n `aria-controls` always names an element that exists, and a section that starts closed is\n already closed in the server's HTML, before any script runs.\n- Open the section that holds the current page, with `defaultOpen` or `open`, so the link marked\n `aria-current` can be seen and reached.\n- When the app closes a section while focus is on one of its links, as a sidebar that keeps only\n the current route's section open does after a link is followed, focus moves to that section's\n heading. Without that, the browser drops focus to the page, and the next Tab starts from the\n top.\n- `label` is the button's content and its accessible name. Keep links and other buttons out of\n it.\n- In forced colors the chevron stays visible, since it's drawn as a border, and the focus ring\n takes the system's text color. In a right-to-left page the chevron sits at the left end of the\n heading and points left while closed. It turns with a short transition, which stops when the\n reader asks for reduced motion.\n\n:::note\nFor router integrations, pass the correct `as` and `to`/`href` props to `Menu.Item`.\n:::",
42
42
  "related": ["Collapse", "Panel", "Sidebar", "TreeView", "useBulmaClasses"],
43
43
  "storybook": "https://bestax.io/storybook/?path=/story/components-menu--basic",
44
44
  "summary": "The `Menu` component provides Bulma's vertical navigation menu: a simple, accessible sidebar or section menu for your Bulma React UI.",
@@ -46,7 +46,7 @@
46
46
  "code": "function example() {\n const [open, setOpen] = useState(false);\n const closeModal = () => setOpen(false);\n\n return (\n <>\n <Button color=\"success\" onClick={() => setOpen(true)}>\n Show Modal\n </Button>\n <Modal\n isActive={open}\n onClose={closeModal}\n aria-labelledby=\"custom-content-title\"\n >\n <Modal.Background onClick={closeModal} />\n <Modal.Content>\n <Box>\n <Title size=\"4\" id=\"custom-content-title\">\n Custom Content\n </Title>\n <Paragraph>Using compound components for full control!</Paragraph>\n </Box>\n </Modal.Content>\n <Modal.Close variant=\"floating\" />\n </Modal>\n </>\n );\n}"
47
47
  }
48
48
  ],
49
- "accessibility": "- The modal root gets `role=\"dialog\"` and `aria-modal=\"true\"` while active — pass an explicit `role` to override (e.g. `Dialog` passes `role=\"presentation\"` on the `Modal` it wraps so its own `role=\"alertdialog\"` is the only one announced).\n- **Give the dialog an accessible name.** With the legacy API, `modalCardTitle` is wired up as `aria-labelledby` for you. With the compound API, pass your own `aria-label` or `aria-labelledby` on `<Modal>` — a `role=\"dialog\"` with no name is announced as just \"dialog\". An explicit `aria-labelledby` always wins over the generated one.\n- Escape closes the modal (calls `onClose`) by default; opt out with `closeOnEscape={false}`. One Escape closes the topmost overlay only, whichever open Modal, [Dialog](https://bestax.io/docs/api/components/dialog), [Sidebar](https://bestax.io/docs/api/components/sidebar) or full-page [Loading](https://bestax.io/docs/api/components/loading) opened last, and the next Escape closes the one under it. A modal on top with `closeOnEscape={false}` still keeps Escape from the layers under it. A [Toast](https://bestax.io/docs/api/components/toast) waits until no overlay is open.\n- The modal calls `preventDefault()` on the Escape it closes on, so a listener of your own that runs after it can tell the key was used.\n- A popup inside the modal takes its own Escape first. With an Autocomplete list, a Dropdown menu, a Popover or a date picker's calendar open, the first Escape closes that and the next one closes the modal.\n- A popup of your own inside a modal should call `preventDefault()` on the Escape it closes on. The modal leaves an Escape that was prevented alone, whether your popup hears it in a React `onKeyDown` or in a listener on `document`. Call it only when the popup is open and the key was pressed in it, so an Escape with nothing to close still closes the modal, and so does one pressed in a modal opened over your popup.\n- The page doesn't scroll under the modal while it's active; opt out with `lockScroll={false}`. The lock is ref-counted and shared with `Dialog`, `Sidebar` and `Loading`, so whichever overlay closes first doesn't unlock the page underneath one that is still open.\n- Focus moves to the first tab stop (or the modal itself) on open, and is restored to the previously focused element on close — unless another modal is still open on top, in which case that one keeps focus.\n- Tab and Shift+Tab cycle within the modal while it is the topmost modal overlay, so the keyboard order agrees with what `aria-modal=\"true\"` tells assistive technology. They wrap at the same tab stops [`useFocusTrap`](https://bestax.io/docs/api/helpers/usefocustrap) does, so disabled, hidden and `tabIndex={-1}` elements are skipped, and so is inert content, such as a closed [Sidebar](https://bestax.io/docs/api/components/sidebar) inside the modal, and the content of a closed `<details>`. A radio group counts once. A Tab from outside the modal brings focus back in, and so does a Tab the browser takes out anyway, as Safari does past links when its settings say to. With another Modal or Dialog, a [Sidebar](https://bestax.io/docs/api/components/sidebar) with an overlay or a full-page Loading opened over it, the modal leaves Tab alone until that one closes. A Sidebar without an overlay isn't modal: opened over the modal, it takes Escape first, but the modal keeps holding Tab and keeps focus, so the sidebar doesn't take focus when it opens, and a Tab from its controls comes back into the modal. A focus trap of your own inside it, such as a portaled panel's, keeps a Tab it calls `preventDefault()` on, and the modal leaves focus where your trap put it. Note that `aria-modal` does not by itself make background content inert, and no `inert` attribute is applied, so background content remains reachable by pointer and by browser UI such as find-in-page.\n- In the props form, the background closes the modal on click, and a close button is provided for both modal-card and modal-content variants (both call `onClose`, so pass it).\n- In the compound form, `Modal.Close` calls the modal's `onClose` unless you give it an `onClick` of your own, and `Modal.Background` closes the modal only when you pass it `onClick={onClose}`.\n\n:::note\nAlways provide an `onClose` handler for accessibility and to allow users to dismiss the modal.\n:::",
49
+ "accessibility": "- The modal root gets `role=\"dialog\"` and `aria-modal=\"true\"` while active — pass an explicit `role` to override (e.g. `Dialog` passes `role=\"presentation\"` on the `Modal` it wraps so its own `role=\"alertdialog\"` is the only one announced).\n- **Give the dialog an accessible name.** With the legacy API, `modalCardTitle` is wired up as `aria-labelledby` for you. With the compound API, pass your own `aria-label` or `aria-labelledby` on `<Modal>` — a `role=\"dialog\"` with no name is announced as just \"dialog\". An explicit `aria-labelledby` always wins over the generated one.\n- Escape closes the modal (calls `onClose`) by default; opt out with `closeOnEscape={false}`. One Escape closes the topmost overlay only, whichever open Modal, [Dialog](https://bestax.io/docs/api/components/dialog), [Sidebar](https://bestax.io/docs/api/components/sidebar) or full-page [Loading](https://bestax.io/docs/api/components/loading) opened last, and the next Escape closes the one under it. A modal on top with `closeOnEscape={false}` still keeps Escape from the layers under it. A [Toast](https://bestax.io/docs/api/components/toast) waits until no overlay is open.\n- The modal calls `preventDefault()` on the Escape it closes on, so a listener of your own that runs after it can tell the key was used.\n- A popup inside the modal takes its own Escape first. With an Autocomplete list, a Dropdown menu, a Popover or a date picker's calendar open, the first Escape closes that and the next one closes the modal.\n- A popup of your own inside a modal should call `preventDefault()` on the Escape it closes on. The modal leaves an Escape that was prevented alone, whether your popup hears it in a React `onKeyDown` or in a listener on `document`. Call it only when the popup is open and the key was pressed in it, so an Escape with nothing to close still closes the modal, and so does one pressed in a modal opened over your popup.\n- The page doesn't scroll under the modal while it's active; opt out with `lockScroll={false}`. The lock is ref-counted and shared with `Dialog`, `Sidebar` and `Loading`, so whichever overlay closes first doesn't unlock the page underneath one that is still open.\n- Focus moves to the first tab stop (or the modal itself) on open, and is restored to the previously focused element on close — unless another modal is still open on top, in which case that one keeps focus.\n- A popup opened inside the modal, such as a `Popover`, `HoverCard` or `ContextMenu` panel, or a `Combobox` menu or picker popover with `appendToBody`, is portaled into the modal's root rather than to the end of `<body>`, so a screen reader that honors `aria-modal` reaches it, and it's inside the Tab cycle below. A `portalContainer` a [`ConfigProvider`](https://bestax.io/docs/api/helpers/config#where-portaled-content-goes) sets still takes it. A [Lightbox](https://bestax.io/docs/api/components/lightbox) is a Modal, so the same goes for it.\n- Tab and Shift+Tab cycle within the modal while it is the topmost modal overlay, so the keyboard order agrees with what `aria-modal=\"true\"` tells assistive technology. They wrap at the same tab stops [`useFocusTrap`](https://bestax.io/docs/api/helpers/usefocustrap) does, so disabled, hidden and `tabIndex={-1}` elements are skipped, and so is inert content, such as a closed [Sidebar](https://bestax.io/docs/api/components/sidebar) inside the modal, and the content of a closed `<details>`. A radio group counts once. A Tab from outside the modal brings focus back in, and so does a Tab the browser takes out anyway, as Safari does past links when its settings say to. With another Modal or Dialog, a [Sidebar](https://bestax.io/docs/api/components/sidebar) with an overlay or a full-page Loading opened over it, the modal leaves Tab alone until that one closes. A Sidebar without an overlay isn't modal: opened over the modal, it takes Escape first, but the modal keeps holding Tab and keeps focus, so the sidebar doesn't take focus when it opens, and a Tab from its controls comes back into the modal. A focus trap of your own inside it, such as a portaled panel's, keeps a Tab it calls `preventDefault()` on, and the modal leaves focus where your trap put it. Note that `aria-modal` does not by itself make background content inert, and no `inert` attribute is applied, so background content remains reachable by pointer and by browser UI such as find-in-page.\n- In the props form, the background closes the modal on click, and a close button is provided for both modal-card and modal-content variants (both call `onClose`, so pass it).\n- In the compound form, `Modal.Close` calls the modal's `onClose` unless you give it an `onClick` of your own, and `Modal.Background` closes the modal only when you pass it `onClick={onClose}`.\n\n:::note\nAlways provide an `onClose` handler for accessibility and to allow users to dismiss the modal.\n:::",
50
50
  "related": [
51
51
  "Button",
52
52
  "Dialog",
@@ -118,7 +118,7 @@
118
118
  "name": "appendToBody",
119
119
  "type": "boolean",
120
120
  "default": "false",
121
- "description": "Render the panel through `Portal`, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default, fixed to the viewport, so an ancestor's `overflow`, `transform` or stacking context can't clip it. The panel then shows from the commit after hydration, never in server markup. Theming variables set on an ancestor of the trigger reach it only when its container is inside that ancestor.",
121
+ "description": "Render the panel through `Portal`, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`, fixed to the viewport, so an ancestor's `overflow`, `transform` or stacking context can't clip it. The panel then shows from the commit after hydration, never in server markup. Theming variables set on an ancestor of the trigger reach it only when its container is inside that ancestor.",
122
122
  "required": false,
123
123
  "inherited": false,
124
124
  "deprecated": false
@@ -22,7 +22,7 @@
22
22
  "code": "<Portal disabled>\n <Notification>Rendered in place, as if there were no portal.</Notification>\n</Portal>"
23
23
  }
24
24
  ],
25
- "accessibility": "- The DOM decides reading and Tab order, not the React tree. Content portaled to the end of `document.body` comes last in both, so a keyboard user can't reach it from the button that opened it by pressing Tab. Move focus into interactive floating content when it opens, and back to what opened it when it closes.\n- Give floating content a role and an accessible name (`role=\"dialog\"` with `aria-label` or `aria-labelledby`), and give its trigger `aria-expanded` and `aria-controls`. Ids work across the portal, since they are shared by the whole document.\n- Content that only appears after hydration is missing from the server HTML. Keep anything a reader or a crawler needs on first load out of a portal.",
25
+ "accessibility": "- The DOM decides reading and Tab order, not the React tree. Content portaled to the end of `document.body` comes last in both, so a keyboard user can't reach it from the button that opened it by pressing Tab. Move focus into interactive floating content when it opens, and back to what opened it when it closes.\n- Give floating content a role and an accessible name (`role=\"dialog\"` with `aria-label` or `aria-labelledby`), and give its trigger `aria-expanded` and `aria-controls`. Ids work across the portal, since they are shared by the whole document.\n- A modal dialog's `aria-modal` hides the rest of the page from screen readers that honor it, content at the end of `document.body` included. The library's popups, such as a `Popover` with `appendToBody`, go into the `Modal`, `Dialog` or `Command.Dialog` they're opened in. A `Portal` doesn't, so inside one, give it a `container` inside the dialog.\n- Content that only appears after hydration is missing from the server HTML. Keep anything a reader or a crawler needs on first load out of a portal.",
26
26
  "related": ["ConfigProvider", "Modal", "Theme", "Toast"],
27
27
  "storybook": "https://bestax.io/storybook/?path=/story/helpers-portal--default",
28
28
  "summary": "Renders its children into another part of the page, `document.body` unless `container` or a `ConfigProvider` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context. React's server renderer can't render a portal, so `Portal` renders nothing on the server and during hydration, and mounts its children in the commit that follows. The first client render then matches the server markup. In an app without server rendering it portals on the first render. React context, such as `ConfigProvider`'s class prefix, reaches the portaled children, and their React events bubble through the React tree. CSS inheritance follows the DOM instead: a scoped (non-`isRoot`) `Theme` sets its variables on a wrapper element the portaled content has left, so that content sees the page-level theme, and styles scoped to a shadow root don't reach `document.body` at all. To keep them, portal into an element inside the themed area or the shadow root: pass it as `container`, or as the `portalContainer` of a `ConfigProvider` around that area. The second also reaches what the library's components portal, such as Popover's `appendToBody` panel or a `Modal` with `portal`. Inside a `LocaleProvider`, the children keep its `dir` and `lang`. A container inside the provider's own wrapper takes the children as they are, and they follow the provider through the page. Any other, `document.body` included, gets them inside one `<div>` carrying the provider's values, with `display: contents` so it adds no box. It is still an element, so content that must be the container's direct child, a `<tr>` in a `<tbody>`, or that a direct-child selector styles, belongs in a container inside the provider. Which of the two is settled when the children mount, so a change of locale updates them in place rather than remounting them. Tab order and focus follow the DOM too. Content portaled from inside a focus trap is no longer inside the trapped element, so the trap neither reaches it nor holds focus there: render nested overlays inside the trapped element, or give them a trap of their own. To trap portaled content, put `useFocusTrap`'s ref on the element inside the `Portal`.",
@@ -69,5 +69,5 @@
69
69
  }
70
70
  ],
71
71
  "cssVars": [],
72
- "doc": "# Portal\n\n## Overview\n\n`Portal` renders its children into another part of the page, `document.body` unless `container` or a `ConfigProvider` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context.\n\nIt is the building block for your own overlays, such as a floating panel. The library's own overlays, such as `Modal`, `Popover`, `HoverCard` and `Command.Dialog`, already portal through their own props, so reach for `Portal` when you are building something they don't cover.\n\nReact's server renderer can't render a portal, so `Portal` renders nothing on the server and during hydration, and mounts its children in the commit that follows. The first client render matches the server markup, and there is no hydration warning to chase. In an app without server rendering it portals on the first render.\n\n---\n\n## Import\n\n```tsx\nimport { Portal } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n### Into `document.body`\n\nWith no `container`, and no [default container](https://bestax.io/docs/api/helpers/portal#a-default-container) set, the children go to the end of `document.body`, whatever clips or transforms the component that renders them.\n\n```tsx\nfunction Banner() {\n return (\n <Box overflow=\"clipped\">\n <p>This box clips its overflow.</p>\n <Portal>\n <Notification color=\"info\">\n Rendered at the end of document.body, outside the box.\n </Notification>\n </Portal>\n </Box>\n );\n}\n```\n\n### Into a container\n\n`container` takes an element or a `document.querySelector` selector. A selector suits an overlay root in your page shell (`<div id=\"overlays\" />`); an element suits a target your own component renders. Hold that element in state from a ref callback, so the portal renders once the target exists.\n\n```tsx live\nfunction example() {\n const [target, setTarget] = useState<HTMLElement | null>(null);\n\n return (\n <>\n <Box overflow=\"clipped\">\n <p>The notification is declared inside this box.</p>\n {target && (\n <Portal container={target}>\n <Notification color=\"success\">\n It renders into the element after the box instead.\n </Notification>\n </Portal>\n )}\n </Box>\n <div ref={setTarget} />\n </>\n );\n}\n```\n\nAn empty selector counts as no `container`. One that matches nothing falls back to `document.body` rather than throwing.\n\nA selector is looked up again on each render, so the content follows the target when the target changes. Moving to another target remounts the children, which resets their state. That includes a selector that only starts matching after the content has already shown in `document.body`, so render the target first (an overlay root in the page shell is), or pass the element once you have it, as above.\n\n### A default container\n\nA `ConfigProvider` with a `portalContainer` sets where every `Portal` inside it goes when it has no `container` of its own. The library's components that portal follow it too, such as `Popover`'s `appendToBody` panel or a `Modal` with `portal`. It takes the same element or selector as `container`, and follows the same rules, including the remount when the target turns up after the content has rendered. What those components portal is `position: fixed`, so the element has to sit where no ancestor has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`, any of which would place it against that ancestor instead of the viewport. The [ConfigProvider page](https://bestax.io/docs/api/helpers/config#where-portaled-content-goes) has the rest, and an example.\n\n```tsx\n<ConfigProvider portalContainer=\"#overlays\">\n <App />\n</ConfigProvider>\n```\n\n### Rendering in place\n\n`disabled` renders the children where the `Portal` is, on the server as well as the client. A component that portals only sometimes (behind an `appendToBody` option, say) can keep one code path.\n\n```tsx live\n<Portal disabled>\n <Notification>Rendered in place, as if there were no portal.</Notification>\n</Portal>\n```\n\n### Floating content and focus\n\nPositioning floating content next to its trigger is up to you; `Portal` only decides where it sits in the DOM.\n\nFocus follows the DOM too. Content portaled to the end of `document.body` is last in the Tab order, so interactive floating content should take focus when it opens and hand it back to its trigger when it closes. A focus trap doesn't cover what a `Portal` inside it renders, because that content is no longer inside the trapped element: render a nested overlay inside the trapped element, or trap it separately. To trap portaled content, put the trap on the element inside the `Portal`, as the [useFocusTrap portal example](https://bestax.io/docs/api/helpers/usefocustrap#with-portals) does.\n\n### Context and styles\n\nReact context reaches portaled children, so a `ConfigProvider` class prefix still applies, and their React events bubble up the React tree to the components around the `Portal`. CSS inheritance follows the DOM instead. A scoped `Theme` (one without `isRoot`) sets its CSS variables on a wrapper element that portaled content has left, so that content sees the page-level theme, and styles scoped to a shadow root don't reach `document.body` at all. To keep them, portal into an element inside the themed area or the shadow root: pass it as `container`, or make it the [default container](https://bestax.io/docs/api/helpers/portal#a-default-container) of a `ConfigProvider` around that area, which also reaches the content components such as `Popover` portal.\n\nThe `dir` and `lang` of a [`LocaleProvider`](https://bestax.io/docs/api/helpers/localeprovider) are the exception: the children keep them wherever they go. A container inside the provider's own wrapper takes the children as they are. Any other, `document.body` included, gets them inside one `<div>` carrying the provider's values, with `display: contents`, so it adds no box but is still an element. Which of the two is settled when the children mount, so a change of locale updates them in place rather than remounting them. Content that must be the container's direct child, such as a `<tr>` in a `<tbody>`, belongs in a container inside the provider.\n\n---\n\n## Accessibility\n\n- The DOM decides reading and Tab order, not the React tree. Content portaled to the end of `document.body` comes last in both, so a keyboard user can't reach it from the button that opened it by pressing Tab. Move focus into interactive floating content when it opens, and back to what opened it when it closes.\n- Give floating content a role and an accessible name (`role=\"dialog\"` with `aria-label` or `aria-labelledby`), and give its trigger `aria-expanded` and `aria-controls`. Ids work across the portal, since they are shared by the whole document.\n- Content that only appears after hydration is missing from the server HTML. Keep anything a reader or a crawler needs on first load out of a portal.\n\n---\n\n## Related Components\n\n- [`Modal`](https://bestax.io/docs/api/components/modal): Has its own `portal` prop, taking the same `true`, selector or element.\n- [`Toast`](https://bestax.io/docs/api/components/toast): Portals unless `inline` is set, into its own `container` when it has one.\n- [`Theme`](https://bestax.io/docs/api/helpers/theme): Scoped themes stay behind when content is portaled; see above.\n- [`ConfigProvider`](https://bestax.io/docs/api/helpers/config): Its `portalContainer` is where a `Portal` with no `container` goes.\n\n---\n\n## Additional Resources\n\n- [Storybook: Portal Stories](https://bestax.io/storybook/?path=/story/helpers-portal--default)\n- [React: `createPortal`](https://react.dev/reference/react-dom/createPortal)\n\n---\n\n## Props\n\n| Prop | Type | Default | Description |\n| ----------- | ------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children` | `ReactNode` | — | Content to render into the container. |\n| `container` | `string` \\| `HTMLElement` | a `ConfigProvider`'s `portalContainer`, else `document.body` | An element, or a `document.querySelector` selector looked up on each render. Omitted or empty, it is the `portalContainer` a `ConfigProvider` sets, or `document.body` when none is set. A selector that matches nothing means `document.body`. Moving to another target remounts the children. |\n| `disabled` | `boolean` | `false` | Render the children in place instead, on the server as well as the client. |"
72
+ "doc": "# Portal\n\n## Overview\n\n`Portal` renders its children into another part of the page, `document.body` unless `container` or a `ConfigProvider` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context.\n\nIt is the building block for your own overlays, such as a floating panel. The library's own overlays, such as `Modal`, `Popover`, `HoverCard` and `Command.Dialog`, already portal through their own props, so reach for `Portal` when you are building something they don't cover.\n\nReact's server renderer can't render a portal, so `Portal` renders nothing on the server and during hydration, and mounts its children in the commit that follows. The first client render matches the server markup, and there is no hydration warning to chase. In an app without server rendering it portals on the first render.\n\n---\n\n## Import\n\n```tsx\nimport { Portal } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n### Into `document.body`\n\nWith no `container`, and no [default container](https://bestax.io/docs/api/helpers/portal#a-default-container) set, the children go to the end of `document.body`, whatever clips or transforms the component that renders them.\n\n```tsx\nfunction Banner() {\n return (\n <Box overflow=\"clipped\">\n <p>This box clips its overflow.</p>\n <Portal>\n <Notification color=\"info\">\n Rendered at the end of document.body, outside the box.\n </Notification>\n </Portal>\n </Box>\n );\n}\n```\n\n### Into a container\n\n`container` takes an element or a `document.querySelector` selector. A selector suits an overlay root in your page shell (`<div id=\"overlays\" />`); an element suits a target your own component renders. Hold that element in state from a ref callback, so the portal renders once the target exists.\n\n```tsx live\nfunction example() {\n const [target, setTarget] = useState<HTMLElement | null>(null);\n\n return (\n <>\n <Box overflow=\"clipped\">\n <p>The notification is declared inside this box.</p>\n {target && (\n <Portal container={target}>\n <Notification color=\"success\">\n It renders into the element after the box instead.\n </Notification>\n </Portal>\n )}\n </Box>\n <div ref={setTarget} />\n </>\n );\n}\n```\n\nAn empty selector counts as no `container`. One that matches nothing falls back to `document.body` rather than throwing.\n\nA selector is looked up again on each render, so the content follows the target when the target changes. Moving to another target remounts the children, which resets their state. That includes a selector that only starts matching after the content has already shown in `document.body`, so render the target first (an overlay root in the page shell is), or pass the element once you have it, as above.\n\n### A default container\n\nA `ConfigProvider` with a `portalContainer` sets where every `Portal` inside it goes when it has no `container` of its own. The library's components that portal follow it too, such as `Popover`'s `appendToBody` panel or a `Modal` with `portal`. It takes the same element or selector as `container`, and follows the same rules, including the remount when the target turns up after the content has rendered. What those components portal is `position: fixed`, so the element has to sit where no ancestor has a `transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain` that includes `paint` or `layout`, any of which would place it against that ancestor instead of the viewport. The [ConfigProvider page](https://bestax.io/docs/api/helpers/config#where-portaled-content-goes) has the rest, and an example.\n\n```tsx\n<ConfigProvider portalContainer=\"#overlays\">\n <App />\n</ConfigProvider>\n```\n\n### Rendering in place\n\n`disabled` renders the children where the `Portal` is, on the server as well as the client. A component that portals only sometimes (behind an `appendToBody` option, say) can keep one code path.\n\n```tsx live\n<Portal disabled>\n <Notification>Rendered in place, as if there were no portal.</Notification>\n</Portal>\n```\n\n### Floating content and focus\n\nPositioning floating content next to its trigger is up to you; `Portal` only decides where it sits in the DOM.\n\nFocus follows the DOM too. Content portaled to the end of `document.body` is last in the Tab order, so interactive floating content should take focus when it opens and hand it back to its trigger when it closes. A focus trap doesn't cover what a `Portal` inside it renders, because that content is no longer inside the trapped element: render a nested overlay inside the trapped element, or trap it separately. To trap portaled content, put the trap on the element inside the `Portal`, as the [useFocusTrap portal example](https://bestax.io/docs/api/helpers/usefocustrap#with-portals) does.\n\n### Context and styles\n\nReact context reaches portaled children, so a `ConfigProvider` class prefix still applies, and their React events bubble up the React tree to the components around the `Portal`. CSS inheritance follows the DOM instead. A scoped `Theme` (one without `isRoot`) sets its CSS variables on a wrapper element that portaled content has left, so that content sees the page-level theme, and styles scoped to a shadow root don't reach `document.body` at all. To keep them, portal into an element inside the themed area or the shadow root: pass it as `container`, or make it the [default container](https://bestax.io/docs/api/helpers/portal#a-default-container) of a `ConfigProvider` around that area, which also reaches the content components such as `Popover` portal.\n\nThe `dir` and `lang` of a [`LocaleProvider`](https://bestax.io/docs/api/helpers/localeprovider) are the exception: the children keep them wherever they go. A container inside the provider's own wrapper takes the children as they are. Any other, `document.body` included, gets them inside one `<div>` carrying the provider's values, with `display: contents`, so it adds no box but is still an element. Which of the two is settled when the children mount, so a change of locale updates them in place rather than remounting them. Content that must be the container's direct child, such as a `<tr>` in a `<tbody>`, belongs in a container inside the provider.\n\n---\n\n## Accessibility\n\n- The DOM decides reading and Tab order, not the React tree. Content portaled to the end of `document.body` comes last in both, so a keyboard user can't reach it from the button that opened it by pressing Tab. Move focus into interactive floating content when it opens, and back to what opened it when it closes.\n- Give floating content a role and an accessible name (`role=\"dialog\"` with `aria-label` or `aria-labelledby`), and give its trigger `aria-expanded` and `aria-controls`. Ids work across the portal, since they are shared by the whole document.\n- A modal dialog's `aria-modal` hides the rest of the page from screen readers that honor it, content at the end of `document.body` included. The library's popups, such as a `Popover` with `appendToBody`, go into the `Modal`, `Dialog` or `Command.Dialog` they're opened in. A `Portal` doesn't, so inside one, give it a `container` inside the dialog.\n- Content that only appears after hydration is missing from the server HTML. Keep anything a reader or a crawler needs on first load out of a portal.\n\n---\n\n## Related Components\n\n- [`Modal`](https://bestax.io/docs/api/components/modal): Has its own `portal` prop, taking the same `true`, selector or element.\n- [`Toast`](https://bestax.io/docs/api/components/toast): Portals unless `inline` is set, into its own `container` when it has one.\n- [`Theme`](https://bestax.io/docs/api/helpers/theme): Scoped themes stay behind when content is portaled; see above.\n- [`ConfigProvider`](https://bestax.io/docs/api/helpers/config): Its `portalContainer` is where a `Portal` with no `container` goes.\n\n---\n\n## Additional Resources\n\n- [Storybook: Portal Stories](https://bestax.io/storybook/?path=/story/helpers-portal--default)\n- [React: `createPortal`](https://react.dev/reference/react-dom/createPortal)\n\n---\n\n## Props\n\n| Prop | Type | Default | Description |\n| ----------- | ------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children` | `ReactNode` | — | Content to render into the container. |\n| `container` | `string` \\| `HTMLElement` | a `ConfigProvider`'s `portalContainer`, else `document.body` | An element, or a `document.querySelector` selector looked up on each render. Omitted or empty, it is the `portalContainer` a `ConfigProvider` sets, or `document.body` when none is set. A selector that matches nothing means `document.body`. Moving to another target remounts the children. |\n| `disabled` | `boolean` | `false` | Render the children in place instead, on the server as well as the client. |"
73
73
  }
@@ -546,7 +546,7 @@
546
546
  "name": "appendToBody",
547
547
  "type": "boolean",
548
548
  "default": "false",
549
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default. On a narrow screen an in-place popover spans the bottom of the viewport, while a portaled one stays on its `position` corner.",
549
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`. On a narrow screen an in-place popover spans the bottom of the viewport, while a portaled one stays on its `position` corner.",
550
550
  "required": false,
551
551
  "inherited": true,
552
552
  "deprecated": false
@@ -917,7 +917,7 @@
917
917
  "name": "appendToBody",
918
918
  "type": "boolean",
919
919
  "default": "false",
920
- "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, `document.body` by default. On a narrow screen an in-place popover spans the bottom of the viewport, while a portaled one stays on its `position` corner.",
920
+ "description": "Render the popover through a portal, into the `portalContainer` a `ConfigProvider` sets, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`. On a narrow screen an in-place popover spans the bottom of the viewport, while a portaled one stays on its `position` corner.",
921
921
  "required": false,
922
922
  "inherited": false,
923
923
  "deprecated": false
@@ -408,7 +408,7 @@
408
408
  "name": "appendToBody",
409
409
  "type": "boolean",
410
410
  "default": "false",
411
- "description": "Renders the panel through a portal, into a `ConfigProvider`'s `portalContainer`, `document.body` by default.",
411
+ "description": "Renders the panel through a portal, into a `ConfigProvider`'s `portalContainer`, else the `Modal`, `Dialog` or `Command.Dialog` it's in, else `document.body`.",
412
412
  "required": false,
413
413
  "inherited": true,
414
414
  "deprecated": false
@@ -141,7 +141,11 @@ focus when it opens, and a `ContextMenu` moves it to its first item and closes o
141
141
  focus back. To trap portaled content,
142
142
  put the trap's ref on the element inside the `Portal`, as below with
143
143
  `useFocusTrap(panelRef, { active: open, restoreFocus: buttonRef })`. A focus trap doesn't cover
144
- what a `Portal` inside it renders, so render nested overlays inside the trapped element.
144
+ what a `Portal` inside it renders, so render nested overlays inside the trapped element. The
145
+ library's own popups, such as `Popover` with `appendToBody`, portal into the `Modal`, `Dialog`
146
+ or `Command.Dialog` they're opened in, but a `Portal` of yours doesn't: inside one, give it a
147
+ `container` inside the dialog, since its `aria-modal` hides the rest of the page from screen
148
+ readers.
145
149
 
146
150
  ```tsx
147
151
  {
@@ -25,10 +25,7 @@ instead of hand-writing markup.
25
25
  `Navbar.Item`, `Tabs.Tab`, `Hero.Body`, `Columns.Column`, `Table.Tr`) and as
26
26
  named exports of the same component (`CardHeader`, `NavbarItem`). A Next.js
27
27
  Server Component imports the named export: the dotted name is `undefined`
28
- there. Build a `Card` or `Modal` from its parts in a client component, though,
29
- since each checks which part each child is and can't recognize a part written in
30
- a Server Component; a few other components do the same
31
- (https://bestax.io/docs/guides/getting-started/react-setups#nextjs-specific).
28
+ there (https://bestax.io/docs/guides/getting-started/react-setups#nextjs-specific).
32
29
  See the component's linked page for the full set. Sub-parts do **not** all take helper
33
30
  props: the `Card.*`, `Table.*`, `Menu.*`, and `Hero.*` families do (most
34
31
  `Navbar.*` too), but `Modal.*`, `Tabs.*`, and `Message.*` sub-parts take none —
@@ -177,6 +177,11 @@ Rules that keep components consistent:
177
177
  call `createPortal` itself, as `Modal` does to render in place until hydration, resolves its
178
178
  target with `usePortalContainer` from the same module, so the `portalContainer` a
179
179
  `ConfigProvider` sets reaches it too. Add it to `src/__tests__/portal-container.test.tsx`.
180
+ A popup, a panel opened against something on the page such as Popover's, goes through
181
+ `PopupPortal` instead, or reads `usePopupContainer` where it calls `createPortal` itself, so
182
+ inside a `Modal`, `Dialog` or `Command.Dialog` it portals into the dialog, where screen
183
+ readers that honor `aria-modal` reach it. A new overlay that sets `aria-modal` wraps its
184
+ content in `PortalScope`, given the element that carries it.
180
185
 
181
186
  - **Portaled content keeps the locale.** A component that calls `createPortal` itself wraps what
182
187
  it portals in `LocalePortalScope` (`helpers/LocaleProvider.tsx`), so the `dir` and `lang` of a
@@ -168,7 +168,7 @@ them with no wiring.
168
168
  | Component | Key props |
169
169
  | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
170
170
  | `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`. |
171
- | `Combobox` | Either way: `multiple`, `keepFirst`, `inline`, `size`, `position` (`'auto'` keeps the list in the viewport), `appendToBody` (in a `Modal`, point a `ConfigProvider`'s `portalContainer` inside the modal), `open`/`defaultOpen`/`onOpenChange`, `onSelect(value)` and the Field props. Given `data` (strings or `{ value, label?, disabled?, group? }`): `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` with `multiple`, shown as tags), `placeholder`, `name`, `empty`, `itemTemplate`, and `disabled`, `required`, `form`, `id`, `aria-label`/`aria-labelledby` for the field; leaving the field empty clears one value. From parts: `.Input` (an `Input`, with `size` and the Field props on the root), `.Menu`, `.List`, `.Item` (`value`, `checked`, `disabled`), `.Group` (`label`), `.Empty`; you filter, keep the value and set the field's text in `onSelect`. |
171
+ | `Combobox` | Either way: `multiple`, `keepFirst`, `inline`, `size`, `position` (`'auto'` keeps the list in the viewport), `appendToBody` (inside a `Modal`, `Dialog` or `Command.Dialog` the list goes into the dialog), `open`/`defaultOpen`/`onOpenChange`, `onSelect(value)` and the Field props. Given `data` (strings or `{ value, label?, disabled?, group? }`): `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` with `multiple`, shown as tags), `placeholder`, `name`, `empty`, `itemTemplate`, and `disabled`, `required`, `form`, `id`, `aria-label`/`aria-labelledby` for the field; leaving the field empty clears one value. From parts: `.Input` (an `Input`, with `size` and the Field props on the root), `.Menu`, `.List`, `.Item` (`value`, `checked`, `disabled`), `.Group` (`label`), `.Empty`; you filter, keep the value and set the field's text in `onSelect`. |
172
172
  | `TreeSelect` | `data` (nodes: `value`, `label`, `disabled`, `icon`, `children`, `hasChildren`, `isLoading`, `loadError`, `onRetry`), `selectionMode` (`'single'`, `'checkbox'`, `'checkbox-independent'`), `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` in the checkbox modes, shown as tags), `checkedStrategy` (`'child'` leaves, `'parent'` topmost, `'all'`), `parentsSelectable`, `closeOnSelect`, `searchable` with `query`/`defaultQuery`/`onQueryChange`, `filter(query, item, path)` and `empty`, `expanded`/`defaultExpanded`/`onExpandedChange` (fetch lazy branches here), `open`/`defaultOpen`/`onOpenChange`, `clearable`, `itemTemplate`, `labels` (`choose`, `search`, `clear`, `loading`, `retry`, `loadingAnnouncement`, `loadedAnnouncement`), `name`, `form`, `required`, `disabled`, `color`, `size`, `isRounded`, `position`, `appendToBody` and the Field props. The field is a read-only combobox that opens a dialog; the `ref` is the field's. |
173
173
  | `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
174
174
  | `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size`, `isLoading` (set it here, not on a `Control` around it, whose spinner covers the right-hand button). |
@@ -390,10 +390,8 @@ is a `div` with `role="button"`).
390
390
  subcomponents (`Card.Header` + `.Title`/`.Icon`, `Card.Image`, `Card.Content`, `Card.Footer`,
391
391
  `Card.FooterItem`). Those parts are also named exports (`CardHeader`, `CardHeaderTitle`,
392
392
  `CardHeaderIcon`, `CardImage`, `CardContent`, `CardFooter`, `CardFooterItem`), the same
393
- components as the dotted names. In a Next.js App Router app, build a card from its parts inside a
394
- client component (a file that starts with `'use client'`): `Card` lays out its parts by checking
395
- which part each child is, and that check can't recognize a part written in a Server Component. A
396
- Server Component uses the props form.
393
+ components as the dotted names. A Next.js Server Component imports the parts by name, since a
394
+ dotted name is `undefined` there.
397
395
 
398
396
  | Prop | Type |
399
397
  | ------------------------------- | ----------------------------- |
@@ -115,7 +115,8 @@ Notes:
115
115
  ```
116
116
 
117
117
  - The `menu` partial **extends** stock Bulma menu (it draws `Menu.Section`'s heading and chevron,
118
- and keeps the active item marked in forced colors, Windows' contrast themes), so an app using
118
+ starts each item's text at the right on a right-to-left page, and keeps the active item marked
119
+ in forced colors, Windows' contrast themes), so an app using
119
120
  `Menu`, or a `TableOfContents`, which renders one, needs both, in either order, and the
120
121
  `tableofcontents` partial for the latter:
121
122
 
package/data/skills.json CHANGED
@@ -6,16 +6,16 @@
6
6
  "promptName": "custom-component",
7
7
  "dir": "bestax-custom-component",
8
8
  "references": [
9
- { "id": "api", "file": "references/api.md", "bytes": 16568 },
9
+ { "id": "api", "file": "references/api.md", "bytes": 16863 },
10
10
  {
11
11
  "id": "component-catalog",
12
12
  "file": "references/component-catalog.md",
13
- "bytes": 27117
13
+ "bytes": 26902
14
14
  },
15
15
  {
16
16
  "id": "library-contributor",
17
17
  "file": "references/library-contributor.md",
18
- "bytes": 23311
18
+ "bytes": 23738
19
19
  },
20
20
  { "id": "patterns", "file": "references/patterns.md", "bytes": 4632 }
21
21
  ],
@@ -64,7 +64,7 @@
64
64
  {
65
65
  "id": "layout-components",
66
66
  "file": "references/layout-components.md",
67
- "bytes": 25761
67
+ "bytes": 25559
68
68
  }
69
69
  ],
70
70
  "examples": [
@@ -162,7 +162,7 @@
162
162
  {
163
163
  "id": "modular-build",
164
164
  "file": "references/modular-build.md",
165
- "bytes": 19671
165
+ "bytes": 19735
166
166
  }
167
167
  ],
168
168
  "examples": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bestax-mcp",
3
- "version": "1.18.39",
3
+ "version": "1.18.41",
4
4
  "mcpName": "io.github.allxsmith/bestax-mcp",
5
5
  "description": "MCP server for @allxsmith/bestax-bulma — component props, examples, CSS variables and agent skills, served offline from a version-pinned index",
6
6
  "type": "module",
@@ -42,7 +42,7 @@
42
42
  "dependencies": {
43
43
  "@modelcontextprotocol/sdk": "^1.32.2",
44
44
  "zod": "^4.6.7",
45
- "@allxsmith/bestax-bulma": "^5.67.1"
45
+ "@allxsmith/bestax-bulma": "^5.67.3"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^26.6.6",