bestax-mcp 1.18.4 → 1.18.6

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.43.1"
5
+ "version": "5.45.0"
6
6
  },
7
7
  "docsBase": "https://bestax.io/docs",
8
8
  "categories": [
@@ -255,8 +255,8 @@
255
255
  "slug": "components/carousel",
256
256
  "import": "import { Carousel, CarouselItem } from '@allxsmith/bestax-bulma';",
257
257
  "compound": false,
258
- "propCount": 30,
259
- "exampleCount": 13
258
+ "propCount": 31,
259
+ "exampleCount": 16
260
260
  },
261
261
  {
262
262
  "name": "Cell",
@@ -278,7 +278,7 @@
278
278
  "import": "import { Checkboxes, Checkbox } from '@allxsmith/bestax-bulma';",
279
279
  "compound": false,
280
280
  "propCount": 8,
281
- "exampleCount": 7
281
+ "exampleCount": 8
282
282
  },
283
283
  {
284
284
  "name": "Checkboxes",
@@ -608,7 +608,7 @@
608
608
  "import": "import { Icon } from '@allxsmith/bestax-bulma';",
609
609
  "compound": false,
610
610
  "propCount": 15,
611
- "exampleCount": 12
611
+ "exampleCount": 14
612
612
  },
613
613
  {
614
614
  "name": "IconText",
@@ -1069,7 +1069,7 @@
1069
1069
  "slug": "elements/table",
1070
1070
  "import": "import { Table, Thead, Tbody, Tfoot, Tr, Th, Td } from '@allxsmith/bestax-bulma';",
1071
1071
  "compound": true,
1072
- "propCount": 27,
1072
+ "propCount": 28,
1073
1073
  "exampleCount": 7
1074
1074
  },
1075
1075
  {
@@ -29,6 +29,18 @@
29
29
  "title": "Indicator Styles",
30
30
  "code": "function IndicatorExample() {\n const [style, setStyle] = useState<'dots' | 'circles' | 'lines' | 'bars'>(\n 'dots'\n );\n return (\n <Block>\n <Buttons mb=\"4\">\n <Button\n onClick={() => setStyle('dots')}\n color={style === 'dots' ? 'primary' : undefined}\n >\n Dots\n </Button>\n <Button\n onClick={() => setStyle('circles')}\n color={style === 'circles' ? 'primary' : undefined}\n >\n Circles\n </Button>\n <Button\n onClick={() => setStyle('lines')}\n color={style === 'lines' ? 'primary' : undefined}\n >\n Lines\n </Button>\n <Button\n onClick={() => setStyle('bars')}\n color={style === 'bars' ? 'primary' : undefined}\n >\n Bars\n </Button>\n </Buttons>\n <Carousel indicatorStyle={style}>\n <CarouselItem>\n <Hero color=\"primary\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Paragraph textColor=\"white\">Slide 1</Paragraph>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"success\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Paragraph textColor=\"white\">Slide 2</Paragraph>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"danger\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Paragraph textColor=\"white\">Slide 3</Paragraph>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n </Carousel>\n </Block>\n );\n}"
31
31
  },
