bestax-mcp 1.18.1 → 1.18.3
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 +20 -2
- package/data/components/Carousel.json +6 -6
- package/data/components/Checkbox.json +7 -3
- package/data/components/Checkboxes.json +1 -1
- package/data/components/Field.json +2 -2
- package/data/components/PinInput.json +341 -0
- package/data/components/Radio.json +3 -3
- package/data/components/Radios.json +1 -1
- package/data/skills/bestax-custom-component/references/component-catalog.md +2 -1
- package/data/skills/bestax-form/SKILL.md +22 -8
- package/data/skills/bestax-form/references/api.md +1 -0
- package/data/skills/bestax-form/references/patterns.md +14 -0
- package/data/skills/bestax-migrate/references/bulma-classes/unmappables.md +80 -22
- package/data/skills/bestax-optimize/references/modular-build.md +6 -2
- package/data/skills/bestax-theming/references/css-variables.md +30 -0
- package/data/skills/bestax-theming/references/themeable-components.md +1 -0
- package/data/skills.json +8 -8
- package/package.json +6 -6
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.
|
|
5
|
+
"version": "5.43.0"
|
|
6
6
|
},
|
|
7
7
|
"docsBase": "https://bestax.io/docs",
|
|
8
8
|
"categories": [
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"File",
|
|
98
98
|
"Input",
|
|
99
99
|
"Numberinput",
|
|
100
|
+
"PinInput",
|
|
100
101
|
"Radio",
|
|
101
102
|
"Radios",
|
|
102
103
|
"Rate",
|
|
@@ -277,7 +278,7 @@
|
|
|
277
278
|
"import": "import { Checkboxes, Checkbox } from '@allxsmith/bestax-bulma';",
|
|
278
279
|
"compound": false,
|
|
279
280
|
"propCount": 8,
|
|
280
|
-
"exampleCount":
|
|
281
|
+
"exampleCount": 7
|
|
281
282
|
},
|
|
282
283
|
{
|
|
283
284
|
"name": "Checkboxes",
|
|
@@ -840,6 +841,17 @@
|
|
|
840
841
|
"propCount": 4,
|
|
841
842
|
"exampleCount": 10
|
|
842
843
|
},
|
|
844
|
+
{
|
|
845
|
+
"name": "PinInput",
|
|
846
|
+
"kind": "component",
|
|
847
|
+
"category": "form",
|
|
848
|
+
"purpose": "The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each.",
|
|
849
|
+
"slug": "form/pininput",
|
|
850
|
+
"import": "import { PinInput } from '@allxsmith/bestax-bulma';",
|
|
851
|
+
"compound": false,
|
|
852
|
+
"propCount": 19,
|
|
853
|
+
"exampleCount": 12
|
|
854
|
+
},
|
|
843
855
|
{
|
|
844
856
|
"name": "Popover",
|
|
845
857
|
"kind": "component",
|
|
@@ -2313,6 +2325,12 @@
|
|
|
2313
2325
|
"DateTimeInput",
|
|
2314
2326
|
"TimeInput"
|
|
2315
2327
|
],
|
|
2328
|
+
"--bulma-pin-input-box-width": ["PinInput"],
|
|
2329
|
+
"--bulma-pin-input-caret-blink-duration": ["PinInput"],
|
|
2330
|
+
"--bulma-pin-input-caret-color": ["PinInput"],
|
|
2331
|
+
"--bulma-pin-input-caret-height": ["PinInput"],
|
|
2332
|
+
"--bulma-pin-input-caret-width": ["PinInput"],
|
|
2333
|
+
"--bulma-pin-input-gap": ["PinInput"],
|
|
2316
2334
|
"--bulma-popover-animation-duration": ["Popover"],
|
|
2317
2335
|
"--bulma-popover-background": ["Popover"],
|
|
2318
2336
|
"--bulma-popover-body-padding": ["Popover"],
|
|
@@ -380,19 +380,19 @@
|
|
|
380
380
|
"deprecated": false
|
|
381
381
|
},
|
|
382
382
|
{
|
|
383
|
-
"name": "
|
|
384
|
-
"type": "
|
|
383
|
+
"name": "children",
|
|
384
|
+
"type": "React.ReactNode",
|
|
385
385
|
"default": null,
|
|
386
|
-
"description": "
|
|
386
|
+
"description": "The slide's content. An image in it is held to the slide's width at its own height, unless something sizes the image itself: a ratio `Image` fills its frame, and an `Avatar`'s photo its circle, as they do outside a carousel.",
|
|
387
387
|
"required": false,
|
|
388
388
|
"inherited": false,
|
|
389
389
|
"deprecated": false
|
|
390
390
|
},
|
|
391
391
|
{
|
|
392
|
-
"name": "
|
|
393
|
-
"type": "
|
|
392
|
+
"name": "className",
|
|
393
|
+
"type": "string",
|
|
394
394
|
"default": null,
|
|
395
|
-
"description": "
|
|
395
|
+
"description": "Additional CSS classes.",
|
|
396
396
|
"required": false,
|
|
397
397
|
"inherited": false,
|
|
398
398
|
"deprecated": false
|
|
@@ -28,9 +28,13 @@
|
|
|
28
28
|
{
|
|
29
29
|
"title": "Valid Colors and Sizes",
|
|
30
30
|
"code": "import { Checkbox, Checkboxes } from '@allxsmith/bestax-bulma';\nimport { checkboxColors, checkboxSizes } from '@allxsmith/bestax-bulma';\n\nfunction example() {\n return (\n <>\n <Checkboxes>\n {checkboxColors.map(color => (\n <Checkbox key={color} color={color} defaultChecked>\n {color}\n </Checkbox>\n ))}\n </Checkboxes>\n <Checkboxes>\n {checkboxSizes.map(size => (\n <Checkbox key={size} size={size} defaultChecked>\n {size}\n </Checkbox>\n ))}\n </Checkboxes>\n </>\n );\n}"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"title": "Theming the Box",
|
|
34
|
+
"code": "<Checkbox className=\"ink-checkbox\" defaultChecked>\n Remember me\n</Checkbox>"
|
|
31
35
|
}
|
|
32
36
|
],
|
|
33
|
-
"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.",
|
|
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.",
|
|
34
38
|
"related": ["Checkboxes", "Field", "useBulmaClasses"],
|
|
35
39
|
"storybook": "https://bestax.io/storybook/?path=/story/form-checkbox--default",
|
|
36
40
|
"summary": "The `Checkbox` component provides a Bulma-styled checkbox input.",
|
|
@@ -93,7 +97,7 @@
|
|
|
93
97
|
"name": "className",
|
|
94
98
|
"type": "string",
|
|
95
99
|
"default": null,
|
|
96
|
-
"description": "Additional CSS classes to
|
|
100
|
+
"description": "Additional CSS classes, on the outer `<label>`. That's the element that declares the `--bulma-checkbox-*` variables, so a class is how to set one: `style` and the other input attributes go to the `<input>`. Name `.styled-checkbox` beside your class in the selector, so it beats the label's own declaration whatever order the stylesheets load in. A `color` prop declares the fills again on `.styled-checkbox.is-<color>`, which ties that selector, so leave `color` unset when a class sets them.",
|
|
97
101
|
"required": false,
|
|
98
102
|
"inherited": false,
|
|
99
103
|
"deprecated": false
|
|
@@ -144,7 +148,7 @@
|
|
|
144
148
|
{
|
|
145
149
|
"css": "--bulma-checkbox-border-color",
|
|
146
150
|
"sass": "$checkbox-border-color",
|
|
147
|
-
"default": "var(--bulma-
|
|
151
|
+
"default": "var(--bulma-text-weak)",
|
|
148
152
|
"scope": "root"
|
|
149
153
|
},
|
|
150
154
|
{
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
"name": "className",
|
|
217
217
|
"type": "string",
|
|
218
218
|
"default": null,
|
|
219
|
-
"description": "Additional CSS classes to
|
|
219
|
+
"description": "Additional CSS classes, on the outer `<label>`. That's the element that declares the `--bulma-checkbox-*` variables, so a class is how to set one: `style` and the other input attributes go to the `<input>`. Name `.styled-checkbox` beside your class in the selector, so it beats the label's own declaration whatever order the stylesheets load in. A `color` prop declares the fills again on `.styled-checkbox.is-<color>`, which ties that selector, so leave `color` unset when a class sets them.",
|
|
220
220
|
"required": false,
|
|
221
221
|
"inherited": false,
|
|
222
222
|
"deprecated": false
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"code": "<Field horizontal>\n <Field.Label size=\"normal\">\n <label className=\"label\" htmlFor=\"compound-name\">\n Name\n </label>\n </Field.Label>\n <Field.Body>\n <Field.Control>\n <Input id=\"compound-name\" placeholder=\"Jane Doe\" />\n </Field.Control>\n </Field.Body>\n</Field>"
|
|
99
99
|
}
|
|
100
100
|
],
|
|
101
|
-
"accessibility": "- For a single control, prefer the control's own `label` prop, with `horizontal` for a side-by-side row. It renders this `Field` and its label and wires the `for` itself, to your `id` when you pass one: `<Input label=\"Email\" id=\"email\" horizontal />`.\n- The `label` prop names the one control the Field holds. A composed `InputBase`, `SelectBase`, `TextAreaBase`, `DateInputBase`, `TimeInputBase`, or `DateTimeInputBase`, or a bestax input that renders a single input of its own (`Input`, `Select`, `TextArea`, `Numberinput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`, `Combobox`, `Taginput`, `File`), adopts a generated id the label's `for` points at, so clicking the label focuses the control and assistive technology announces it. An `inline` picker renders no input, so it takes nothing.\n- A control with an `id` of its own keeps it and never takes the generated one, so `<Field label=\"Email\"><Input id=\"email\" /></Field>` renders a label that names nothing. Leave the `id` off, or pass the same value as `labelProps={{ htmlFor: 'email' }}`. Pass `labelProps={{ htmlFor: undefined }}` to opt out of the association entirely.\n- A range `Slider` takes that id on its low thumb, and its thumbs also point `aria-labelledby` at the label (at `labelProps.id` when you set one), so under a label of \"Price range\" they announce as \"Price range Minimum value\" and \"Price range Maximum value\". An `ariaLabel` entry names its thumb instead, and an `aria-label` or `aria-labelledby` on the Slider takes the label's place in both names. A label you wire by hand with `labelProps={{ htmlFor }}` gets no generated id, so it reaches the thumbs' names only when you give it an `id` there too, as in `labelProps={{ htmlFor: 'price', id: 'price-label' }}`.\n- An `Autocomplete`, a `Combobox` or a `Taginput` points its open list's `aria-labelledby` at the label the same way, so under a label of \"Country\" the list announces as \"Country\" rather than its fallback name, \"Suggestions\". A label you wire by hand needs an `id` in `labelProps` for this too, as in `labelProps={{ htmlFor: 'country', id: 'country-label' }}`.\n- A group (`Radios`, `Checkboxes`, `Rate`, `DateRangeInput`, or a composed `DateRangeInputBase`, `inline` or not) can't take a `for`, so the label gets an id (yours from `labelProps.id`, otherwise a generated one) and the group points `aria-labelledby` at it. An `aria-label` or `aria-labelledby` you set on the group wins over the Field's label, as it does over a group's own `label`.\n- Association is skipped for `grouped`/`hasAddons` fields (they hold several controls), and a `Field` nested inside a labeled one starts its own scope. A horizontal Field wraps its children in a `Field.Body` of its own and keeps the association, but the pattern with an inner `Field` in that body needs it wired by hand. A label you wire by hand names the control its `for` points at in a `grouped` or `hasAddons` field too, and with an `id` in `labelProps` it names a range `Slider`'s thumbs and an `Autocomplete`'s, a `Combobox`'s or a `Taginput`'s list there as well. No group points at it on its own.\n- An input in a `hasAddons` row still needs a name of its own. For a visible label, put it on an outer `Field` and wire it by hand, as in [Addons (With a Label)](https://bestax.io/docs/api/form/field#addons-with-a-label). Without one, give the input an `aria-label`, as the other addon examples do. A placeholder disappears once the reader starts typing, so it doesn't stand in for either.\n- Two controls in one plain labeled Field would both adopt its id. Give each an `id` of its own and label it individually.\n- Once a labeled Field's content has mounted, its label keeps the generated `for` only while one of the inputs above holds the id, and drops it over anything else: a group, a `Checkbox`, `Radio` or `Switch`, an `inline` picker, an inner `Field`, an input with an `id` of its own, markup of your own, or nothing at all. A server render, and the first render while hydrating, still write the `for`, so hydration matches, and an input that mounts later puts it back. A `for` you set with `labelProps={{ htmlFor }}` always stays. `Checkbox`, `Radio` and `Switch` take nothing from the label on their own: each is named by its own children, so put the text there, and the label names one only when you wire it as you would any control with an `id` of its own, `labelProps={{ htmlFor }}` plus that control's `id`. Where you point content at the label by hand, as with a group in an inner `Field`, `labelProps={{ id }}` is enough in the browser, and `labelProps={{ id, htmlFor: undefined }}` keeps the `for` out of server-rendered HTML too.\n- Grouped/horizontal layouts use Bulma’s grid for layout.",
|
|
101
|
+
"accessibility": "- For a single control, prefer the control's own `label` prop, with `horizontal` for a side-by-side row. It renders this `Field` and its label and wires the `for` itself, to your `id` when you pass one: `<Input label=\"Email\" id=\"email\" horizontal />`.\n- The `label` prop names the one control the Field holds. A composed `InputBase`, `SelectBase`, `TextAreaBase`, `DateInputBase`, `TimeInputBase`, or `DateTimeInputBase`, or a bestax input that renders a single input of its own (`Input`, `Select`, `TextArea`, `Numberinput`, `PinInput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`, `Combobox`, `Taginput`, `File`), adopts a generated id the label's `for` points at, so clicking the label focuses the control and assistive technology announces it. An `inline` picker renders no input, so it takes nothing.\n- A control with an `id` of its own keeps it and never takes the generated one, so `<Field label=\"Email\"><Input id=\"email\" /></Field>` renders a label that names nothing. Leave the `id` off, or pass the same value as `labelProps={{ htmlFor: 'email' }}`. Pass `labelProps={{ htmlFor: undefined }}` to opt out of the association entirely.\n- A range `Slider` takes that id on its low thumb, and its thumbs also point `aria-labelledby` at the label (at `labelProps.id` when you set one), so under a label of \"Price range\" they announce as \"Price range Minimum value\" and \"Price range Maximum value\". An `ariaLabel` entry names its thumb instead, and an `aria-label` or `aria-labelledby` on the Slider takes the label's place in both names. A label you wire by hand with `labelProps={{ htmlFor }}` gets no generated id, so it reaches the thumbs' names only when you give it an `id` there too, as in `labelProps={{ htmlFor: 'price', id: 'price-label' }}`.\n- An `Autocomplete`, a `Combobox` or a `Taginput` points its open list's `aria-labelledby` at the label the same way, so under a label of \"Country\" the list announces as \"Country\" rather than its fallback name, \"Suggestions\". A label you wire by hand needs an `id` in `labelProps` for this too, as in `labelProps={{ htmlFor: 'country', id: 'country-label' }}`.\n- A group (`Radios`, `Checkboxes`, `Rate`, `DateRangeInput`, or a composed `DateRangeInputBase`, `inline` or not) can't take a `for`, so the label gets an id (yours from `labelProps.id`, otherwise a generated one) and the group points `aria-labelledby` at it. An `aria-label` or `aria-labelledby` you set on the group wins over the Field's label, as it does over a group's own `label`.\n- Association is skipped for `grouped`/`hasAddons` fields (they hold several controls), and a `Field` nested inside a labeled one starts its own scope. A horizontal Field wraps its children in a `Field.Body` of its own and keeps the association, but the pattern with an inner `Field` in that body needs it wired by hand. A label you wire by hand names the control its `for` points at in a `grouped` or `hasAddons` field too, and with an `id` in `labelProps` it names a range `Slider`'s thumbs and an `Autocomplete`'s, a `Combobox`'s or a `Taginput`'s list there as well. No group points at it on its own.\n- An input in a `hasAddons` row still needs a name of its own. For a visible label, put it on an outer `Field` and wire it by hand, as in [Addons (With a Label)](https://bestax.io/docs/api/form/field#addons-with-a-label). Without one, give the input an `aria-label`, as the other addon examples do. A placeholder disappears once the reader starts typing, so it doesn't stand in for either.\n- Two controls in one plain labeled Field would both adopt its id. Give each an `id` of its own and label it individually.\n- Once a labeled Field's content has mounted, its label keeps the generated `for` only while one of the inputs above holds the id, and drops it over anything else: a group, a `Checkbox`, `Radio` or `Switch`, an `inline` picker, an inner `Field`, an input with an `id` of its own, markup of your own, or nothing at all. A server render, and the first render while hydrating, still write the `for`, so hydration matches, and an input that mounts later puts it back. A `for` you set with `labelProps={{ htmlFor }}` always stays. `Checkbox`, `Radio` and `Switch` take nothing from the label on their own: each is named by its own children, so put the text there, and the label names one only when you wire it as you would any control with an `id` of its own, `labelProps={{ htmlFor }}` plus that control's `id`. Where you point content at the label by hand, as with a group in an inner `Field`, `labelProps={{ id }}` is enough in the browser, and `labelProps={{ id, htmlFor: undefined }}` keeps the `for` out of server-rendered HTML too.\n- Grouped/horizontal layouts use Bulma’s grid for layout.",
|
|
102
102
|
"related": [
|
|
103
103
|
"Checkbox",
|
|
104
104
|
"Checkboxes",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"name": "label",
|
|
162
162
|
"type": "React.ReactNode",
|
|
163
163
|
"default": null,
|
|
164
|
-
"description": "Field label, rendered above the widget. Automatically names the one control the Field holds: a composed `InputBase`, `SelectBase`, `TextAreaBase`, `DateInputBase`, `TimeInputBase` or `DateTimeInputBase` (an `inline` picker has no input, so it takes nothing), or a bestax input that renders a single input of its own (`Input`, `Select`, `TextArea`, `Numberinput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`, `Combobox`, `Taginput`, `File`), adopts a generated id that the label's `htmlFor` points at, and a group (`Radios`, `Checkboxes`, `Rate`, `DateRangeInput`, or a composed `DateRangeInputBase`, `inline` or not) points `aria-labelledby` at the label's own id unless you gave the group an `aria-label` or `aria-labelledby`. A range `Slider` takes the id on its low thumb and also starts each thumb's name with the label through `aria-labelledby`, unless its `ariaLabel` names that thumb or its own `aria-label` or `aria-labelledby` takes the label's place. An `Autocomplete`, a `Combobox` or a `Taginput` also names its open list after the label through `aria-labelledby`. `Checkbox`, `Radio` and `Switch` are named by their own children, and the label names one only when you wire it as you would any control with an `id` of its own, `labelProps={{ htmlFor }}` plus that control's `id`. Only the single inputs take the `htmlFor`, so once the Field's content has mounted the label keeps it only while one of them holds the id, and drops it over anything else: a group, a `Checkbox`, `Radio` or `Switch`, an `inline` picker, an inner `Field`, an input with an `id` of its own, markup of your own, or nothing at all. A server render, and the first render while hydrating, still write the `htmlFor`, which keeps hydration matching, and an input that mounts later puts it back. The label names an input with an `id` of your own only through `labelProps={{ htmlFor }}` set to that `id`, a `for` it always keeps; `labelProps={{ htmlFor: undefined }}` opts out. Skipped for `grouped`/`hasAddons` fields (multiple controls), and a nested `Field` starts its own scope, so a horizontal Field whose body holds an inner `Field` names the control there only when you wire it. Two controls in one plain labeled Field would both adopt the id, so give each an `id` of its own.",
|
|
164
|
+
"description": "Field label, rendered above the widget. Automatically names the one control the Field holds: a composed `InputBase`, `SelectBase`, `TextAreaBase`, `DateInputBase`, `TimeInputBase` or `DateTimeInputBase` (an `inline` picker has no input, so it takes nothing), or a bestax input that renders a single input of its own (`Input`, `Select`, `TextArea`, `Numberinput`, `PinInput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`, `Combobox`, `Taginput`, `File`), adopts a generated id that the label's `htmlFor` points at, and a group (`Radios`, `Checkboxes`, `Rate`, `DateRangeInput`, or a composed `DateRangeInputBase`, `inline` or not) points `aria-labelledby` at the label's own id unless you gave the group an `aria-label` or `aria-labelledby`. A range `Slider` takes the id on its low thumb and also starts each thumb's name with the label through `aria-labelledby`, unless its `ariaLabel` names that thumb or its own `aria-label` or `aria-labelledby` takes the label's place. An `Autocomplete`, a `Combobox` or a `Taginput` also names its open list after the label through `aria-labelledby`. `Checkbox`, `Radio` and `Switch` are named by their own children, and the label names one only when you wire it as you would any control with an `id` of its own, `labelProps={{ htmlFor }}` plus that control's `id`. Only the single inputs take the `htmlFor`, so once the Field's content has mounted the label keeps it only while one of them holds the id, and drops it over anything else: a group, a `Checkbox`, `Radio` or `Switch`, an `inline` picker, an inner `Field`, an input with an `id` of its own, markup of your own, or nothing at all. A server render, and the first render while hydrating, still write the `htmlFor`, which keeps hydration matching, and an input that mounts later puts it back. The label names an input with an `id` of your own only through `labelProps={{ htmlFor }}` set to that `id`, a `for` it always keeps; `labelProps={{ htmlFor: undefined }}` opts out. Skipped for `grouped`/`hasAddons` fields (multiple controls), and a nested `Field` starts its own scope, so a horizontal Field whose body holds an inner `Field` names the control there only when you wire it. Two controls in one plain labeled Field would both adopt the id, so give each an `id` of its own.",
|
|
165
165
|
"required": false,
|
|
166
166
|
"inherited": false,
|
|
167
167
|
"deprecated": false
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "PinInput",
|
|
3
|
+
"kind": "component",
|
|
4
|
+
"category": "form",
|
|
5
|
+
"slug": "form/pininput",
|
|
6
|
+
"docsUrl": "https://bestax.io/docs/api/form/pininput",
|
|
7
|
+
"examples": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Basic PinInput",
|
|
10
|
+
"code": "<PinInput label=\"Verification code\" />"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"title": "Verifying a Code",
|
|
14
|
+
"code": "function example() {\n const [code, setCode] = useState('');\n const [checked, setChecked] = useState<string>();\n const wrong = checked !== undefined && checked !== '123456';\n return (\n <PinInput\n label=\"Enter the 6-digit code we sent you\"\n value={code}\n onChange={next => {\n setCode(next);\n setChecked(undefined);\n }}\n onComplete={setChecked}\n color={wrong ? 'danger' : checked ? 'success' : undefined}\n message={\n wrong\n ? 'That code isn’t right. Try 123456.'\n : checked\n ? 'Verified.'\n : undefined\n }\n messageColor={wrong ? 'danger' : 'success'}\n />\n );\n}"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"title": "Length",
|
|
18
|
+
"code": "<Block>\n <PinInput label=\"PIN\" length={4} />\n <PinInput label=\"Backup code\" length={8} />\n</Block>"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"title": "Letters and Digits",
|
|
22
|
+
"code": "<PinInput label=\"Invite code\" type=\"alphanumeric\" length={5} />"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"title": "Masked",
|
|
26
|
+
"code": "<PinInput label=\"PIN\" length={4} isMasked defaultValue=\"12\" />"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"title": "Colors, Sizes and Rounded Boxes",
|
|
30
|
+
"code": "<Block>\n <PinInput label=\"Primary\" color=\"primary\" length={4} defaultValue=\"12\" />\n <PinInput label=\"Small\" size=\"small\" length={4} defaultValue=\"12\" />\n <PinInput label=\"Large\" size=\"large\" length={4} defaultValue=\"12\" />\n <PinInput label=\"Rounded\" isRounded length={4} defaultValue=\"12\" />\n</Block>"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"title": "Disabled",
|
|
34
|
+
"code": "<PinInput label=\"Verification code\" disabled defaultValue=\"123\" />"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"title": "Controlled vs Uncontrolled",
|
|
38
|
+
"code": "<PinInput label=\"Code\" defaultValue=\"42\" />"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"title": "In a Form",
|
|
42
|
+
"code": "function example() {\n const [sent, setSent] = useState<string>();\n return (\n <form\n onSubmit={e => {\n e.preventDefault();\n setSent(String(new FormData(e.currentTarget).get('otp')));\n }}\n >\n <PinInput label=\"Verification code\" name=\"otp\" required />\n <Button type=\"submit\" color=\"primary\" mt=\"3\">\n Verify\n </Button>\n {sent !== undefined && (\n <Paragraph mt=\"2\">The form sent otp={sent}</Paragraph>\n )}\n </form>\n );\n}"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"title": "Inside a Field",
|
|
46
|
+
"code": "<Field horizontal label=\"Code\">\n <PinInput length={4} />\n</Field>"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "Right to Left",
|
|
50
|
+
"code": "<Block dir=\"rtl\">\n <PinInput label=\"رمز التحقق\" defaultValue=\"12\" />\n</Block>"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"title": "Forwarded ref",
|
|
54
|
+
"code": "function example() {\n const ref = React.useRef<HTMLInputElement>(null);\n return (\n <>\n <PinInput ref={ref} label=\"Verification code\" />\n <Button mt=\"3\" onClick={() => ref.current?.focus()}>\n Focus the code\n </Button>\n </>\n );\n}"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"accessibility": "- **One field:** the code is in one `<input>`, named by the label, so a screen reader announces a single text field with the code as its value. The boxes are drawn with `aria-hidden`, so nothing reads them a second time, or as a run of separate fields.\n- **Say how long the code is in the label,** as in \"Enter the 6-digit code\", since the boxes that show it are hidden from a screen reader. The `message` isn't tied to the input, so a screen reader doesn't read it when the field takes focus. If the length lives in help text of your own, point the input's `aria-describedby` at it.\n- **Masked,** the input is a password field, so a screen reader doesn't read the code out.\n- **The keyboard a phone shows** is the number pad by default, through `inputMode=\"numeric\"`, and the full keyboard with `type=\"alphanumeric\"`.\n- **Focus** shows on the boxes, since the input itself is transparent: the box at the caret, or every selected box, gets Bulma's focus ring, and an empty box at the caret draws a blinking caret. Under `prefers-reduced-motion: reduce` the caret stays drawn and doesn't blink.\n- **Forced colors:** in Windows high contrast mode the box at the caret is outlined in the system's highlight color, where Bulma's focus ring, a shadow, is gone, the caret is drawn in the system's text color, and a disabled box takes the system's color for disabled text.",
|
|
58
|
+
"related": ["Field", "Input", "Numberinput", "useBulmaClasses"],
|
|
59
|
+
"storybook": "https://bestax.io/storybook/?path=/story/form-pininput--default",
|
|
60
|
+
"summary": "The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each. The code lives in one real `<input>` that lies over the boxes, so it is one field to a screen reader, and paste, autofill from a text message and password managers work on it as they do on any input. The boxes draw what it holds, and the box at the caret, or every box in a selection, shows Bulma's focus look.",
|
|
61
|
+
"import": "import { PinInput } from '@allxsmith/bestax-bulma';",
|
|
62
|
+
"sourceFile": "bulma-ui/src/form/PinInput.tsx",
|
|
63
|
+
"rootClass": "pin-input",
|
|
64
|
+
"parts": [
|
|
65
|
+
{
|
|
66
|
+
"path": "PinInput",
|
|
67
|
+
"summary": "The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each. The code lives in one real `<input>` that lies over the boxes, so it is one field to a screen reader, and paste, autofill from a text message and password managers work on it as they do on any input. The boxes draw what it holds, and the box at the caret, or every box in a selection, shows Bulma's focus look.",
|
|
68
|
+
"component": null,
|
|
69
|
+
"props": [
|
|
70
|
+
{
|
|
71
|
+
"name": "label",
|
|
72
|
+
"type": "React.ReactNode",
|
|
73
|
+
"default": null,
|
|
74
|
+
"description": "Field label. Automatically associated with the input that holds the code via `htmlFor`, so a screen reader announces one field under this name. Uses your `id` when provided, otherwise a generated one. Dropped inside an outer `Field`, whose own label associates instead when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`) and you set no `id` here.",
|
|
75
|
+
"required": false,
|
|
76
|
+
"inherited": false,
|
|
77
|
+
"deprecated": false
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "labelProps",
|
|
81
|
+
"type": "React.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }",
|
|
82
|
+
"default": null,
|
|
83
|
+
"description": "Props for the label element. An explicit `htmlFor` here overrides the automatic association (no id is generated then).",
|
|
84
|
+
"required": false,
|
|
85
|
+
"inherited": false,
|
|
86
|
+
"deprecated": false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "id",
|
|
90
|
+
"type": "string",
|
|
91
|
+
"default": null,
|
|
92
|
+
"description": "Applied to the input that holds the code (the labellable control), not the row of boxes.",
|
|
93
|
+
"required": false,
|
|
94
|
+
"inherited": false,
|
|
95
|
+
"deprecated": false
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "length",
|
|
99
|
+
"type": "number",
|
|
100
|
+
"default": "6",
|
|
101
|
+
"description": "How many characters the code has. Each gets a box, and it is the input's `maxLength`.",
|
|
102
|
+
"required": false,
|
|
103
|
+
"inherited": false,
|
|
104
|
+
"deprecated": false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "value",
|
|
108
|
+
"type": "string",
|
|
109
|
+
"default": null,
|
|
110
|
+
"description": "The code (controlled). Only its first `length` characters are shown and submitted.",
|
|
111
|
+
"required": false,
|
|
112
|
+
"inherited": false,
|
|
113
|
+
"deprecated": false
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "defaultValue",
|
|
117
|
+
"type": "string",
|
|
118
|
+
"default": "''",
|
|
119
|
+
"description": "The code to start with (uncontrolled). Only its first `length` characters are kept.",
|
|
120
|
+
"required": false,
|
|
121
|
+
"inherited": false,
|
|
122
|
+
"deprecated": false
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "type",
|
|
126
|
+
"type": "'number' | 'alphanumeric'",
|
|
127
|
+
"default": "'number'",
|
|
128
|
+
"description": "What the code takes. A digit from any script, such as an Arabic-Indic or a Persian one, goes in as its ASCII digit, so the code reaches `onChange`, `onComplete` and the form as `0` to `9`. Anything else typed or pasted is dropped, and the caret stays where it was. The input stays a text field either way, so a code keeps its leading zeros.",
|
|
129
|
+
"required": false,
|
|
130
|
+
"inherited": false,
|
|
131
|
+
"deprecated": false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "isMasked",
|
|
135
|
+
"type": "boolean",
|
|
136
|
+
"default": "false",
|
|
137
|
+
"description": "Shows a dot in each filled box in place of its character, and makes the input a password field, so a screen reader doesn't read the code out either.",
|
|
138
|
+
"required": false,
|
|
139
|
+
"inherited": false,
|
|
140
|
+
"deprecated": false
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "color",
|
|
144
|
+
"type": "'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | 'black' | 'dark' | 'light' | 'white'",
|
|
145
|
+
"default": null,
|
|
146
|
+
"description": "Bulma color for the boxes, the colors an `Input` takes.",
|
|
147
|
+
"required": false,
|
|
148
|
+
"inherited": false,
|
|
149
|
+
"deprecated": false
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "size",
|
|
153
|
+
"type": "'small' | 'medium' | 'large'",
|
|
154
|
+
"default": null,
|
|
155
|
+
"description": "Size of the boxes.",
|
|
156
|
+
"required": false,
|
|
157
|
+
"inherited": false,
|
|
158
|
+
"deprecated": false
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "isRounded",
|
|
162
|
+
"type": "boolean",
|
|
163
|
+
"default": "false",
|
|
164
|
+
"description": "Round boxes.",
|
|
165
|
+
"required": false,
|
|
166
|
+
"inherited": false,
|
|
167
|
+
"deprecated": false
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "disabled",
|
|
171
|
+
"type": "boolean",
|
|
172
|
+
"default": "false",
|
|
173
|
+
"description": "Whether the input is disabled.",
|
|
174
|
+
"required": false,
|
|
175
|
+
"inherited": false,
|
|
176
|
+
"deprecated": false
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"name": "onChange",
|
|
180
|
+
"type": "(value: string) => void",
|
|
181
|
+
"default": null,
|
|
182
|
+
"description": "Called with the code each time it changes. A paste replaces the whole code with what was pasted, kept to what `type` takes and cut to `length`, and a paste with nothing the code takes changes nothing.",
|
|
183
|
+
"required": false,
|
|
184
|
+
"inherited": false,
|
|
185
|
+
"deprecated": false
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"name": "onComplete",
|
|
189
|
+
"type": "(value: string) => void",
|
|
190
|
+
"default": null,
|
|
191
|
+
"description": "Called with the code when a change makes it `length` characters long, such as typing the last character, a paste or autofill. It fires once for each change that fills the last box with a new code, and never on a render. A `value` you set yourself doesn't call it.",
|
|
192
|
+
"required": false,
|
|
193
|
+
"inherited": false,
|
|
194
|
+
"deprecated": false
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "labelSize",
|
|
198
|
+
"type": "'small' | 'normal' | 'medium' | 'large'",
|
|
199
|
+
"default": null,
|
|
200
|
+
"description": "Size for the label (used in horizontal layouts).",
|
|
201
|
+
"required": false,
|
|
202
|
+
"inherited": true,
|
|
203
|
+
"deprecated": false
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "horizontal",
|
|
207
|
+
"type": "boolean",
|
|
208
|
+
"default": "false",
|
|
209
|
+
"description": "Horizontal field layout.",
|
|
210
|
+
"required": false,
|
|
211
|
+
"inherited": true,
|
|
212
|
+
"deprecated": false
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"name": "message",
|
|
216
|
+
"type": "React.ReactNode",
|
|
217
|
+
"default": null,
|
|
218
|
+
"description": "Help/validation message below the input.",
|
|
219
|
+
"required": false,
|
|
220
|
+
"inherited": true,
|
|
221
|
+
"deprecated": false
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"name": "messageColor",
|
|
225
|
+
"type": "'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'",
|
|
226
|
+
"default": null,
|
|
227
|
+
"description": "Bulma color for the message.",
|
|
228
|
+
"required": false,
|
|
229
|
+
"inherited": true,
|
|
230
|
+
"deprecated": false
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"name": "fieldClassName",
|
|
234
|
+
"type": "string",
|
|
235
|
+
"default": null,
|
|
236
|
+
"description": "Additional CSS classes for the Field wrapper.",
|
|
237
|
+
"required": false,
|
|
238
|
+
"inherited": true,
|
|
239
|
+
"deprecated": false
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
"extraProps": [
|
|
243
|
+
{
|
|
244
|
+
"name": "name",
|
|
245
|
+
"type": "string",
|
|
246
|
+
"default": null,
|
|
247
|
+
"description": "Form field name, on the input, so a form submits the code under it.",
|
|
248
|
+
"required": false,
|
|
249
|
+
"inherited": false,
|
|
250
|
+
"deprecated": false
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "autoComplete",
|
|
254
|
+
"type": "string",
|
|
255
|
+
"default": "\"one-time-code\"",
|
|
256
|
+
"description": "Lets the browser offer a code it received by text message, and tells a password manager this is a one-time code.",
|
|
257
|
+
"required": false,
|
|
258
|
+
"inherited": false,
|
|
259
|
+
"deprecated": false
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "inputMode",
|
|
263
|
+
"type": "string",
|
|
264
|
+
"default": null,
|
|
265
|
+
"description": "The keyboard a phone shows: `\"numeric\"` with `type=\"number\"`, `\"text\"` with `type=\"alphanumeric\"`.",
|
|
266
|
+
"required": false,
|
|
267
|
+
"inherited": false,
|
|
268
|
+
"deprecated": false
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"name": "pattern",
|
|
272
|
+
"type": "string",
|
|
273
|
+
"default": null,
|
|
274
|
+
"description": "Exactly `length` of what `type` takes, `[0-9]{6}` by default, so a form with `required` won't submit a partial code. Any non-empty partial code fails it, `required` or not.",
|
|
275
|
+
"required": false,
|
|
276
|
+
"inherited": false,
|
|
277
|
+
"deprecated": false
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"name": "className",
|
|
281
|
+
"type": "string",
|
|
282
|
+
"default": null,
|
|
283
|
+
"description": "Additional CSS classes for the row of boxes.",
|
|
284
|
+
"required": false,
|
|
285
|
+
"inherited": false,
|
|
286
|
+
"deprecated": false
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"name": "ref",
|
|
290
|
+
"type": "React.Ref<HTMLInputElement>",
|
|
291
|
+
"default": null,
|
|
292
|
+
"description": "Ref forwarded to the input that holds the code.",
|
|
293
|
+
"required": false,
|
|
294
|
+
"inherited": false,
|
|
295
|
+
"deprecated": false
|
|
296
|
+
}
|
|
297
|
+
],
|
|
298
|
+
"catchAll": "All standard <input> attributes and Bulma helper props",
|
|
299
|
+
"types": []
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
"cssVars": [
|
|
303
|
+
{
|
|
304
|
+
"css": "--bulma-pin-input-gap",
|
|
305
|
+
"sass": "$pin-input-gap",
|
|
306
|
+
"default": "0.5em",
|
|
307
|
+
"scope": "root"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"css": "--bulma-pin-input-box-width",
|
|
311
|
+
"sass": "$pin-input-box-width",
|
|
312
|
+
"default": "var(--bulma-control-height)",
|
|
313
|
+
"scope": "root"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"css": "--bulma-pin-input-caret-width",
|
|
317
|
+
"sass": "$pin-input-caret-width",
|
|
318
|
+
"default": "max(1px, 0.0625em)",
|
|
319
|
+
"scope": "root"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"css": "--bulma-pin-input-caret-height",
|
|
323
|
+
"sass": "$pin-input-caret-height",
|
|
324
|
+
"default": "1.25em",
|
|
325
|
+
"scope": "root"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"css": "--bulma-pin-input-caret-color",
|
|
329
|
+
"sass": "$pin-input-caret-color",
|
|
330
|
+
"default": "currentColor",
|
|
331
|
+
"scope": "root"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"css": "--bulma-pin-input-caret-blink-duration",
|
|
335
|
+
"sass": "$pin-input-caret-blink-duration",
|
|
336
|
+
"default": "1s",
|
|
337
|
+
"scope": "root"
|
|
338
|
+
}
|
|
339
|
+
],
|
|
340
|
+
"cssVarsFrom": "Input"
|
|
341
|
+
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"code": "import { Radio, Radios } from '@allxsmith/bestax-bulma';\nimport { radioColors, radioSizes } from '@allxsmith/bestax-bulma';\n\nfunction example() {\n return (\n <>\n <Radios label=\"Accent color\" name=\"accent\" defaultValue=\"primary\">\n {radioColors.map(color => (\n <Radio key={color} value={color} color={color}>\n {color}\n </Radio>\n ))}\n </Radios>\n <Radios label=\"Size\" name=\"accent-size\" defaultValue=\"normal\">\n {radioSizes.map(size => (\n <Radio key={size} value={size} size={size}>\n {size}\n </Radio>\n ))}\n </Radios>\n </>\n );\n}"
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
|
-
"accessibility": "- Each `Radio` is rendered as a `<label>` wrapping an `<input type=\"radio\">` and the label text, for optimal accessibility.\n- Use the same `name` prop for a group of radios to ensure only one can be selected.",
|
|
29
|
+
"accessibility": "- Each `Radio` is rendered as a `<label>` wrapping an `<input type=\"radio\">` and the label text, for optimal accessibility.\n- Use the same `name` prop for a group of radios to ensure only one can be selected.\n- An unchecked radio shows only its ring, `--bulma-radio-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. Set a replacement on a class you pass through `className`, since `style` goes to the `<input>`, naming the label's class too (`.styled-radio.my-radio`) so load order can't undo it, and hold it to 3:1 as well.",
|
|
30
30
|
"related": ["Field", "Radios", "useBulmaClasses"],
|
|
31
31
|
"storybook": "https://bestax.io/storybook/?path=/story/form-radio--mutually-exclusive",
|
|
32
32
|
"summary": "The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"name": "className",
|
|
81
81
|
"type": "string",
|
|
82
82
|
"default": null,
|
|
83
|
-
"description": "Additional CSS classes to
|
|
83
|
+
"description": "Additional CSS classes, on the outer `<label>`. That's the element that declares the `--bulma-radio-*` variables, so a class is how to set one: `style` and the other input attributes go to the `<input>`. Name `.styled-radio` beside your class in the selector, so it beats the label's own declaration whatever order the stylesheets load in. A `color` prop declares the active color again on `.styled-radio.is-<color>`, which ties that selector, so leave `color` unset when a class sets it.",
|
|
84
84
|
"required": false,
|
|
85
85
|
"inherited": false,
|
|
86
86
|
"deprecated": false
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
{
|
|
126
126
|
"css": "--bulma-radio-border-color",
|
|
127
127
|
"sass": "$radio-border-color",
|
|
128
|
-
"default": "var(--bulma-
|
|
128
|
+
"default": "var(--bulma-text-weak)",
|
|
129
129
|
"scope": "root"
|
|
130
130
|
},
|
|
131
131
|
{
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
"name": "className",
|
|
208
208
|
"type": "string",
|
|
209
209
|
"default": null,
|
|
210
|
-
"description": "Additional CSS classes to
|
|
210
|
+
"description": "Additional CSS classes, on the outer `<label>`. That's the element that declares the `--bulma-radio-*` variables, so a class is how to set one: `style` and the other input attributes go to the `<input>`. Name `.styled-radio` beside your class in the selector, so it beats the label's own declaration whatever order the stylesheets load in. A `color` prop declares the active color again on `.styled-radio.is-<color>`, which ties that selector, so leave `color` unset when a class sets it.",
|
|
211
211
|
"required": false,
|
|
212
212
|
"inherited": false,
|
|
213
213
|
"deprecated": false
|
|
@@ -37,7 +37,7 @@ instead of hand-writing markup.
|
|
|
37
37
|
- Raw `*Base` form exports (`InputBase`, `SelectBase`, `TextAreaBase`, …) are
|
|
38
38
|
escape-hatch variants of the convenience wrappers above them; see the Form docs.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
104 documented components. Generated from the API docs — every exported
|
|
41
41
|
component is guaranteed to appear (the generator fails if one lacks an API page).
|
|
42
42
|
|
|
43
43
|
## Elements
|
|
@@ -123,6 +123,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
123
123
|
- [File](https://bestax.io/docs/api/form/file) — The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
|
|
124
124
|
- [Input](https://bestax.io/docs/api/form/input) — The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states…
|
|
125
125
|
- [Numberinput](https://bestax.io/docs/api/form/numberinput) — The `Numberinput` component provides a number input with increment/decrement buttons.
|
|
126
|
+
- [PinInput](https://bestax.io/docs/api/form/pininput) — The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each.
|
|
126
127
|
- [Radio](https://bestax.io/docs/api/form/radio) — The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.
|
|
127
128
|
- [Radios](https://bestax.io/docs/api/form/radios) — The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.
|
|
128
129
|
- [Rate](https://bestax.io/docs/api/form/rate) — The `Rate` component provides a star/icon-based rating system.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bestax-form
|
|
3
|
-
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
3
|
+
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -97,6 +97,7 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
97
97
|
| `Combobox` | A value from a list: `data` for a select, tags or groups, or parts. |
|
|
98
98
|
| `Slider` | Range slider; single/dual thumbs, steps, tooltips, vertical. |
|
|
99
99
|
| `Numberinput` | Numeric input with increment/decrement, min/max, step, stepper. |
|
|
100
|
+
| `PinInput` | A one-time code or PIN as a row of boxes, all in one real input. |
|
|
100
101
|
| `Rate` | Star rating; `max`, `precision` (half/quarter), custom icons, `disabled`. |
|
|
101
102
|
| `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
|
|
102
103
|
| `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers; month or year via `granularity`. |
|
|
@@ -113,6 +114,19 @@ parts for a server search whose answer must show unfiltered, or a "Create" item:
|
|
|
113
114
|
value and never sets the field's text, so you filter the items, keep the value and set the text in
|
|
114
115
|
`onSelect`.
|
|
115
116
|
|
|
117
|
+
**A verification code or a PIN is `PinInput`**, not a row of one-character `Input`s with focus
|
|
118
|
+
moved by hand. It holds the code in one real input under a row of boxes, so a screen reader hears
|
|
119
|
+
one field named by its `label`, and paste, autofill from a text message (`autoComplete` is
|
|
120
|
+
`"one-time-code"`) and password managers work as they do on any input. Give it `length` (6 by
|
|
121
|
+
default), `value`/`onChange` with the code as a string, and verify in `onComplete`, which fires
|
|
122
|
+
once each time a change fills the last box. It takes digits unless `type="alphanumeric"`, turns a
|
|
123
|
+
digit from any script (Arabic-Indic, Persian) into its ASCII digit, drops anything else typed or
|
|
124
|
+
pasted, and `isMasked` shows dots for a PIN. A paste replaces the whole code. Its default `pattern`
|
|
125
|
+
asks for the full length, so `required` rejects a partial code, not just an empty one. Say the
|
|
126
|
+
length in the `label` ("Enter the 6-digit code"): the boxes are hidden from a screen reader, and the
|
|
127
|
+
`message` isn't tied to the input, so it isn't read on focus. Separate help text needs
|
|
128
|
+
`aria-describedby` on the PinInput.
|
|
129
|
+
|
|
116
130
|
## Common props
|
|
117
131
|
|
|
118
132
|
Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
|
|
@@ -139,10 +153,10 @@ spellings compile only where they historically existed: `isFullWidth` everywhere
|
|
|
139
153
|
`Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
|
|
140
154
|
|
|
141
155
|
**Labels.** Default to the convenience `label` prop, with `horizontal` for a side-by-side row.
|
|
142
|
-
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `
|
|
143
|
-
`DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
144
|
-
`htmlFor`/`id`, to your `id` when you pass one
|
|
145
|
-
explicit `labelProps={{ htmlFor }}` wins. `Combobox` renders its `Field` and label too, and wires
|
|
156
|
+
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `PinInput`, `Slider`, `DateInput`,
|
|
157
|
+
`TimeInput`, `DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
158
|
+
and wire `htmlFor`/`id`, to your `id` when you pass one
|
|
159
|
+
(`<Input label="Email" id="email" horizontal />`); an explicit `labelProps={{ htmlFor }}` wins. `Combobox` renders its `Field` and label too, and wires
|
|
146
160
|
them to its field. Given `data`, its `id`, `aria-label` and `aria-labelledby` go to the field;
|
|
147
161
|
built from parts, put them on `Combobox.Input`, since on the `Combobox` they land on its wrapper
|
|
148
162
|
`div`. The group inputs (`Checkboxes`, `Radios`, `Rate`, `DateRangeInput`) name their
|
|
@@ -255,9 +269,9 @@ multi-field form plus the advanced inputs.
|
|
|
255
269
|
## Reuse the shipped components
|
|
256
270
|
|
|
257
271
|
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch, File,
|
|
258
|
-
Autocomplete, Combobox, Slider, Numberinput, Rate, Taginput, and the date/time inputs (see
|
|
259
|
-
inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
260
|
-
control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
272
|
+
Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, and the date/time inputs (see
|
|
273
|
+
the inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
274
|
+
reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
261
275
|
`docs/docs/api/form/` first — it's probably already there under a different name.
|
|
262
276
|
|
|
263
277
|
## What happens after submit
|
|
@@ -93,6 +93,7 @@ File input with `label`, `message`, color/size, and icon support.
|
|
|
93
93
|
| `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`. |
|
|
94
94
|
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
95
95
|
| `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). |
|
|
96
|
+
| `PinInput` | `length` (6), `value`/`defaultValue`/`onChange` (the code as a string), `onComplete(code)` once a change fills the last box, `type` (`'number'` or `'alphanumeric'`), `isMasked`, `name`, `color`, `size`, `isRounded`, `disabled` and the Field props. One input holds the code (`autoComplete="one-time-code"`); a paste replaces it. |
|
|
96
97
|
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
97
98
|
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
98
99
|
|
|
@@ -287,6 +287,20 @@ const [range, setRange] = useState<[number, number]>([20, 80]);
|
|
|
287
287
|
const [qty, setQty] = useState(1);
|
|
288
288
|
<Numberinput value={qty} onChange={setQty} min={1} max={10} step={1} />;
|
|
289
289
|
|
|
290
|
+
// PinInput: a one-time code or PIN, a box per character, all in one input.
|
|
291
|
+
// onComplete fires once each time a change fills the last box.
|
|
292
|
+
const [code, setCode] = useState('');
|
|
293
|
+
const verify = (otp: string) => {
|
|
294
|
+
// Send it to your server, then show the result with color and message.
|
|
295
|
+
};
|
|
296
|
+
<PinInput
|
|
297
|
+
label="Enter the 6-digit code"
|
|
298
|
+
name="otp"
|
|
299
|
+
value={code}
|
|
300
|
+
onChange={setCode}
|
|
301
|
+
onComplete={verify}
|
|
302
|
+
/>;
|
|
303
|
+
|
|
290
304
|
// Rate
|
|
291
305
|
const [stars, setStars] = useState(3);
|
|
292
306
|
<Rate value={stars} onChange={setStars} max={5} />;
|
|
@@ -527,23 +527,23 @@ neither, so load what the app still needs once for the whole app, from its entry
|
|
|
527
527
|
whose styles ship in `extras.css` and `bestax.css`, and stop loading the extension once nothing
|
|
528
528
|
the app keeps uses its classes:
|
|
529
529
|
|
|
530
|
-
| Extension | bestax
|
|
531
|
-
| ------------------------------------------------------- |
|
|
532
|
-
| `bulma-carousel` | `Carousel`, with a `CarouselItem` for each slide
|
|
533
|
-
| `bulma-slider` | `Slider`
|
|
534
|
-
| `bulma-switch` | `Switch`
|
|
535
|
-
| `bulma-checkradio` | `Checkbox` and `Radio`, which draw their own box
|
|
536
|
-
| `bulma-tooltip`, `@creativebulma/bulma-tooltip` | `Tooltip`
|
|
537
|
-
| `bulma-badge`, `@creativebulma/bulma-badge` | `Badge`
|
|
538
|
-
| `bulma-steps` | `Steps`
|
|
539
|
-
| `bulma-timeline` | `Timeline`
|
|
540
|
-
| `bulma-tagsinput`, `@creativebulma/bulma-tagsinput` | `Taginput`
|
|
541
|
-
| `bulma-calendar` | `DateInput`, `TimeInput`, `DateTimeInput` and `DateRangeInput`
|
|
542
|
-
| `bulma-accordion` | `Collapses`, with a `Collapse` for each item
|
|
543
|
-
| `bulma-collapsible`, `@creativebulma/bulma-collapsible` | `Collapse` (`Collapses` for an accordion)
|
|
544
|
-
| `bulma-pageloader` | `Loading`, `isFullPage` for one over the whole page
|
|
545
|
-
| `bulma-quickview` | `Sidebar`
|
|
546
|
-
| `bulma-toast` | `Toast`: `toast.show()` from code, with a `ToastContainer` once
|
|
530
|
+
| Extension | bestax |
|
|
531
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
532
|
+
| `bulma-carousel` | `Carousel`, with a `CarouselItem` for each slide and `itemsToShow` for several in view |
|
|
533
|
+
| `bulma-slider` | `Slider` |
|
|
534
|
+
| `bulma-switch` | `Switch` |
|
|
535
|
+
| `bulma-checkradio` | `Checkbox` and `Radio`, which draw their own box |
|
|
536
|
+
| `bulma-tooltip`, `@creativebulma/bulma-tooltip` | `Tooltip` |
|
|
537
|
+
| `bulma-badge`, `@creativebulma/bulma-badge` | `Badge` |
|
|
538
|
+
| `bulma-steps` | `Steps` |
|
|
539
|
+
| `bulma-timeline` | `Timeline` |
|
|
540
|
+
| `bulma-tagsinput`, `@creativebulma/bulma-tagsinput` | `Taginput` |
|
|
541
|
+
| `bulma-calendar` | `DateInput`, `TimeInput`, `DateTimeInput` and `DateRangeInput` |
|
|
542
|
+
| `bulma-accordion` | `Collapses`, with a `Collapse` for each item |
|
|
543
|
+
| `bulma-collapsible`, `@creativebulma/bulma-collapsible` | `Collapse` (`Collapses` for an accordion) |
|
|
544
|
+
| `bulma-pageloader` | `Loading`, `isFullPage` for one over the whole page |
|
|
545
|
+
| `bulma-quickview` | `Sidebar` |
|
|
546
|
+
| `bulma-toast` | `Toast`: `toast.show()` from code, with a `ToastContainer` once |
|
|
547
547
|
|
|
548
548
|
`bulma-extensions`, the bundle, holds several of these: replace the ones in the table, and keep
|
|
549
549
|
loading the bundle only for the rest. **bulma-social** has no bestax component: keep loading it,
|
|
@@ -626,8 +626,24 @@ read for the burger, a carousel and a theme toggle too, and its TODO names the f
|
|
|
626
626
|
takes `hasDelete` and `onDelete`, `Tag` takes `onDelete` and `Message` takes `onClose`; render
|
|
627
627
|
the element only while it's in state.
|
|
628
628
|
- **`html-script:carousel`** drives a carousel. `Carousel` takes its slides as `CarouselItem`
|
|
629
|
-
children and renders its own arrows and indicators (`autoplay` for a timed one).
|
|
630
|
-
in
|
|
629
|
+
children and renders its own arrows and indicators (`autoplay` for a timed one). For several
|
|
630
|
+
slides in view, set `itemsToShow`, with a variant per viewport (`itemsToShowMobile`,
|
|
631
|
+
`itemsToShowTablet`, …), and `itemsToScroll`: bulma-carousel calls them `slidesToShow` and
|
|
632
|
+
`slidesToScroll`. bulma-carousel's `breakpoints` replace both at and below pixel widths of its
|
|
633
|
+
own (each entry's `changePoint`), and a page that sets none gets its defaults: 1 slide up to
|
|
634
|
+
480px, 2 up to 640px and 3 up to 768px, each scrolling as many, so `slidesToShow` applies only
|
|
635
|
+
from 769px up. Below that the defaults decide whatever `slidesToShow` says: a phone shows 1
|
|
636
|
+
slide, and a page that sets `slidesToShow: 1` shows 2 or 3 between a phone's width and a
|
|
637
|
+
tablet's. A page's own `breakpoints` replace the defaults, and an empty list turns them off.
|
|
638
|
+
`Carousel` has no pixel widths: `itemsToShowMobile` covers every width below 769px, where all
|
|
639
|
+
of those defaults sit, and the wider variants start at Bulma's breakpoints. So set
|
|
640
|
+
`itemsToShow` to the page's `slidesToShow`, and `itemsToShowMobile` to what the page shows on
|
|
641
|
+
a phone (1 under the defaults) where that differs; one `itemsToScroll` covers every width. To
|
|
642
|
+
keep the steps bulma-carousel takes between those widths, compute `itemsToShow` from the
|
|
643
|
+
window's width in your own code. `Carousel` loops unless `repeat={false}`, while
|
|
644
|
+
bulma-carousel stops at the ends unless `loop` (back to the start) or `infinite` (round
|
|
645
|
+
without a break) is set, so a carousel with neither takes `repeat={false}`. Its styles ship in
|
|
646
|
+
bestax's `extras.css` and `bestax.css`, not in Bulma's stylesheet.
|
|
631
647
|
- **`html-script:theme`** switches Bulma's light and dark schemes. The toggle is `Theme`'s: it
|
|
632
648
|
writes `data-theme` on `<html>` from its `colorMode` (`light`, `dark` or `system`), so keep the
|
|
633
649
|
choice in state, save it in `localStorage`, and pass it. The first paint is `index.html`'s:
|
|
@@ -698,9 +714,51 @@ than `this`. A `<body onload>` becomes an effect.
|
|
|
698
714
|
- **`html-url:relative`**: `img/logo.png` resolved against the page's folder, and in the app it
|
|
699
715
|
resolves against the address the component renders at. The file is there beside the page, so
|
|
700
716
|
copy it into the app's `public/` folder and point at it from the site root (`/img/logo.png`), or
|
|
701
|
-
import it.
|
|
702
|
-
|
|
703
|
-
|
|
717
|
+
import it. Each relative URL in a `srcset` is its own entry, quoted as itself
|
|
718
|
+
(`` `hero@2x.png` in `srcset` ``). Several of these share one TODO line, and each is its own
|
|
719
|
+
report entry.
|
|
720
|
+
|
|
721
|
+
A link to another page beside this one should point at the route that page becomes: the
|
|
722
|
+
router's, in an app with one. A site of static pages, with no router to join, can route on the
|
|
723
|
+
URL's hash in a small module of its own and install no router package, so every page still
|
|
724
|
+
opens from a static host as it is:
|
|
725
|
+
|
|
726
|
+
```ts
|
|
727
|
+
// router.ts: the page the address's hash names (`#/about`).
|
|
728
|
+
import { useEffect, useState } from 'react';
|
|
729
|
+
|
|
730
|
+
export const routes = { home: '#/', about: '#/about' } as const;
|
|
731
|
+
export type Route = keyof typeof routes;
|
|
732
|
+
|
|
733
|
+
function routeOf(hash: string): Route {
|
|
734
|
+
const found = (Object.keys(routes) as Route[]).find(
|
|
735
|
+
route => routes[route] === hash
|
|
736
|
+
);
|
|
737
|
+
return found ?? 'home';
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export function useRoute(): Route {
|
|
741
|
+
const [route, setRoute] = useState(() => routeOf(window.location.hash));
|
|
742
|
+
useEffect(() => {
|
|
743
|
+
const onHashChange = () => {
|
|
744
|
+
const hash = window.location.hash;
|
|
745
|
+
// Only `#/…` (or no hash) names a page; leave an in-page anchor such
|
|
746
|
+
// as `#features`, or a placeholder `#`, to the browser.
|
|
747
|
+
if (hash !== '' && !hash.startsWith('#/')) return;
|
|
748
|
+
setRoute(routeOf(hash));
|
|
749
|
+
window.scrollTo(0, 0); // a new page starts at the top, as it did
|
|
750
|
+
};
|
|
751
|
+
window.addEventListener('hashchange', onHashChange);
|
|
752
|
+
return () => window.removeEventListener('hashchange', onHashChange);
|
|
753
|
+
}, []);
|
|
754
|
+
return route;
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
The app renders the page the route names, from the modules the pages became
|
|
759
|
+
(`const Page = { home: HomePage, about: AboutPage }[useRoute()]`), with the parts the pages
|
|
760
|
+
share around it, and a link to `about.html` takes `href={routes.about}`.
|
|
761
|
+
|
|
704
762
|
- **`html-url:missing`**: a relative URL whose target isn't beside the page, read the way the
|
|
705
763
|
browser read it, so it was a dead link or a missing file already. Point it somewhere real, or
|
|
706
764
|
drop it.
|
|
@@ -63,6 +63,7 @@ Stock Bulma examples:
|
|
|
63
63
|
@use 'bulma/sass/elements/title'; // Title, SubTitle
|
|
64
64
|
@use 'bulma/sass/form/input-textarea'; // Input, TextArea
|
|
65
65
|
@use 'bulma/sass/form/select'; // Select
|
|
66
|
+
@use 'bulma/sass/form/tools'; // Field, Control
|
|
66
67
|
@use 'bulma/sass/components/card'; // Card
|
|
67
68
|
@use 'bulma/sass/components/modal'; // Modal
|
|
68
69
|
@use 'bulma/sass/components/navbar'; // Navbar
|
|
@@ -83,8 +84,9 @@ is **more complete** than the docs page's Option C example, which omits several)
|
|
|
83
84
|
`emptystate`, `timeline`, `treeview`
|
|
84
85
|
|
|
85
86
|
**Form** (`@allxsmith/bestax-bulma/scss/form/<name>`):
|
|
86
|
-
`checkbox`, `radio`, `switch`, `slider`, `numberinput`, `
|
|
87
|
-
`taginput`, `picker-popover`, `dateinput`, `timeinput`, `datetimeinput`,
|
|
87
|
+
`checkbox`, `radio`, `switch`, `slider`, `numberinput`, `pininput`, `rate`, `autocomplete`,
|
|
88
|
+
`combobox`, `taginput`, `picker-popover`, `dateinput`, `timeinput`, `datetimeinput`,
|
|
89
|
+
`daterangeinput`, `file`
|
|
88
90
|
|
|
89
91
|
**Elements** (`@allxsmith/bestax-bulma/scss/elements/<name>`): `kbd`, `linkbutton`, `loader`,
|
|
90
92
|
`notification`, `progress`, `skeleton`
|
|
@@ -128,6 +130,8 @@ Notes:
|
|
|
128
130
|
- `DateInput`/`TimeInput`/`DateTimeInput` also need `picker-popover`.
|
|
129
131
|
- `DateRangeInput` needs `daterangeinput` for its field and `dateinput` for its calendar, and
|
|
130
132
|
`picker-popover` too.
|
|
133
|
+
- `PinInput`'s boxes are Bulma inputs, so an app using it needs `bulma/sass/form/input-textarea`
|
|
134
|
+
next to `scss/form/pininput`, and `bulma/sass/form/tools` for the label and help text around it.
|
|
131
135
|
- `Popover.Close` renders a Bulma button, so an app using it needs `bulma/sass/elements/button`
|
|
132
136
|
next to `scss/components/popover`.
|
|
133
137
|
- `ContextMenu`'s menu is Bulma's dropdown markup, so an app using it needs
|
|
@@ -248,6 +248,9 @@ lone class or the `style` prop cannot outrank:
|
|
|
248
248
|
its own declaration — target the declaring element in your CSS:
|
|
249
249
|
`.tooltip-content { --bulma-tooltip-background: … }`.
|
|
250
250
|
|
|
251
|
+
The `style` prop also misses on Checkbox and Radio, which put it on their hidden `<input>`; set
|
|
252
|
+
their variables through `className` (see their entries below).
|
|
253
|
+
|
|
251
254
|
### Avatar / Avatars / Badge
|
|
252
255
|
|
|
253
256
|
| Variable | Default |
|
|
@@ -327,6 +330,17 @@ rather than with these variables.
|
|
|
327
330
|
`--bulma-checkbox-label-gap`, `--bulma-checkbox-radius`, `--bulma-checkbox-size`,
|
|
328
331
|
`--bulma-checkbox-transition-duration`
|
|
329
332
|
|
|
333
|
+
The check mark and the mixed dash are both `--bulma-checkbox-check-color`, on the
|
|
334
|
+
`--bulma-checkbox-active-color` (checked) or `--bulma-checkbox-indeterminate-color` (mixed) fill,
|
|
335
|
+
so a dark fill needs a light check color. Set these on a class you pass through `className`, not
|
|
336
|
+
with `style`: Checkbox puts `style` and its other attributes on the hidden `<input>`, not on the
|
|
337
|
+
`.styled-checkbox` label that declares them. Name both classes in the selector
|
|
338
|
+
(`.styled-checkbox.my-box`, mind the class prefix) so it wins whatever order the stylesheets load
|
|
339
|
+
in. A `color` prop declares both fills again on `.styled-checkbox.is-<color>`, which ties with
|
|
340
|
+
that selector, so leave `color` unset when a class sets the fill. The unchecked border, `--bulma-checkbox-border-color`, defaults to `text-weak` so
|
|
341
|
+
the box holds the 3:1 WCAG asks of a control's boundary; keep a replacement at 3:1 against what
|
|
342
|
+
the form sits on.
|
|
343
|
+
|
|
330
344
|
### Collapse
|
|
331
345
|
|
|
332
346
|
`--bulma-collapse-border-color`, `--bulma-collapse-group-gap`, `--bulma-collapse-header-weight`,
|
|
@@ -498,6 +512,16 @@ sizes. Bulma's own `--bulma-notification-*` variables set the rest.
|
|
|
498
512
|
`--bulma-numberinput-stepper-button-hover-bg`, `--bulma-numberinput-stepper-button-hover-color`,
|
|
499
513
|
`--bulma-numberinput-stepper-width`
|
|
500
514
|
|
|
515
|
+
### PinInput
|
|
516
|
+
|
|
517
|
+
Declared on the row, `.pin-input`. Each box is a Bulma `.input`, so Bulma's `--bulma-input-*`
|
|
518
|
+
variables color it, and a box's size comes from its size class. The gap and the caret's size are
|
|
519
|
+
in `em`, so they follow the size of the boxes, and the caret is `currentColor`, the box's text.
|
|
520
|
+
|
|
521
|
+
`--bulma-pin-input-box-width`, `--bulma-pin-input-caret-blink-duration`,
|
|
522
|
+
`--bulma-pin-input-caret-color`, `--bulma-pin-input-caret-height`, `--bulma-pin-input-caret-width`,
|
|
523
|
+
`--bulma-pin-input-gap`
|
|
524
|
+
|
|
501
525
|
### Picker popover (DateInput / TimeInput / DateTimeInput / DateRangeInput)
|
|
502
526
|
|
|
503
527
|
`--bulma-picker-popover-animation-duration`, `--bulma-picker-popover-background`,
|
|
@@ -525,6 +549,12 @@ them. See the constituent-element exception above.
|
|
|
525
549
|
`--bulma-radio-border-width`, `--bulma-radio-dot-color`, `--bulma-radio-focus-shadow-size`,
|
|
526
550
|
`--bulma-radio-label-gap`, `--bulma-radio-size`, `--bulma-radio-transition-duration`
|
|
527
551
|
|
|
552
|
+
As with Checkbox, set these on a class you pass through `className`, with both classes in the
|
|
553
|
+
selector (`.styled-radio.my-radio`): `style` goes to the hidden `<input>`. A `color` prop
|
|
554
|
+
declares `--bulma-radio-active-color` again on `.styled-radio.is-<color>`, which ties that
|
|
555
|
+
selector, so leave `color` unset when a class sets it. The unchecked ring, `--bulma-radio-border-color`, defaults to `text-weak` for the same 3:1 boundary
|
|
556
|
+
contrast.
|
|
557
|
+
|
|
528
558
|
### Rate
|
|
529
559
|
|
|
530
560
|
`--bulma-rate-color-active`, `--bulma-rate-color-hover`, `--bulma-rate-color-inactive`,
|
|
@@ -73,6 +73,7 @@ Shades (`colorShade` / `backgroundColorShade`): `00, 05, 10, … 95, invert, lig
|
|
|
73
73
|
| `Loading` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | spinner color; default light grey |
|
|
74
74
|
| `Navbar` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
|
|
75
75
|
| `Numberinput` | `primary \| link \| info \| success \| warning \| danger \| light \| dark` | `small \| medium \| large` | also `inputColor` (the 6) for the inner input |
|
|
76
|
+
| `PinInput` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | each box is a Bulma `.input`, so `--bulma-input-*` theme it; also `isRounded`; `--bulma-pin-input-*` |
|
|
76
77
|
| `Pagination` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | **entire `color` prop deprecated** (no CSS ships; dev-warn; removal next major) |
|
|
77
78
|
| `Panel` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
|
|
78
79
|
| `Progress` | every `validColors` member (the `-bis`/`-ter` shades and the greys deprecated: no CSS, dev-warn, removed next major — see ⚠️) | `small \| medium \| large` | — |
|
package/data/skills.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"id": "component-catalog",
|
|
12
12
|
"file": "references/component-catalog.md",
|
|
13
|
-
"bytes":
|
|
13
|
+
"bytes": 20804
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"id": "library-contributor",
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "bestax-form",
|
|
28
|
-
"description": "Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.",
|
|
28
|
+
"description": "Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.",
|
|
29
29
|
"promptName": "form",
|
|
30
30
|
"dir": "bestax-form",
|
|
31
31
|
"references": [
|
|
32
|
-
{ "id": "api", "file": "references/api.md", "bytes":
|
|
33
|
-
{ "id": "patterns", "file": "references/patterns.md", "bytes":
|
|
32
|
+
{ "id": "api", "file": "references/api.md", "bytes": 19315 },
|
|
33
|
+
{ "id": "patterns", "file": "references/patterns.md", "bytes": 9835 }
|
|
34
34
|
],
|
|
35
35
|
"examples": []
|
|
36
36
|
},
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
{
|
|
114
114
|
"id": "bulma-classes-unmappables",
|
|
115
115
|
"file": "references/bulma-classes/unmappables.md",
|
|
116
|
-
"bytes":
|
|
116
|
+
"bytes": 56988
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"id": "css-migration",
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
{
|
|
163
163
|
"id": "modular-build",
|
|
164
164
|
"file": "references/modular-build.md",
|
|
165
|
-
"bytes":
|
|
165
|
+
"bytes": 12331
|
|
166
166
|
}
|
|
167
167
|
],
|
|
168
168
|
"examples": []
|
|
@@ -176,12 +176,12 @@
|
|
|
176
176
|
{
|
|
177
177
|
"id": "css-variables",
|
|
178
178
|
"file": "references/css-variables.md",
|
|
179
|
-
"bytes":
|
|
179
|
+
"bytes": 48297
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
"id": "themeable-components",
|
|
183
183
|
"file": "references/themeable-components.md",
|
|
184
|
-
"bytes":
|
|
184
|
+
"bytes": 25101
|
|
185
185
|
}
|
|
186
186
|
],
|
|
187
187
|
"examples": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bestax-mcp",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.3",
|
|
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",
|
|
@@ -40,16 +40,16 @@
|
|
|
40
40
|
"url": "https://github.com/allxsmith/bestax/issues"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
44
|
-
"zod": "^4.
|
|
45
|
-
"@allxsmith/bestax-bulma": "^5.
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
44
|
+
"zod": "^4.6.7",
|
|
45
|
+
"@allxsmith/bestax-bulma": "^5.43.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@jest/globals": "^30.0.0",
|
|
49
49
|
"@types/jest": "^30.0.0",
|
|
50
|
-
"@types/node": "^26.6.
|
|
50
|
+
"@types/node": "^26.6.5",
|
|
51
51
|
"jest": "^30.2.0",
|
|
52
|
-
"ts-jest": "^29.4.
|
|
52
|
+
"ts-jest": "^29.4.15",
|
|
53
53
|
"typescript": "^6.0.3"
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|