32
+ {
33
+ "title": "Indicators Over the Slides",
34
+ "code": "<Carousel indicatorInside>\n <CarouselItem>\n <Hero color=\"dark\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Slide 1</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"link\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Slide 2</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"primary\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Slide 3</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n</Carousel>"
35
+ },
36
+ {
37
+ "title": "Plain Indicators Over the Slides",
38
+ "code": "<Carousel indicatorInside overlayControls={false}>\n {['Garden Room', 'Harbor Suite', 'Loft', 'Courtyard Twin'].map(room => (\n <CarouselItem key={room}>\n <Card m=\"2\">\n <Card.Content pb=\"6\">\n <Title as=\"p\" size=\"5\">\n {room}\n </Title>\n <SubTitle as=\"p\" size=\"6\">\n Sleeps two\n </SubTitle>\n </Card.Content>\n </Card>\n </CarouselItem>\n ))}\n</Carousel>"
39
+ },
40
+ {
41
+ "title": "Overlay Arrows",
42
+ "code": "<Carousel overlayControls>\n <CarouselItem>\n <Hero color=\"dark\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Slide 1</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"link\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Slide 2</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n</Carousel>"
43
+ },
32
44
  {
33
45
  "title": "Without Arrows",
34
46
  "code": "<Carousel arrow={false}>\n <CarouselItem>\n <Hero color=\"primary\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Swipe or Click Dots</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n <CarouselItem>\n <Hero color=\"link\" size=\"medium\">\n <Hero.Body textAlign=\"centered\">\n <Title as=\"p\">Navigation Only</Title>\n </Hero.Body>\n </Hero>\n </CarouselItem>\n</Carousel>"
@@ -219,7 +231,16 @@
219
231
  "name": "indicatorInside",
220
232
  "type": "boolean",
221
233
  "default": "false",
222
- "description": "Position indicators inside carousel.",
234
+ "description": "Puts the indicators over the slides instead of below them, at the edge `indicatorPosition` names. It also turns on the overlay look unless `overlayControls` says otherwise, so on its own it gives the look made for photos: light arrows, and light indicators on a dark gradient band.",
235
+ "required": false,
236
+ "inherited": false,
237
+ "deprecated": false
238
+ },
239
+ {
240
+ "name": "overlayControls",
241
+ "type": "boolean",
242
+ "default": "indicatorInside",
243
+ "description": "The overlay look, made for photos. The arrows take the `--bulma-carousel-overlay-arrow-*` colors, and indicators over the slides take the `--bulma-carousel-overlay-indicator-*` colors on a dark gradient band. Indicators below the slides keep their plain colors, since they sit on the page. It follows `indicatorInside` unless you set it, so `overlayControls={false}` puts the indicators over the slides with the plain look, and `overlayControls` alone gives the arrows the overlay look with the indicators below. While it's on, `arrowColor` doesn't apply.",
223
244
  "required": false,
224
245
  "inherited": false,
225
246
  "deprecated": false
@@ -228,7 +249,7 @@
228
249
  "name": "indicatorPosition",
229
250
  "type": "'bottom' | 'top'",
230
251
  "default": "'bottom'",
231
- "description": "Indicator position. Default: 'bottom'.",
252
+ "description": "The edge of the slides `indicatorInside` puts the indicators on. Default: 'bottom'. Without `indicatorInside` they sit below the slides whatever this says, so leave it unset there.",
232
253
  "required": false,
233
254
  "inherited": false,
234
255
  "deprecated": false
@@ -309,7 +330,7 @@
309
330
  "name": "arrowColor",
310
331
  "type": "'light' | 'dark'",
311
332
  "default": null,
312
- "description": "Color theme for navigation arrows.",
333
+ "description": "Color theme for navigation arrows. Not applied while the overlay look is on (see `overlayControls`, which `indicatorInside` turns on), whose arrows take `--bulma-carousel-overlay-arrow-color`.",
313
334
  "required": false,
314
335
  "inherited": false,
315
336
  "deprecated": false
@@ -597,25 +618,25 @@
597
618
  {
598
619
  "css": "--bulma-carousel-overlay-arrow-color",
599
620
  "sass": "$carousel-overlay-arrow-color",
600
- "default": "var(--bulma-scheme-main)",
621
+ "default": "var(--bulma-white)",
601
622
  "scope": "root"
602
623
  },
603
624
  {
604
625
  "css": "--bulma-carousel-overlay-indicator-background",
605
626
  "sass": "$carousel-overlay-indicator-background",
606
- "default": "hsla(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-scheme-main-l), 0.5)",
627
+ "default": "hsla(var(--bulma-white-h), var(--bulma-white-s), var(--bulma-white-l), 0.5)",
607
628
  "scope": "root"
608
629
  },
609
630
  {
610
631
  "css": "--bulma-carousel-overlay-indicator-background-hover",
611
632
  "sass": "$carousel-overlay-indicator-background-hover",
612
- "default": "hsla(var(--bulma-scheme-h), var(--bulma-scheme-s), var(--bulma-scheme-main-l), 0.8)",
633
+ "default": "hsla(var(--bulma-white-h), var(--bulma-white-s), var(--bulma-white-l), 0.8)",
613
634
  "scope": "root"
614
635
  },
615
636
  {
616
637
  "css": "--bulma-carousel-overlay-indicator-background-active",
617
638
  "sass": "$carousel-overlay-indicator-background-active",
618
- "default": "var(--bulma-scheme-main)",
639
+ "default": "var(--bulma-white)",
619
640
  "scope": "root"
620
641
  },
621
642
  {
@@ -32,9 +32,13 @@
32
32
  {
33
33
  "title": "Theming the Box",
34
34
  "code": "<Checkbox className=\"ink-checkbox\" defaultChecked>\n Remember me\n</Checkbox>"
35
+ },
36
+ {
37
+ "title": "Box Size",
38
+ "code": "<Checkbox className=\"compact-checkbox\">Remember me</Checkbox>"
35
39
  }
36
40
  ],
37
- "accessibility": "- Each `Checkbox` is rendered as a `<label>` wrapping an `<input type=\"checkbox\">` and the label text, for optimal accessibility.\n- The clickable area includes both box and label by default.\n- Browsers expose a box with `indeterminate` set as mixed, so the component adds no ARIA for it.\n- An unchecked box shows only its border, `--bulma-checkbox-border-color`. It defaults to `--bulma-text-weak`, which holds the 3:1 contrast WCAG asks of a control's boundary (non-text contrast) on Bulma's page and card surfaces in light and dark. Hold a replacement border, and a custom check color on its fill, to 3:1 as well.\n- In forced colors (Windows contrast themes), the box, the check mark and the mixed dash take system colors, so they stay visible whatever the variables hold.",
41
+ "accessibility": "- Each `Checkbox` is rendered as a `<label>` wrapping an `<input type=\"checkbox\">` and the label text, for optimal accessibility.\n- The clickable area includes both box and label by default.\n- Browsers expose a box with `indeterminate` set as mixed, so the component adds no ARIA for it.\n- The box is at least 24px square, the minimum size WCAG 2.2 sets for a target (2.5.8), whatever `--bulma-checkbox-size` says. [Box Size](https://bestax.io/docs/api/form/checkbox#box-size) shows how to make it smaller, and why to keep the label text inside the `Checkbox` when you do.\n- An unchecked box shows only its border, `--bulma-checkbox-border-color`. It defaults to `--bulma-text-weak`, which holds the 3:1 contrast WCAG asks of a control's boundary (non-text contrast) on Bulma's page and card surfaces in light and dark. Hold a replacement border, and a custom check color on its fill, to 3:1 as well.\n- In forced colors (Windows contrast themes), the box, the check mark and the mixed dash take system colors, so they stay visible whatever the variables hold.",
38
42
  "related": ["Checkboxes", "Field", "useBulmaClasses"],
39
43
  "storybook": "https://bestax.io/storybook/?path=/story/form-checkbox--default",
40
44
  "summary": "The `Checkbox` component provides a Bulma-styled checkbox input.",
@@ -87,8 +91,8 @@
87
91
  {
88
92
  "name": "indeterminate",
89
93
  "type": "boolean",
90
- "default": "false",
91
- "description": "Shows the mixed state, as in a \"Select all\" box when some of what it covers is checked. It's a look and an announcement only: `checked` is still the value, and a click still toggles `checked`. A browser clears the mixed state on a click, so the component sets it again from this prop after every click and every render. Left unset, the component doesn't touch it, and once it has set it, going back to unset clears it once and hands it back. It has no HTML attribute, so server-rendered markup can't carry it, and a mixed box renders unmixed until hydration.",
94
+ "default": null,
95
+ "description": "Shows the mixed state, as in a \"Select all\" box when some of what it covers is checked. It's a look and an announcement only: `checked` is still the value, and a click still toggles `checked`. Set to `true` or `false`, it's controlled: a browser clears the mixed state on a click, so the component sets it again from this prop after every click and every render. Unset isn't `false`. The component leaves the input's own `indeterminate` property alone, so a ref can set it, and going from set to unset clears it once and hands it back. It has no HTML attribute, so server-rendered markup can't carry it, and a mixed box renders unmixed until hydration.",
92
96
  "required": false,
93
97
  "inherited": false,
94
98
  "deprecated": false
@@ -206,8 +206,8 @@
206
206
  {
207
207
  "name": "indeterminate",
208
208
  "type": "boolean",
209
- "default": "false",
210
- "description": "Shows the mixed state, as in a \"Select all\" box when some of what it covers is checked. It's a look and an announcement only: `checked` is still the value, and a click still toggles `checked`. A browser clears the mixed state on a click, so the component sets it again from this prop after every click and every render. Left unset, the component doesn't touch it, and once it has set it, going back to unset clears it once and hands it back. It has no HTML attribute, so server-rendered markup can't carry it, and a mixed box renders unmixed until hydration.",
209
+ "default": null,
210
+ "description": "Shows the mixed state, as in a \"Select all\" box when some of what it covers is checked. It's a look and an announcement only: `checked` is still the value, and a click still toggles `checked`. Set to `true` or `false`, it's controlled: a browser clears the mixed state on a click, so the component sets it again from this prop after every click and every render. Unset isn't `false`. The component leaves the input's own `indeterminate` property alone, so a ref can set it, and going from set to unset clears it once and hands it back. It has no HTML attribute, so server-rendered markup can't carry it, and a mixed box renders unmixed until hydration.",
211
211
  "required": false,
212
212
  "inherited": false,
213
213
  "deprecated": false
@@ -13,6 +13,14 @@
13
13
  "title": "Font Awesome Large",
14
14
  "code": "<Icon\n name=\"star\"\n variant=\"solid\"\n features=\"fa-2x\"\n ariaLabel=\"Star icon large\"\n/>"
15
15
  },
16
+ {
17
+ "title": "Brand Icons",
18
+ "code": "<Block display=\"flex\" alignItems=\"center\">\n <Icon name=\"github\" variant=\"brands\" ariaLabel=\"GitHub\" />\n <Icon name=\"linkedin\" variant=\"brands\" ariaLabel=\"LinkedIn\" />\n <Icon name=\"mastodon\" variant=\"brands\" ariaLabel=\"Mastodon\" />\n</Block>"
19
+ },
20
+ {
21
+ "title": "Brand Icons",
22
+ "code": "<Block display=\"flex\" alignItems=\"center\">\n <Link href=\"https://github.com\" aria-label=\"GitHub\" mr=\"3\">\n <Icon name=\"github\" variant=\"brands\" size=\"medium\" features=\"fa-lg\" />\n </Link>\n <Link href=\"https://www.linkedin.com\" aria-label=\"LinkedIn\" mr=\"3\">\n <Icon name=\"linkedin\" variant=\"brands\" size=\"medium\" features=\"fa-lg\" />\n </Link>\n <Link href=\"https://mastodon.social\" aria-label=\"Mastodon\">\n <Icon name=\"mastodon\" variant=\"brands\" size=\"medium\" features=\"fa-lg\" />\n </Link>\n</Block>"
23
+ },
16
24
  {
17
25
  "title": "Spinning Icon",
18
26
  "code": "<Icon\n name=\"spinner\"\n variant=\"solid\"\n features={['fa-spin', 'fa-2x']}\n ariaLabel=\"Loading spinner\"\n/>"
@@ -27,7 +27,7 @@
27
27
  },
28
28
  {
29
29
  "title": "Custom Alignment and Width",
30
- "code": "<Table>\n <Thead>\n <Tr>\n <Th isAligned=\"left\" width={200}>\n Name\n </Th>\n <Th isAligned=\"right\">Score</Th>\n </Tr>\n </Thead>\n <Tbody>\n <Tr>\n <Td>Jane Doe</Td>\n <Td>98</Td>\n </Tr>\n </Tbody>\n</Table>"
30
+ "code": "<Table>\n <Thead>\n <Tr>\n <Th isAligned=\"left\" width={200}>\n Name\n </Th>\n <Th isAligned=\"right\">Score</Th>\n </Tr>\n </Thead>\n <Tbody>\n <Tr>\n <Td isAligned=\"left\">Jane Doe</Td>\n <Td isAligned=\"right\">98</Td>\n </Tr>\n </Tbody>\n</Table>"
31
31
  },
32
32
  {
33
33
  "title": "Compound (dot-notation) usage",
@@ -333,7 +333,7 @@
333
333
  },
334
334
  {
335
335
  "path": "Table.Td",
336
- "summary": "Td component for rendering a styled Bulma table cell. Supports Bulma color modifiers and helper classes for additional styling.",
336
+ "summary": "Td component for rendering a styled Bulma table cell. Supports alignment, Bulma color modifiers, and helper classes for additional styling.",
337
337
  "component": "Td",
338
338
  "props": [
339
339
  {
@@ -345,6 +345,15 @@
345
345
  "inherited": false,
346
346
  "deprecated": false
347
347
  },
348
+ {
349
+ "name": "isAligned",
350
+ "type": "'left' | 'right' | 'centered'",
351
+ "default": null,
352
+ "description": "Text alignment for the table cell ('left', 'right', 'centered').",
353
+ "required": false,
354
+ "inherited": false,
355
+ "deprecated": false
356
+ },
348
357
  {
349
358
  "name": "color",
350
359
  "type": "TableColor",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  {
17
17
  "title": "Dark Mode",
18
- "code": "import { useState } from 'react';\n\nfunction App() {\n const [mode, setMode] = useState<'light' | 'dark' | 'system'>('system');\n return (\n <Theme isRoot colorMode={mode}>\n <Button color=\"primary\" onClick={() => setMode('dark')}>\n Dark\n </Button>\n <Button color=\"primary\" onClick={() => setMode('light')}>\n Light\n </Button>\n <Button onClick={() => setMode('system')}>System</Button>\n </Theme>\n );\n}"
18
+ "code": "// Pairs with an inline <head> script in index.html that writes data-theme\n// before the first paint: the choice saved under STORAGE_KEY, or else the\n// OS preference. The Theme API page's Dark Mode section has the script.\nimport { useState } from 'react';\nimport { Theme, ToggleButton } from '@allxsmith/bestax-bulma';\n\ntype ColorMode = 'light' | 'dark';\n\n// The key the index.html script reads the saved choice from.\nconst STORAGE_KEY = 'theme';\n\n// The scheme the index.html script wrote on <html>.\nfunction initialColorMode(): ColorMode {\n if (typeof document === 'undefined') return 'light';\n return document.documentElement.getAttribute('data-theme') === 'dark'\n ? 'dark'\n : 'light';\n}\n\nfunction App() {\n const [mode, setMode] = useState<ColorMode>(initialColorMode);\n\n const setDark = (dark: boolean) => {\n const next: ColorMode = dark ? 'dark' : 'light';\n setMode(next);\n try {\n localStorage.setItem(STORAGE_KEY, next);\n } catch {\n // Storage is blocked, so the choice lasts until the page reloads.\n }\n };\n\n return (\n <Theme isRoot colorMode={mode}>\n <ToggleButton pressed={mode === 'dark'} onPressedChange={setDark}>\n Dark mode\n </ToggleButton>\n </Theme>\n );\n}"
19
19
  },
20
20
  {
21
21
  "title": "Local Theme (Component Level)",
@@ -467,5 +467,5 @@
467
467
  }
468
468
  ],
469
469
  "cssVars": [],
470
- "doc": "# Theme\n\n## Overview\n\nThe `Theme` component provides a powerful way to customize Bulma's appearance using CSS custom properties (CSS variables). It allows you to override Bulma's design tokens like colors, spacing, typography, and other visual properties either globally or locally within specific component trees. The Theme component supports both CSS variable injection via props and direct CSS variable objects.\n\n---\n\n## Import\n\n```tsx\nimport { Theme } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n### Basic Theme Customization\n\n```tsx\nfunction BasicThemeCustomization() {\n return (\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <Box p=\"4\">\n <Button color=\"primary\">Purple Primary Button</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Global Theme (Root Level)\n\n```tsx\nfunction GlobalTheme() {\n return (\n <Theme isRoot={true} primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <div>\n <Button color=\"primary\">Global Purple Theme</Button>\n <Button color=\"info\">Info Button</Button>\n </div>\n </Theme>\n );\n}\n```\n\n### Dark Mode\n\nThe `colorMode` prop sets Bulma's `data-theme` on `<html>`, flipping the light/dark scheme globally.\nUse `'system'` to follow the OS preference.\n\nA prefixed stylesheet reads Bulma's prefixed attribute instead (`data-bestax-theme` for\n`bestax-prefixed.css` and `bestax-no-helpers-prefixed.css`). Under a `ConfigProvider`\n`classPrefix`, `colorMode` writes that attribute as well as `data-theme`, so it works with the\nprefixed builds.\n\n:::warning Single-mode designs should pin the scheme\n\nOmitting `colorMode` preserves whatever `data-theme` is already set — and when nothing has set\none (the usual case), the scheme follows the visitor's OS: Bulma's text colors flip on a\ndark-mode machine even if your design is light-only, breaking contrast against any fixed custom\nbackgrounds. If you support only one mode, pin it on the `<html>` in your `index.html`\n(`<html lang=\"en\" data-theme=\"light\">`); if you support both, don't hardcode surface/text\ncolors — see [Dark Mode & Contrast](https://bestax.io/docs/guides/features/css-variables#dark-mode--contrast).\n\n:::\n\n`colorMode` writes the attribute from an effect, so a page rendered in the browser paints its\nfirst frame with whatever `<html>` already has, which is why a fixed scheme belongs in\n`index.html`. A toggle that remembers the visitor's choice writes it before that frame, from an\ninline `<head>` script, and starts `colorMode` from the attribute the script wrote; the\n`bestax-theming` skill's\n[dark-mode example](https://github.com/allxsmith/bestax/blob/main/skills/bestax-theming/examples/dark-mode.tsx)\nhas the script and the app's side.\n\n```tsx\nimport { useState } from 'react';\n\nfunction App() {\n const [mode, setMode] = useState<'light' | 'dark' | 'system'>('system');\n return (\n <Theme isRoot colorMode={mode}>\n <Button color=\"primary\" onClick={() => setMode('dark')}>\n Dark\n </Button>\n <Button color=\"primary\" onClick={() => setMode('light')}>\n Light\n </Button>\n <Button onClick={() => setMode('system')}>System</Button>\n </Theme>\n );\n}\n```\n\n### Local Theme (Component Level)\n\n```tsx\nfunction LocalTheme() {\n return (\n <div>\n <Button color=\"primary\">Standard Primary</Button>\n\n <Theme primaryH=\"120\" primaryS=\"100%\" primaryL=\"40%\">\n <Button color=\"primary\">Green Primary</Button>\n </Theme>\n\n <Button color=\"primary\">Standard Primary Again</Button>\n </div>\n );\n}\n```\n\n### Using CSS Variables Object\n\n```tsx\nfunction SunsetTheme() {\n const sunsetTheme = {\n '--bulma-scheme-h': '18',\n '--bulma-scheme-s': '90%',\n '--bulma-light-l': '85%',\n '--bulma-dark-l': '20%',\n '--bulma-primary-h': '25',\n '--bulma-primary-s': '85%',\n '--bulma-primary-l': '55%',\n };\n\n return (\n <Theme bulmaVars={sunsetTheme} isRoot>\n <Box p=\"4\">\n <Title>Sunset Theme</Title>\n <Button color=\"primary\">Sunset Button</Button>\n <Button color=\"info\">Info Button</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Advanced CSS Variables Usage\n\nBulma derives the body font and the control radius from these variables at `:root`, so set\ntypography and radius on an `isRoot` theme.\n\n```tsx\nfunction TypographyTheme() {\n const typographyTheme = {\n '--bulma-family-primary': '\"Helvetica Neue\", sans-serif',\n '--bulma-family-code': '\"Fira Code\", monospace',\n '--bulma-size-normal': '16px',\n '--bulma-weight-bold': '700',\n };\n\n return (\n <Theme isRoot bulmaVars={typographyTheme}>\n <Title>Custom Typography</Title>\n <SubTitle>With custom fonts and weights</SubTitle>\n </Theme>\n );\n}\n```\n\n```tsx\nfunction RadiusTheme() {\n // `radius` is the border radius helper, as on every component, so the radius scale goes through bulmaVars\n const radii = {\n '--bulma-radius-small': '6px',\n '--bulma-radius': '12px',\n '--bulma-radius-medium': '16px',\n '--bulma-radius-large': '20px',\n };\n\n return (\n <Theme isRoot bulmaVars={radii}>\n <Button color=\"primary\">Rounder Button</Button>\n </Theme>\n );\n}\n```\n\nSpacing that Bulma reads where it is used, such as the block and column gaps, works on a scoped\ntheme:\n\n```tsx\nfunction SpacingTheme() {\n const spacingTheme = {\n '--bulma-block-spacing': '2rem',\n '--bulma-column-gap': '1rem',\n };\n\n return (\n <Theme bulmaVars={spacingTheme}>\n <Columns>\n <Column>\n <Box>Wider column gap</Box>\n <Box>Wider block spacing</Box>\n </Column>\n <Column>\n <Box>Wider column gap</Box>\n </Column>\n </Columns>\n </Theme>\n );\n}\n```\n\n### Complete Color Scheme\n\n```tsx\nfunction ForestTheme() {\n const forestScheme = {\n schemeH: '150', // forest green hue\n schemeS: '50%',\n lightL: '80%',\n lightInvertL: '20%',\n darkL: '18%',\n darkInvertL: '85%',\n softL: '55%',\n boldL: '35%',\n primaryH: '160',\n primaryS: '60%',\n primaryL: '45%',\n linkH: '155',\n linkS: '65%',\n linkL: '40%',\n successH: '120',\n successS: '70%',\n successL: '45%',\n warningH: '45',\n warningS: '85%',\n warningL: '55%',\n dangerH: '355',\n dangerS: '75%',\n dangerL: '50%',\n hoverBackgroundLDelta: '4%',\n activeBackgroundLDelta: '8%',\n };\n\n return (\n <Theme {...forestScheme} isRoot>\n <Box p=\"4\">\n <Title>Forest Theme</Title>\n <Button color=\"primary\">Forest Primary</Button>\n <Button color=\"success\">Success</Button>\n <Button color=\"info\">Info</Button>\n <Button color=\"warning\">Warning</Button>\n <Button color=\"danger\">Danger</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Theme with Styling\n\n```tsx\nfunction StyledTheme() {\n return (\n <Theme\n className=\"custom-theme-wrapper\"\n primaryH=\"45\"\n primaryS=\"100%\"\n primaryL=\"50%\"\n p=\"5\"\n m=\"3\"\n textAlign=\"centered\"\n >\n <Title>Styled Theme Container</Title>\n <Button color=\"primary\">Styled Button</Button>\n </Theme>\n );\n}\n```\n\n### Nested Themes\n\n```tsx\nfunction NestedThemes() {\n return (\n <Theme primaryH=\"210\" primaryS=\"60%\" primaryL=\"45%\" p=\"4\">\n <Title>Outer Theme (Blue)</Title>\n <Button color=\"primary\">Blue Primary</Button>\n\n <Theme primaryH=\"120\" primaryS=\"70%\" primaryL=\"40%\" p=\"3\" mt=\"4\">\n <Title size=\"4\">Inner Theme (Green)</Title>\n <Button color=\"primary\">Green Primary</Button>\n </Theme>\n\n <Button color=\"primary\" mt=\"3\">\n Blue Primary Again\n </Button>\n </Theme>\n );\n}\n```\n\n### Theme with ConfigProvider\n\n```tsx\nfunction PrefixedTheme() {\n return (\n <ConfigProvider classPrefix=\"bestax-\">\n <Theme primaryH=\"300\" primaryS=\"80%\" primaryL=\"50%\" isRoot>\n <Button color=\"primary\">Prefixed & Themed Button</Button>\n </Theme>\n </ConfigProvider>\n );\n}\n```\n\n---\n\n## Best Practices\n\n### Global vs Local Themes\n\n- Use `isRoot={true}` for application-wide themes that should affect all components\n- Use local themes (default `isRoot={false}`) for component-specific styling or theme variations\n- Local themes inherit from parent themes and can override specific variables\n- Several `isRoot` themes can be mounted at once, such as a color theme at the app root and a\n typography theme next to it. Each adds its own variables at `:root`; where two set the same\n variable, the inner or later-mounted one wins, as with nested local themes, and unmounting one\n removes only its own variables\n\n### CSS Variable Naming\n\n- All Bulma CSS variables follow the `--bulma-*` naming convention\n- Named props exist for the scheme and color variables only (like `primaryH`); every other variable, typography and radius included, goes through `bulmaVars`\n- `bulmaVars` keys are the full variable names (`'--bulma-family-primary'`), not camelCase\n- `bulmaVars` also accepts the scheme surface variables — `--bulma-scheme-main`, `--bulma-scheme-main-bis`, `--bulma-scheme-main-ter`, `--bulma-scheme-invert`, `--bulma-scheme-invert-bis`, `--bulma-scheme-invert-ter` — so overriding `--bulma-scheme-main-bis`/`-ter` re-tints every scheme-background band (e.g., `<Section bgColor=\"scheme-main-bis\">`) at once\n- `bulmaVars` also accepts `--bulma-shadow` (plus `--bulma-shadow-h`/`-s`/`-l`) — the upstream token that `.box`, `.card`, `.dropdown`, and `.panel` derive their own shadow variables from. Those selectors re-declare their derived variable (e.g. `--bulma-box-shadow`) on themselves, so setting it from an ancestor `Theme` is only inherited and never wins; overriding `--bulma-shadow` itself is the one override that cascades. There is no individual `shadow` prop for this — `shadow` already names the unrelated `shadowless` helper class — so reach it through `bulmaVars`\n\n### Performance Considerations\n\n- Prefer setting themes at higher levels in your component tree rather than deeply nested\n- CSS variables are inherited, so child themes only need to override specific variables\n\n### Color System\n\n- Bulma uses HSL (Hue, Saturation, Lightness) for its color system\n- Hue values range from 0-360 (color wheel degrees)\n- Saturation and Lightness are typically percentages (e.g., '50%')\n- The scheme variables control the base color relationships\n\n---\n\n## API Reference\n\n### Named Props and `bulmaVars`\n\n`ThemeProps` has a named, camelCase prop for each scheme and color variable, listed under\n[CSS Variable Props](https://bestax.io/docs/api/helpers/theme#css-variable-props). Each converts to its CSS variable by the same rule:\n\n```\npropName → --bulma-prop-name\n```\n\n- `primaryH` → `--bulma-primary-h`\n- `schemeS` → `--bulma-scheme-s`\n- `lightL` → `--bulma-light-l`\n- `hoverBackgroundLDelta` → `--bulma-hover-background-l-delta`\n\nEvery other variable (typography, radius, spacing, and the rest) has no named prop in\n`ThemeProps` and goes through `bulmaVars`, keyed by its full `--bulma-*` name. JavaScript code\nthat passes one as a camelCase prop (`familyPrimary`) may still render, but it is outside the\ntyped API, so move it to `bulmaVars`:\n\n```tsx\n<Theme\n isRoot\n primaryH=\"350\"\n primaryS=\"73%\"\n primaryL=\"44%\"\n bulmaVars={{\n '--bulma-radius': '2px',\n '--bulma-radius-small': '1px',\n '--bulma-family-primary': '\"IBM Plex Sans\", system-ui, sans-serif',\n '--bulma-family-code': '\"IBM Plex Mono\", ui-monospace, monospace',\n }}\n>\n <App />\n</Theme>\n```\n\nSome of those names are already helper props, so they cannot double as CSS-variable props:\n\n- `radius` is the border radius helper, as on every other component, so `<Theme radius=\"radiusless\">`\n adds `is-radiusless` to the wrapper. On a Theme it also sets `--bulma-radius` to `0`, so what\n is inside the Theme loses its radius too; under `isRoot` it writes that at `:root`, squaring\n everything on the page that takes its radius from it. The sizes (`small`, `normal`, `large`,\n `rounded`) add their `has-radius-*` class to the wrapper and set no variable, so they round\n the wrapper and leave what is inside alone; under `isRoot` there is no wrapper, so they do\n nothing and warn in development. Set any other `--bulma-radius` through\n `bulmaVars`. `<Theme radius=\"2px\" />` is a type error; in JavaScript it still sets\n `--bulma-radius`, which is how the prop used to behave, but that route is deprecated and logs\n a warning in development.\n- `shadow` is the `shadowless` helper. Set `--bulma-shadow` through `bulmaVars`.\n- `columnGap` is the column gap helper, as on every other component, so `<Theme columnGap=\"2\">`\n adds `is-column-gap-2` to the wrapper and sets no variable; under `isRoot` there is no wrapper,\n so it does nothing and warns in development. Set the columns gutter, `--bulma-column-gap`,\n through `bulmaVars`. Before the gap helpers, JavaScript could pass `columnGap` to set that\n variable. A string that is not a gap step (`columnGap=\"1rem\"`) still does, through a\n deprecated route that logs a warning in development, but a gap step does not:\n `columnGap=\"0\"`, which used to zero the gutters inside the Theme, now adds `is-column-gap-0`\n to the wrapper.\n\n### Props vs bulmaVars\n\nYou can set the scheme and color variables in two ways:\n\n1. **Named Props** (recommended where one exists):\n\n ```tsx\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <App />\n </Theme>\n ```\n\n2. **bulmaVars Object** (for every other variable, or when you have many to set):\n\n ```tsx\n <Theme\n bulmaVars={{\n '--bulma-primary-h': '270',\n '--bulma-primary-s': '100%',\n '--bulma-primary-l': '50%',\n '--bulma-block-spacing': '2rem',\n }}\n >\n <App />\n </Theme>\n ```\n\n3. **Combined** (props take precedence over bulmaVars):\n ```tsx\n <Theme\n primaryH=\"270\" // This takes precedence\n bulmaVars={{\n '--bulma-primary-h': '180', // This is overridden\n '--bulma-block-spacing': '2rem',\n }}\n >\n <App />\n </Theme>\n ```\n\n### TypeScript Support\n\n`bulmaVars` is typed as a partial record keyed by the union of every variable `Theme` accepts.\nWrite it as an object literal and your editor autocompletes the keys and flags a misspelled or\nunsupported one as a type error. A separately declared object only has to share one valid key\nto typecheck, so a typo in it is silently dropped; annotate it with\n`satisfies ThemeProps['bulmaVars']` to keep the check:\n\n```tsx\n<Theme\n primaryH=\"270\"\n bulmaVars={{\n '--bulma-block-spacing': '2rem', // start typing '--bulma-' to list the rest\n }}\n>\n <App />\n</Theme>\n```\n\n---\n\n## See Also\n\n- [ConfigProvider](https://bestax.io/docs/api/helpers/config) - For class prefixing and configuration\n- [useBulmaClasses](https://bestax.io/docs/api/helpers/usebulmaclasses) - For applying Bulma helper classes\n- [Bulma CSS Variables Documentation](https://bulma.io/documentation/features/css-variables/) - Official Bulma CSS variables reference\n\n---\n\n## Props\n\n| Prop | Type | Description |\n| ----------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children` | `ReactNode` | The child components to apply the theme to. |\n| `className` | `string` | Additional CSS classes for the theme wrapper. A root theme has no wrapper, so under `isRoot` it does nothing and warns in development. |\n| `isRoot` | `boolean` | When `true`, applies CSS variables globally at `:root` level. When `false` (default), applies variables only to the wrapper div. Several root themes compose; an inner or later-mounted one wins a variable they share. A root theme renders no wrapper, so `className` and the helper props, such as `m` or `shadow`, do nothing on it and warn in development. |\n| `colorMode` | `'light' \\| 'dark' \\| 'system'` | Sets Bulma's light/dark scheme by writing the `data-theme` attribute on `<html>`, plus `data-<prefix>theme` inside a `ConfigProvider` with a `classPrefix`. If the prefix cannot form an attribute name, `'light'` and `'dark'` write only `data-theme` and warn in development. Always global (even on a scoped `Theme`). `'system'` removes them so Bulma follows the OS `prefers-color-scheme`. Omit to leave the current setting untouched. |\n| `bulmaVars` | `ThemeProps['bulmaVars']` | Object mapping Bulma CSS variable names to string values (e.g., `{'--bulma-primary-h': '210'}`). Keys are limited to the `BulmaVars` type; anything else is not applied. A variable Bulma declares on the component itself changes nothing from here and warns in development; see [Which Variables a Theme Can Change](https://bestax.io/docs/api/helpers/theme#which-variables-a-theme-can-change). |\n| `columnGap` | [`BulmaGapStep`](https://bestax.io/docs/api/helpers/valid-values) | The column gap helper: a gap step adds `is-column-gap-<step>` to the wrapper div and sets no variable, so under `isRoot` it does nothing and warns in development. Set `--bulma-column-gap` through `bulmaVars`. Any other string still sets `--bulma-column-gap`, a deprecated route that warns in development. |\n| `radius` | `'radiusless' \\| 'small' \\| 'normal' \\| 'large' \\| 'rounded'` | The border radius helper. `radiusless` adds `is-radiusless` to the wrapper div and sets `--bulma-radius` to `0`, at `:root` under `isRoot`. The sizes add their `has-radius-*` class to the wrapper and set no variable, so under `isRoot` they do nothing and warn in development. Any other string still sets `--bulma-radius`, a deprecated route that warns in development. |\n\n### CSS Variable Props\n\nThe Theme component accepts individual CSS variable props that correspond to Bulma's design tokens. These props are automatically converted to their corresponding CSS custom properties (e.g., `primaryH` → `--bulma-primary-h`).\n\n#### Scheme Variables\n\n| Prop | Type | Description | CSS Variable |\n| ------------------------ | -------- | ------------------------------------------------- | ----------------------------------- |\n| `schemeH` | `string` | Base hue for the color scheme (0-360) | `--bulma-scheme-h` |\n| `schemeS` | `string` | Base saturation for the color scheme (percentage) | `--bulma-scheme-s` |\n| `lightL` | `string` | Lightness value for light backgrounds | `--bulma-light-l` |\n| `lightInvertL` | `string` | Inverted lightness for light backgrounds | `--bulma-light-invert-l` |\n| `darkL` | `string` | Lightness value for dark backgrounds | `--bulma-dark-l` |\n| `darkInvertL` | `string` | Inverted lightness for dark backgrounds | `--bulma-dark-invert-l` |\n| `softL` | `string` | Lightness value for soft colors | `--bulma-soft-l` |\n| `boldL` | `string` | Lightness value for bold colors | `--bulma-bold-l` |\n| `softInvertL` | `string` | Inverted lightness for soft colors | `--bulma-soft-invert-l` |\n| `boldInvertL` | `string` | Inverted lightness for bold colors | `--bulma-bold-invert-l` |\n| `hoverBackgroundLDelta` | `string` | Lightness delta for hover background states | `--bulma-hover-background-l-delta` |\n| `activeBackgroundLDelta` | `string` | Lightness delta for active background states | `--bulma-active-background-l-delta` |\n| `hoverBorderLDelta` | `string` | Lightness delta for hover border states | `--bulma-hover-border-l-delta` |\n| `activeBorderLDelta` | `string` | Lightness delta for active border states | `--bulma-active-border-l-delta` |\n| `hoverColorLDelta` | `string` | Lightness delta for hover text color states | `--bulma-hover-color-l-delta` |\n| `activeColorLDelta` | `string` | Lightness delta for active text color states | `--bulma-active-color-l-delta` |\n| `hoverShadowADelta` | `string` | Alpha delta for hover shadow states | `--bulma-hover-shadow-a-delta` |\n| `activeShadowADelta` | `string` | Alpha delta for active shadow states | `--bulma-active-shadow-a-delta` |\n\n#### Color Variables\n\n| Prop | Type | Description | CSS Variable |\n| ---------- | -------- | ------------------------------------- | ------------------- |\n| `primaryH` | `string` | Primary color hue (0-360) | `--bulma-primary-h` |\n| `primaryS` | `string` | Primary color saturation (percentage) | `--bulma-primary-s` |\n| `primaryL` | `string` | Primary color lightness (percentage) | `--bulma-primary-l` |\n| `linkH` | `string` | Link color hue (0-360) | `--bulma-link-h` |\n| `linkS` | `string` | Link color saturation (percentage) | `--bulma-link-s` |\n| `linkL` | `string` | Link color lightness (percentage) | `--bulma-link-l` |\n| `infoH` | `string` | Info color hue (0-360) | `--bulma-info-h` |\n| `infoS` | `string` | Info color saturation (percentage) | `--bulma-info-s` |\n| `infoL` | `string` | Info color lightness (percentage) | `--bulma-info-l` |\n| `successH` | `string` | Success color hue (0-360) | `--bulma-success-h` |\n| `successS` | `string` | Success color saturation (percentage) | `--bulma-success-s` |\n| `successL` | `string` | Success color lightness (percentage) | `--bulma-success-l` |\n| `warningH` | `string` | Warning color hue (0-360) | `--bulma-warning-h` |\n| `warningS` | `string` | Warning color saturation (percentage) | `--bulma-warning-s` |\n| `warningL` | `string` | Warning color lightness (percentage) | `--bulma-warning-l` |\n| `dangerH` | `string` | Danger color hue (0-360) | `--bulma-danger-h` |\n| `dangerS` | `string` | Danger color saturation (percentage) | `--bulma-danger-s` |\n| `dangerL` | `string` | Danger color lightness (percentage) | `--bulma-danger-l` |\n\n#### Shadow Variables\n\nReachable only through `bulmaVars` — there is no individual prop, since `shadow` already names\nthe `BulmaOtherProps` `shadowless` helper class. As noted above, `--bulma-shadow` is the one\noverride that cascades into `.box`/`.card`/`.dropdown`/`.panel` shadows.\n\n| CSS Variable | Description |\n| ------------------ | ----------------------------------------------------------------------------------------------------- |\n| `--bulma-shadow-h` | Shadow color hue (0-360) |\n| `--bulma-shadow-s` | Shadow color saturation (percentage) |\n| `--bulma-shadow-l` | Shadow color lightness (percentage) |\n| `--bulma-shadow` | The full shadow value that `.box`/`.card`/`.dropdown`/`.panel` derive their own shadow variables from |\n\n#### Which Variables a Theme Can Change\n\n`bulmaVars` takes any key of the `BulmaVars` type. Write it as an object literal and your editor\nlists them as you type `'--bulma-'`. Not every one of those keys does something from a `Theme`,\nthough, and the reason is where Bulma declares the variable.\n\nA `Theme` writes its variables on its wrapper `div`, or at `:root` under `isRoot`. A component\ninherits them from there only if it declares no value of its own, because an element's own\ndeclaration always beats an inherited one.\n\n- **Global variables reach everything under the Theme.** Bulma declares the scheme, color and\n shadow variables above, and the typography, radius, spacing and timing tokens (such as\n `--bulma-family-primary` and `--bulma-radius`), on `:root`. Set them on a `Theme`, or on any\n ancestor. Some of them Bulma also declares again on one component, which keeps its own value\n while everything else takes the Theme's. That component's API page lists the variable as\n declared on it.\n- **Component variables have to be set on the component.** Where Bulma declares a variable on\n the component's own element, as it does `--bulma-card-radius`, `--bulma-tag-h` and\n `--bulma-delete-dimensions`, a value a `Theme` sets never reaches it. Those keys are still\n accepted, so existing code keeps compiling, but they change nothing, and `Theme` logs a\n warning in development naming them. Set them on the component instead, with\n `className` (a class in your own stylesheet) or the `style` prop, or at build time through\n [Sass](https://bestax.io/docs/guides/features/sass-customization).\n\nEach component's API page lists its variables under **CSS & Sass Variables** and says where Bulma\ndeclares them, so check there before reaching for a `Theme`. The\n[shadow note](https://bestax.io/docs/api/helpers/theme#css-variable-naming) above is the same rule in action: `--bulma-box-shadow` lives on\n`.box`, so the way to change box shadows from a `Theme` is the global `--bulma-shadow` they derive\nfrom.\n\nThe Theme component also supports all [Bulma helper class props](https://bestax.io/docs/api/helpers/usebulmaclasses) for styling the wrapper element."
470
+ "doc": "# Theme\n\n## Overview\n\nThe `Theme` component provides a powerful way to customize Bulma's appearance using CSS custom properties (CSS variables). It allows you to override Bulma's design tokens like colors, spacing, typography, and other visual properties either globally or locally within specific component trees. The Theme component supports both CSS variable injection via props and direct CSS variable objects.\n\n---\n\n## Import\n\n```tsx\nimport { Theme } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n### Basic Theme Customization\n\n```tsx\nfunction BasicThemeCustomization() {\n return (\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <Box p=\"4\">\n <Button color=\"primary\">Purple Primary Button</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Global Theme (Root Level)\n\n```tsx\nfunction GlobalTheme() {\n return (\n <Theme isRoot={true} primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <div>\n <Button color=\"primary\">Global Purple Theme</Button>\n <Button color=\"info\">Info Button</Button>\n </div>\n </Theme>\n );\n}\n```\n\n### Dark Mode\n\nThe `colorMode` prop sets Bulma's `data-theme` on `<html>`, flipping the light/dark scheme globally.\nUse `'system'` to follow the OS preference.\n\nA prefixed stylesheet reads Bulma's prefixed attribute instead (`data-bestax-theme` for\n`bestax-prefixed.css` and `bestax-no-helpers-prefixed.css`). Under a `ConfigProvider`\n`classPrefix`, `colorMode` writes that attribute as well as `data-theme`, so it works with the\nprefixed builds.\n\n:::warning Single-mode designs should pin the scheme\n\nOmitting `colorMode` preserves whatever `data-theme` is already set — and when nothing has set\none (the usual case), the scheme follows the visitor's OS: Bulma's text colors flip on a\ndark-mode machine even if your design is light-only, breaking contrast against any fixed custom\nbackgrounds. If you support only one mode, pin it on the `<html>` in your `index.html`\n(`<html lang=\"en\" data-theme=\"light\">`); if you support both, don't hardcode surface/text\ncolors — see [Dark Mode & Contrast](https://bestax.io/docs/guides/features/css-variables#dark-mode--contrast).\n\n:::\n\n`colorMode` writes the attribute from an effect, so a page rendered in the browser paints its\nfirst frame with whatever `<html>` already has, which is why a fixed scheme belongs in\n`index.html`. A toggle that remembers the visitor's choice has two parts. First, an inline script\nin the `<head>` of `index.html` writes the saved choice, or else the OS preference, before the\nfirst paint. Make it a plain `<script>`, not `type=\"module\"`, so it runs before the body renders.\nWith a prefixed stylesheet, have it set `data-bestax-theme` as well.\n\n```html\n<script>\n // Bulma's scheme on <html> before the first paint: the saved choice,\n // or else the OS preference. The app saves under the same key.\n (() => {\n let saved = null;\n try {\n saved = localStorage.getItem('theme');\n } catch {\n // Storage is blocked, so fall back to the OS preference.\n }\n const dark =\n saved === 'light' || saved === 'dark'\n ? saved === 'dark'\n : window.matchMedia('(prefers-color-scheme: dark)').matches;\n document.documentElement.setAttribute(\n 'data-theme',\n dark ? 'dark' : 'light'\n );\n })();\n</script>\n```\n\nSecond, the app starts `colorMode` from the attribute the script wrote, and saves each choice\nunder the key the script reads. The `bestax-theming` skill's\n[dark-mode example](https://github.com/allxsmith/bestax/blob/main/skills/bestax-theming/examples/dark-mode.tsx)\nis the same recipe, and also covers a page that has no script.\n\n```tsx\n// Pairs with an inline <head> script in index.html that writes data-theme\n// before the first paint: the choice saved under STORAGE_KEY, or else the\n// OS preference. The Theme API page's Dark Mode section has the script.\nimport { useState } from 'react';\nimport { Theme, ToggleButton } from '@allxsmith/bestax-bulma';\n\ntype ColorMode = 'light' | 'dark';\n\n// The key the index.html script reads the saved choice from.\nconst STORAGE_KEY = 'theme';\n\n// The scheme the index.html script wrote on <html>.\nfunction initialColorMode(): ColorMode {\n if (typeof document === 'undefined') return 'light';\n return document.documentElement.getAttribute('data-theme') === 'dark'\n ? 'dark'\n : 'light';\n}\n\nfunction App() {\n const [mode, setMode] = useState<ColorMode>(initialColorMode);\n\n const setDark = (dark: boolean) => {\n const next: ColorMode = dark ? 'dark' : 'light';\n setMode(next);\n try {\n localStorage.setItem(STORAGE_KEY, next);\n } catch {\n // Storage is blocked, so the choice lasts until the page reloads.\n }\n };\n\n return (\n <Theme isRoot colorMode={mode}>\n <ToggleButton pressed={mode === 'dark'} onPressedChange={setDark}>\n Dark mode\n </ToggleButton>\n </Theme>\n );\n}\n```\n\n### Local Theme (Component Level)\n\n```tsx\nfunction LocalTheme() {\n return (\n <div>\n <Button color=\"primary\">Standard Primary</Button>\n\n <Theme primaryH=\"120\" primaryS=\"100%\" primaryL=\"40%\">\n <Button color=\"primary\">Green Primary</Button>\n </Theme>\n\n <Button color=\"primary\">Standard Primary Again</Button>\n </div>\n );\n}\n```\n\n### Using CSS Variables Object\n\n```tsx\nfunction SunsetTheme() {\n const sunsetTheme = {\n '--bulma-scheme-h': '18',\n '--bulma-scheme-s': '90%',\n '--bulma-light-l': '85%',\n '--bulma-dark-l': '20%',\n '--bulma-primary-h': '25',\n '--bulma-primary-s': '85%',\n '--bulma-primary-l': '55%',\n };\n\n return (\n <Theme bulmaVars={sunsetTheme} isRoot>\n <Box p=\"4\">\n <Title>Sunset Theme</Title>\n <Button color=\"primary\">Sunset Button</Button>\n <Button color=\"info\">Info Button</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Advanced CSS Variables Usage\n\nBulma derives the body font and the control radius from these variables at `:root`, so set\ntypography and radius on an `isRoot` theme.\n\n```tsx\nfunction TypographyTheme() {\n const typographyTheme = {\n '--bulma-family-primary': '\"Helvetica Neue\", sans-serif',\n '--bulma-family-code': '\"Fira Code\", monospace',\n '--bulma-size-normal': '16px',\n '--bulma-weight-bold': '700',\n };\n\n return (\n <Theme isRoot bulmaVars={typographyTheme}>\n <Title>Custom Typography</Title>\n <SubTitle>With custom fonts and weights</SubTitle>\n </Theme>\n );\n}\n```\n\n```tsx\nfunction RadiusTheme() {\n // `radius` is the border radius helper, as on every component, so the radius scale goes through bulmaVars\n const radii = {\n '--bulma-radius-small': '6px',\n '--bulma-radius': '12px',\n '--bulma-radius-medium': '16px',\n '--bulma-radius-large': '20px',\n };\n\n return (\n <Theme isRoot bulmaVars={radii}>\n <Button color=\"primary\">Rounder Button</Button>\n </Theme>\n );\n}\n```\n\nSpacing that Bulma reads where it is used, such as the block and column gaps, works on a scoped\ntheme:\n\n```tsx\nfunction SpacingTheme() {\n const spacingTheme = {\n '--bulma-block-spacing': '2rem',\n '--bulma-column-gap': '1rem',\n };\n\n return (\n <Theme bulmaVars={spacingTheme}>\n <Columns>\n <Column>\n <Box>Wider column gap</Box>\n <Box>Wider block spacing</Box>\n </Column>\n <Column>\n <Box>Wider column gap</Box>\n </Column>\n </Columns>\n </Theme>\n );\n}\n```\n\n### Complete Color Scheme\n\n```tsx\nfunction ForestTheme() {\n const forestScheme = {\n schemeH: '150', // forest green hue\n schemeS: '50%',\n lightL: '80%',\n lightInvertL: '20%',\n darkL: '18%',\n darkInvertL: '85%',\n softL: '55%',\n boldL: '35%',\n primaryH: '160',\n primaryS: '60%',\n primaryL: '45%',\n linkH: '155',\n linkS: '65%',\n linkL: '40%',\n successH: '120',\n successS: '70%',\n successL: '45%',\n warningH: '45',\n warningS: '85%',\n warningL: '55%',\n dangerH: '355',\n dangerS: '75%',\n dangerL: '50%',\n hoverBackgroundLDelta: '4%',\n activeBackgroundLDelta: '8%',\n };\n\n return (\n <Theme {...forestScheme} isRoot>\n <Box p=\"4\">\n <Title>Forest Theme</Title>\n <Button color=\"primary\">Forest Primary</Button>\n <Button color=\"success\">Success</Button>\n <Button color=\"info\">Info</Button>\n <Button color=\"warning\">Warning</Button>\n <Button color=\"danger\">Danger</Button>\n </Box>\n </Theme>\n );\n}\n```\n\n### Theme with Styling\n\n```tsx\nfunction StyledTheme() {\n return (\n <Theme\n className=\"custom-theme-wrapper\"\n primaryH=\"45\"\n primaryS=\"100%\"\n primaryL=\"50%\"\n p=\"5\"\n m=\"3\"\n textAlign=\"centered\"\n >\n <Title>Styled Theme Container</Title>\n <Button color=\"primary\">Styled Button</Button>\n </Theme>\n );\n}\n```\n\n### Nested Themes\n\n```tsx\nfunction NestedThemes() {\n return (\n <Theme primaryH=\"210\" primaryS=\"60%\" primaryL=\"45%\" p=\"4\">\n <Title>Outer Theme (Blue)</Title>\n <Button color=\"primary\">Blue Primary</Button>\n\n <Theme primaryH=\"120\" primaryS=\"70%\" primaryL=\"40%\" p=\"3\" mt=\"4\">\n <Title size=\"4\">Inner Theme (Green)</Title>\n <Button color=\"primary\">Green Primary</Button>\n </Theme>\n\n <Button color=\"primary\" mt=\"3\">\n Blue Primary Again\n </Button>\n </Theme>\n );\n}\n```\n\n### Theme with ConfigProvider\n\n```tsx\nfunction PrefixedTheme() {\n return (\n <ConfigProvider classPrefix=\"bestax-\">\n <Theme primaryH=\"300\" primaryS=\"80%\" primaryL=\"50%\" isRoot>\n <Button color=\"primary\">Prefixed & Themed Button</Button>\n </Theme>\n </ConfigProvider>\n );\n}\n```\n\n---\n\n## Best Practices\n\n### Global vs Local Themes\n\n- Use `isRoot={true}` for application-wide themes that should affect all components\n- Use local themes (default `isRoot={false}`) for component-specific styling or theme variations\n- Local themes inherit from parent themes and can override specific variables\n- Several `isRoot` themes can be mounted at once, such as a color theme at the app root and a\n typography theme next to it. Each adds its own variables at `:root`; where two set the same\n variable, the inner or later-mounted one wins, as with nested local themes, and unmounting one\n removes only its own variables\n\n### CSS Variable Naming\n\n- All Bulma CSS variables follow the `--bulma-*` naming convention\n- Named props exist for the scheme and color variables only (like `primaryH`); every other variable, typography and radius included, goes through `bulmaVars`\n- `bulmaVars` keys are the full variable names (`'--bulma-family-primary'`), not camelCase\n- `bulmaVars` also accepts the scheme surface variables — `--bulma-scheme-main`, `--bulma-scheme-main-bis`, `--bulma-scheme-main-ter`, `--bulma-scheme-invert`, `--bulma-scheme-invert-bis`, `--bulma-scheme-invert-ter` — so overriding `--bulma-scheme-main-bis`/`-ter` re-tints every scheme-background band (e.g., `<Section bgColor=\"scheme-main-bis\">`) at once\n- `bulmaVars` also accepts `--bulma-shadow` (plus `--bulma-shadow-h`/`-s`/`-l`) — the upstream token that `.box`, `.card`, `.dropdown`, and `.panel` derive their own shadow variables from. Those selectors re-declare their derived variable (e.g. `--bulma-box-shadow`) on themselves, so setting it from an ancestor `Theme` is only inherited and never wins; overriding `--bulma-shadow` itself is the one override that cascades. There is no individual `shadow` prop for this — `shadow` already names the unrelated `shadowless` helper class — so reach it through `bulmaVars`\n\n### Performance Considerations\n\n- Prefer setting themes at higher levels in your component tree rather than deeply nested\n- CSS variables are inherited, so child themes only need to override specific variables\n\n### Color System\n\n- Bulma uses HSL (Hue, Saturation, Lightness) for its color system\n- Hue values range from 0-360 (color wheel degrees)\n- Saturation and Lightness are typically percentages (e.g., '50%')\n- The scheme variables control the base color relationships\n\n---\n\n## API Reference\n\n### Named Props and `bulmaVars`\n\n`ThemeProps` has a named, camelCase prop for each scheme and color variable, listed under\n[CSS Variable Props](https://bestax.io/docs/api/helpers/theme#css-variable-props). Each converts to its CSS variable by the same rule:\n\n```\npropName → --bulma-prop-name\n```\n\n- `primaryH` → `--bulma-primary-h`\n- `schemeS` → `--bulma-scheme-s`\n- `lightL` → `--bulma-light-l`\n- `hoverBackgroundLDelta` → `--bulma-hover-background-l-delta`\n\nEvery other variable (typography, radius, spacing, and the rest) has no named prop in\n`ThemeProps` and goes through `bulmaVars`, keyed by its full `--bulma-*` name. JavaScript code\nthat passes one as a camelCase prop (`familyPrimary`) may still render, but it is outside the\ntyped API, so move it to `bulmaVars`:\n\n```tsx\n<Theme\n isRoot\n primaryH=\"350\"\n primaryS=\"73%\"\n primaryL=\"44%\"\n bulmaVars={{\n '--bulma-radius': '2px',\n '--bulma-radius-small': '1px',\n '--bulma-family-primary': '\"IBM Plex Sans\", system-ui, sans-serif',\n '--bulma-family-code': '\"IBM Plex Mono\", ui-monospace, monospace',\n }}\n>\n <App />\n</Theme>\n```\n\nSome of those names are already helper props, so they cannot double as CSS-variable props:\n\n- `radius` is the border radius helper, as on every other component, so `<Theme radius=\"radiusless\">`\n adds `is-radiusless` to the wrapper. On a Theme it also sets `--bulma-radius` to `0`, so what\n is inside the Theme loses its radius too; under `isRoot` it writes that at `:root`, squaring\n everything on the page that takes its radius from it. The sizes (`small`, `normal`, `large`,\n `rounded`) add their `has-radius-*` class to the wrapper and set no variable, so they round\n the wrapper and leave what is inside alone; under `isRoot` there is no wrapper, so they do\n nothing and warn in development. Set any other `--bulma-radius` through\n `bulmaVars`. `<Theme radius=\"2px\" />` is a type error; in JavaScript it still sets\n `--bulma-radius`, which is how the prop used to behave, but that route is deprecated and logs\n a warning in development.\n- `shadow` is the `shadowless` helper. Set `--bulma-shadow` through `bulmaVars`.\n- `columnGap` is the column gap helper, as on every other component, so `<Theme columnGap=\"2\">`\n adds `is-column-gap-2` to the wrapper and sets no variable; under `isRoot` there is no wrapper,\n so it does nothing and warns in development. Set the columns gutter, `--bulma-column-gap`,\n through `bulmaVars`. Before the gap helpers, JavaScript could pass `columnGap` to set that\n variable. A string that is not a gap step (`columnGap=\"1rem\"`) still does, through a\n deprecated route that logs a warning in development, but a gap step does not:\n `columnGap=\"0\"`, which used to zero the gutters inside the Theme, now adds `is-column-gap-0`\n to the wrapper.\n\n### Props vs bulmaVars\n\nYou can set the scheme and color variables in two ways:\n\n1. **Named Props** (recommended where one exists):\n\n ```tsx\n <Theme primaryH=\"270\" primaryS=\"100%\" primaryL=\"50%\">\n <App />\n </Theme>\n ```\n\n2. **bulmaVars Object** (for every other variable, or when you have many to set):\n\n ```tsx\n <Theme\n bulmaVars={{\n '--bulma-primary-h': '270',\n '--bulma-primary-s': '100%',\n '--bulma-primary-l': '50%',\n '--bulma-block-spacing': '2rem',\n }}\n >\n <App />\n </Theme>\n ```\n\n3. **Combined** (props take precedence over bulmaVars):\n ```tsx\n <Theme\n primaryH=\"270\" // This takes precedence\n bulmaVars={{\n '--bulma-primary-h': '180', // This is overridden\n '--bulma-block-spacing': '2rem',\n }}\n >\n <App />\n </Theme>\n ```\n\n### TypeScript Support\n\n`bulmaVars` is typed as a partial record keyed by the union of every variable `Theme` accepts.\nWrite it as an object literal and your editor autocompletes the keys and flags a misspelled or\nunsupported one as a type error. A separately declared object only has to share one valid key\nto typecheck, so a typo in it is silently dropped; annotate it with\n`satisfies ThemeProps['bulmaVars']` to keep the check:\n\n```tsx\n<Theme\n primaryH=\"270\"\n bulmaVars={{\n '--bulma-block-spacing': '2rem', // start typing '--bulma-' to list the rest\n }}\n>\n <App />\n</Theme>\n```\n\n---\n\n## See Also\n\n- [ConfigProvider](https://bestax.io/docs/api/helpers/config) - For class prefixing and configuration\n- [useBulmaClasses](https://bestax.io/docs/api/helpers/usebulmaclasses) - For applying Bulma helper classes\n- [Bulma CSS Variables Documentation](https://bulma.io/documentation/features/css-variables/) - Official Bulma CSS variables reference\n\n---\n\n## Props\n\n| Prop | Type | Description |\n| ----------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children` | `ReactNode` | The child components to apply the theme to. |\n| `className` | `string` | Additional CSS classes for the theme wrapper. A root theme has no wrapper, so under `isRoot` it does nothing and warns in development. |\n| `isRoot` | `boolean` | When `true`, applies CSS variables globally at `:root` level. When `false` (default), applies variables only to the wrapper div. Several root themes compose; an inner or later-mounted one wins a variable they share. A root theme renders no wrapper, so `className` and the helper props, such as `m` or `shadow`, do nothing on it and warn in development. |\n| `colorMode` | `'light' \\| 'dark' \\| 'system'` | Sets Bulma's light/dark scheme by writing the `data-theme` attribute on `<html>`, plus `data-<prefix>theme` inside a `ConfigProvider` with a `classPrefix`. If the prefix cannot form an attribute name, `'light'` and `'dark'` write only `data-theme` and warn in development. Always global (even on a scoped `Theme`). `'system'` removes them so Bulma follows the OS `prefers-color-scheme`. Omit to leave the current setting untouched. |\n| `bulmaVars` | `ThemeProps['bulmaVars']` | Object mapping Bulma CSS variable names to string values (e.g., `{'--bulma-primary-h': '210'}`). Keys are limited to the `BulmaVars` type; anything else is not applied. A variable Bulma declares on the component itself changes nothing from here and warns in development; see [Which Variables a Theme Can Change](https://bestax.io/docs/api/helpers/theme#which-variables-a-theme-can-change). |\n| `columnGap` | [`BulmaGapStep`](https://bestax.io/docs/api/helpers/valid-values) | The column gap helper: a gap step adds `is-column-gap-<step>` to the wrapper div and sets no variable, so under `isRoot` it does nothing and warns in development. Set `--bulma-column-gap` through `bulmaVars`. Any other string still sets `--bulma-column-gap`, a deprecated route that warns in development. |\n| `radius` | `'radiusless' \\| 'small' \\| 'normal' \\| 'large' \\| 'rounded'` | The border radius helper. `radiusless` adds `is-radiusless` to the wrapper div and sets `--bulma-radius` to `0`, at `:root` under `isRoot`. The sizes add their `has-radius-*` class to the wrapper and set no variable, so under `isRoot` they do nothing and warn in development. Any other string still sets `--bulma-radius`, a deprecated route that warns in development. |\n\n### CSS Variable Props\n\nThe Theme component accepts individual CSS variable props that correspond to Bulma's design tokens. These props are automatically converted to their corresponding CSS custom properties (e.g., `primaryH` → `--bulma-primary-h`).\n\n#### Scheme Variables\n\n| Prop | Type | Description | CSS Variable |\n| ------------------------ | -------- | ------------------------------------------------- | ----------------------------------- |\n| `schemeH` | `string` | Base hue for the color scheme (0-360) | `--bulma-scheme-h` |\n| `schemeS` | `string` | Base saturation for the color scheme (percentage) | `--bulma-scheme-s` |\n| `lightL` | `string` | Lightness value for light backgrounds | `--bulma-light-l` |\n| `lightInvertL` | `string` | Inverted lightness for light backgrounds | `--bulma-light-invert-l` |\n| `darkL` | `string` | Lightness value for dark backgrounds | `--bulma-dark-l` |\n| `darkInvertL` | `string` | Inverted lightness for dark backgrounds | `--bulma-dark-invert-l` |\n| `softL` | `string` | Lightness value for soft colors | `--bulma-soft-l` |\n| `boldL` | `string` | Lightness value for bold colors | `--bulma-bold-l` |\n| `softInvertL` | `string` | Inverted lightness for soft colors | `--bulma-soft-invert-l` |\n| `boldInvertL` | `string` | Inverted lightness for bold colors | `--bulma-bold-invert-l` |\n| `hoverBackgroundLDelta` | `string` | Lightness delta for hover background states | `--bulma-hover-background-l-delta` |\n| `activeBackgroundLDelta` | `string` | Lightness delta for active background states | `--bulma-active-background-l-delta` |\n| `hoverBorderLDelta` | `string` | Lightness delta for hover border states | `--bulma-hover-border-l-delta` |\n| `activeBorderLDelta` | `string` | Lightness delta for active border states | `--bulma-active-border-l-delta` |\n| `hoverColorLDelta` | `string` | Lightness delta for hover text color states | `--bulma-hover-color-l-delta` |\n| `activeColorLDelta` | `string` | Lightness delta for active text color states | `--bulma-active-color-l-delta` |\n| `hoverShadowADelta` | `string` | Alpha delta for hover shadow states | `--bulma-hover-shadow-a-delta` |\n| `activeShadowADelta` | `string` | Alpha delta for active shadow states | `--bulma-active-shadow-a-delta` |\n\n#### Color Variables\n\n| Prop | Type | Description | CSS Variable |\n| ---------- | -------- | ------------------------------------- | ------------------- |\n| `primaryH` | `string` | Primary color hue (0-360) | `--bulma-primary-h` |\n| `primaryS` | `string` | Primary color saturation (percentage) | `--bulma-primary-s` |\n| `primaryL` | `string` | Primary color lightness (percentage) | `--bulma-primary-l` |\n| `linkH` | `string` | Link color hue (0-360) | `--bulma-link-h` |\n| `linkS` | `string` | Link color saturation (percentage) | `--bulma-link-s` |\n| `linkL` | `string` | Link color lightness (percentage) | `--bulma-link-l` |\n| `infoH` | `string` | Info color hue (0-360) | `--bulma-info-h` |\n| `infoS` | `string` | Info color saturation (percentage) | `--bulma-info-s` |\n| `infoL` | `string` | Info color lightness (percentage) | `--bulma-info-l` |\n| `successH` | `string` | Success color hue (0-360) | `--bulma-success-h` |\n| `successS` | `string` | Success color saturation (percentage) | `--bulma-success-s` |\n| `successL` | `string` | Success color lightness (percentage) | `--bulma-success-l` |\n| `warningH` | `string` | Warning color hue (0-360) | `--bulma-warning-h` |\n| `warningS` | `string` | Warning color saturation (percentage) | `--bulma-warning-s` |\n| `warningL` | `string` | Warning color lightness (percentage) | `--bulma-warning-l` |\n| `dangerH` | `string` | Danger color hue (0-360) | `--bulma-danger-h` |\n| `dangerS` | `string` | Danger color saturation (percentage) | `--bulma-danger-s` |\n| `dangerL` | `string` | Danger color lightness (percentage) | `--bulma-danger-l` |\n\n#### Shadow Variables\n\nReachable only through `bulmaVars` — there is no individual prop, since `shadow` already names\nthe `BulmaOtherProps` `shadowless` helper class. As noted above, `--bulma-shadow` is the one\noverride that cascades into `.box`/`.card`/`.dropdown`/`.panel` shadows.\n\n| CSS Variable | Description |\n| ------------------ | ----------------------------------------------------------------------------------------------------- |\n| `--bulma-shadow-h` | Shadow color hue (0-360) |\n| `--bulma-shadow-s` | Shadow color saturation (percentage) |\n| `--bulma-shadow-l` | Shadow color lightness (percentage) |\n| `--bulma-shadow` | The full shadow value that `.box`/`.card`/`.dropdown`/`.panel` derive their own shadow variables from |\n\n#### Which Variables a Theme Can Change\n\n`bulmaVars` takes any key of the `BulmaVars` type. Write it as an object literal and your editor\nlists them as you type `'--bulma-'`. Not every one of those keys does something from a `Theme`,\nthough, and the reason is where Bulma declares the variable.\n\nA `Theme` writes its variables on its wrapper `div`, or at `:root` under `isRoot`. A component\ninherits them from there only if it declares no value of its own, because an element's own\ndeclaration always beats an inherited one.\n\n- **Global variables reach everything under the Theme.** Bulma declares the scheme, color and\n shadow variables above, and the typography, radius, spacing and timing tokens (such as\n `--bulma-family-primary` and `--bulma-radius`), on `:root`. Set them on a `Theme`, or on any\n ancestor. Some of them Bulma also declares again on one component, which keeps its own value\n while everything else takes the Theme's. That component's API page lists the variable as\n declared on it.\n- **Component variables have to be set on the component.** Where Bulma declares a variable on\n the component's own element, as it does `--bulma-card-radius`, `--bulma-tag-h` and\n `--bulma-delete-dimensions`, a value a `Theme` sets never reaches it. Those keys are still\n accepted, so existing code keeps compiling, but they change nothing, and `Theme` logs a\n warning in development naming them. Set them on the component instead, with\n `className` (a class in your own stylesheet) or the `style` prop, or at build time through\n [Sass](https://bestax.io/docs/guides/features/sass-customization).\n\nEach component's API page lists its variables under **CSS & Sass Variables** and says where Bulma\ndeclares them, so check there before reaching for a `Theme`. The\n[shadow note](https://bestax.io/docs/api/helpers/theme#css-variable-naming) above is the same rule in action: `--bulma-box-shadow` lives on\n`.box`, so the way to change box shadows from a `Theme` is the global `--bulma-shadow` they derive\nfrom.\n\nThe Theme component also supports all [Bulma helper class props](https://bestax.io/docs/api/helpers/usebulmaclasses) for styling the wrapper element."
471
471
  }
@@ -57,6 +57,10 @@ Run these steps in order. Don't hand-convert what the codemod converts automatic
57
57
  entry, and port each script's work into the component. Under `--css bestax` or `--css bulma`
58
58
  the module imports the stylesheet in place of the page's Bulma `<link>`.
59
59
 
60
+ A file that fails to parse or transform is marked `✖`, listed under `failed` in the report
61
+ and left as it was, and the run exits 1 while every other file still converts. Fix that file
62
+ and run the codemod on it again. TODOs alone leave the exit code at 0.
63
+
60
64
  2. **Apply it** (same command without `--dry`), then run the project's formatter — the
61
65
  codemod preserves surrounding formatting but doesn't prettify what it rewrites.
62
66
  Besides the components, it also migrates **stylesheets** (CSS imports →
@@ -2,7 +2,8 @@
2
2
 
3
3
  Which class becomes which prop, per component, then the helper classes every component takes.
4
4
  A class that becomes no prop stays in `className`. So does a second class for a prop already
5
- written (`is-small is-large` keeps `is-large`), so the element still renders both.
5
+ written (`is-small is-large` keeps `is-large`), so the element still renders both. A second
6
+ color gets a `modifier:<root>` TODO (`modifier:button`), since only one of the two may show.
6
7
 
7
8
  `{viewport}` is one of `mobile`, `tablet`, `desktop`, `widescreen`, `fullhd` unless a row says
8
9
  otherwise, and the prop takes the matching suffix: `is-6-tablet` → `sizeTablet="6"`.
@@ -74,7 +75,8 @@ bestax picks the heading from `size` (`size="3"` renders an `<h3>`) unless `as="
74
75
  `<h2>`, `is-4` stays a class and the element becomes `<Title as="h2" className="is-4">`, which
75
76
  renders the same `<h2>`. To write it as a prop, `textSize="4"` keeps the `<h2>` and sizes it the
76
77
  same: it renders Bulma's `is-size-4`, which has the font size `is-4` gives a title or subtitle,
77
- and as a Bulma helper it is `!important`, so check a title the page's own CSS sizes.
78
+ and as a Bulma helper it is `!important`, so check a title the page's own CSS sizes. The
79
+ codemod's `modifier:title` (or `modifier:subtitle`) TODO on the element says the same.
78
80
 
79
81
  ### Other components
80
82
 
@@ -406,7 +406,7 @@ reason), or beside a class for the same prop (`p-2`, before it or after), which
406
406
  since it's what the page renders today. Write `m-0` or `p-0` in its place, or drop it if the
407
407
  spacing it once removed no longer matters.
408
408
 
409
- ## A class that styles nothing where it is: `modifier:<root>`
409
+ ## A modifier class that needs a look: `modifier:<root>`
410
410
 
411
411
  `modifier:image`: an `.image` size or ratio (`is-64x64`, `is-square`, `is-4by3`) on an element
412
412
  without `.image`. Bulma styles them only on the element that carries `.image`, so this one
@@ -416,6 +416,24 @@ them), re-run and it can convert; on any other tag it stays markup, and the TODO
416
416
  Otherwise drop the class. A computed `className` the codemod can't read whole gets no such TODO, since a class it
417
417
  can't see may be the `.image`.
418
418
 
419
+ `modifier:button` (and `modifier:hero`, `modifier:tag`, `modifier:notification`,
420
+ `modifier:progress`, `modifier:navbar`): a second color on an element that converted
421
+ (`button is-black is-primary`). The component takes one `color`, the first color class on the
422
+ element, and the other stays in `className`, which renders the same. Two colors on one element
423
+ look the way Bulma's stylesheet settles them, not the order they're written in, so one of
424
+ them may be doing nothing. Look at what the page shows, write the color you want as `color`,
425
+ and drop the other class. A color a `clsx` or `classnames` condition adds gets no TODO, since
426
+ that is a state the page asks for.
427
+
428
+ `modifier:title`, `modifier:subtitle`: a heading size on a heading it doesn't name
429
+ (`<h1 className="title is-3">`). bestax picks the heading from `size` (`size="3"` renders an
430
+ `<h3>`), so the size stays in `className` and the element becomes `<Title className="is-3">`,
431
+ the same `<h1>`. To write it as a prop, as the MCP server's `lookup_bulma_classes` says,
432
+ `textSize="3"` in its place keeps the `<h1>` and sizes it the same. It renders Bulma's
433
+ `is-size-3`, which is `!important`, so check a title the page's own CSS sizes. There is no TODO
434
+ when an `is-size-*` beside it writes `textSize` already, or when the element has two size
435
+ classes.
436
+
419
437
  ## A whole file it left alone
420
438
 
421
439
  These come only when the file has an element the codemod would convert (a computed className
@@ -572,9 +590,17 @@ neither, so load what the app still needs once for the whole app, from its entry
572
590
  - **`html-asset:stylesheet`**: any other stylesheet. The page's own, beside it (in its folder or
573
591
  one below), is imported once for the app in the order the page loads it, so its rules still win
574
592
  over Bulma's where they did (a module beside the page imports `css/styles.css` as
575
- `./css/styles.css`), or its rules move into the app's stylesheet. For one that isn't in the
576
- page's folder, the TODO says only that: the page's `<base href>`, if it has one, may have loaded
577
- it from somewhere else, so find the one it meant, or drop it.
593
+ `./css/styles.css`), or its rules move into the app's stylesheet. A relative URL in it (a
594
+ `url()`, or an `@import` written as a string) resolves against the stylesheet's own folder, not
595
+ the page's, so the TODO names each one it finds: `url('../media/background.webp')` in
596
+ `css/styles.css` points at `media/background.webp` beside the page. Imported where it is, the
597
+ bundler still resolves them. Moved into the app's stylesheet (`src/index.css`), each needs
598
+ rewriting against that file's folder, or copy the files into the app's `public/` folder and
599
+ point at them from the site root (`url('/media/background.webp')`), which the TODO spells out
600
+ for each one inside the page's folder. An `image-set()` that names a file as a bare string
601
+ isn't read, so check those by hand. For a stylesheet that isn't in the page's folder, the TODO
602
+ says only that: the page's `<base href>`, if it has one, may have loaded it from somewhere
603
+ else, so find the one it meant, or drop it.
578
604
  - **`html-asset:script`**: any other script. The component runs none: load a library the app still
579
605
  needs in `index.html` (or install it), and port what the page's own scripts did into the
580
606
  component. A script of the page's own, beside it, is read the way an inline script is, so one
@@ -796,9 +822,15 @@ than `this`. A `<body onload>` becomes an effect.
796
822
 
797
823
  The HTML has an error the browser repaired (`html-parse:duplicate-attribute`,
798
824
  `html-parse:end-tag-without-matching-open-element`), reported once for each kind, with how many
799
- more there are. The module follows the DOM the browser builds from it, which may not be what the
800
- markup meant, so check that part of the page. A code too long for a rule id is reported as the bare
801
- `html-parse`.
825
+ more there are, except a duplicate attribute (below). The module follows the DOM the browser
826
+ builds from it, which may not be what the markup meant, so check that part of the page. A code too
827
+ long for a rule id is reported as the bare `html-parse`.
828
+
829
+ A duplicate attribute on an element gets an entry of its own, naming the attribute, the copy the
830
+ element keeps (the first) and the one the browser drops (`placeholder="Email"` kept,
831
+ `placeholder="Your email"` dropped). The module has the kept one, so if the page meant the other,
832
+ change the value there. One the codemod can't name, such as one on an end tag, is counted with the
833
+ rest.
802
834
 
803
835
  ### The page itself: `html-file:<case>`
804
836
 
@@ -322,6 +322,15 @@ come from the `itemsToShow*` props, and `--bulma-carousel-items-in-view` is the
322
322
  viewport picks. The props also decide how far a step moves, so set the count with the props
323
323
  rather than with these variables.
324
324
 
325
+ The `--bulma-carousel-overlay-*` variables color the arrows, and indicators over the slides, only
326
+ while the overlay look is on: `overlayControls`, which follows `indicatorInside` unless set. The look
327
+ also puts a dark gradient band behind indicators over the slides (a `background`, not a variable),
328
+ and while it's on the arrows ignore `arrowColor`. For indicators over light slides, such as white
329
+ cards, use `<Carousel indicatorInside overlayControls={false}>`: plain colors and no band.
330
+ `overlayControls` alone gives the arrows the overlay look with the indicators below the slides. The
331
+ overlay colors default to light ones (`--bulma-white`) in either color scheme, since they sit on a
332
+ dark tint.
333
+
325
334
  ### Checkbox
326
335
 
327
336
  `--bulma-checkbox-active-color`, `--bulma-checkbox-background`, `--bulma-checkbox-border-color`,
@@ -341,6 +350,12 @@ that selector, so leave `color` unset when a class sets the fill. The unchecked
341
350
  the box holds the 3:1 WCAG asks of a control's boundary; keep a replacement at 3:1 against what
342
351
  the form sits on.
343
352
 
353
+ `--bulma-checkbox-size` (default `1.25em`) can't take the box below 24px: the stylesheet gives the
354
+ `.check` box a `min-width` and `min-height` of 24px, WCAG 2.2's minimum target size, so with
355
+ Bulma's default font sizes the box is 24px at the default and `small` sizes. For a smaller box, set the size and also
356
+ `min-width: 0; min-height: 0` on `.styled-checkbox.my-box .check` (mind the class prefix), and keep
357
+ the label text inside the `Checkbox`, since the whole label is the click target.
358
+
344
359
  ### Collapse
345
360
 
346
361
  `--bulma-collapse-border-color`, `--bulma-collapse-group-gap`, `--bulma-collapse-header-weight`,
package/data/skills.json CHANGED
@@ -108,12 +108,12 @@
108
108
  {
109
109
  "id": "bulma-classes-prop-map",
110
110
  "file": "references/bulma-classes/prop-map.md",
111
- "bytes": 38917
111
+ "bytes": 39110
112
112
  },
113
113
  {
114
114
  "id": "bulma-classes-unmappables",
115
115
  "file": "references/bulma-classes/unmappables.md",
116
- "bytes": 56988
116
+ "bytes": 59446
117
117
  },
118
118
  {
119
119
  "id": "css-migration",
@@ -176,7 +176,7 @@
176
176
  {
177
177
  "id": "css-variables",
178
178
  "file": "references/css-variables.md",
179
- "bytes": 48297
179
+ "bytes": 49488
180
180
  },
181
181
  {
182
182
  "id": "themeable-components",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bestax-mcp",
3
- "version": "1.18.4",
3
+ "version": "1.18.6",
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.1",
44
44
  "zod": "^4.6.7",
45
- "@allxsmith/bestax-bulma": "^5.43.1"
45
+ "@allxsmith/bestax-bulma": "^5.45.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@jest/globals": "^30.0.0",