bestax-mcp 0.0.0
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/README.md +90 -0
- package/data/catalog.json +1869 -0
- package/data/components/Autocomplete.json +604 -0
- package/data/components/Avatar.json +220 -0
- package/data/components/Avatars.json +294 -0
- package/data/components/Badge.json +261 -0
- package/data/components/Block.json +105 -0
- package/data/components/Box.json +157 -0
- package/data/components/Breadcrumb.json +155 -0
- package/data/components/Button.json +599 -0
- package/data/components/Buttons.json +533 -0
- package/data/components/Card.json +506 -0
- package/data/components/Carousel.json +512 -0
- package/data/components/Cell.json +154 -0
- package/data/components/Checkbox.json +176 -0
- package/data/components/Checkboxes.json +240 -0
- package/data/components/Code.json +128 -0
- package/data/components/Collapse.json +218 -0
- package/data/components/Column.json +284 -0
- package/data/components/Columns.json +542 -0
- package/data/components/ConfigProvider.json +16 -0
- package/data/components/Container.json +149 -0
- package/data/components/Content.json +228 -0
- package/data/components/Control.json +484 -0
- package/data/components/DateInput.json +872 -0
- package/data/components/DateTimeInput.json +952 -0
- package/data/components/Delete.json +153 -0
- package/data/components/Dialog.json +285 -0
- package/data/components/Divider.json +68 -0
- package/data/components/Dropdown.json +353 -0
- package/data/components/Emphasis.json +99 -0
- package/data/components/Field.json +539 -0
- package/data/components/Figure.json +143 -0
- package/data/components/File.json +403 -0
- package/data/components/Footer.json +109 -0
- package/data/components/Grid.json +357 -0
- package/data/components/Hero.json +340 -0
- package/data/components/Icon.json +232 -0
- package/data/components/IconText.json +307 -0
- package/data/components/Image.json +166 -0
- package/data/components/Input.json +583 -0
- package/data/components/Level.json +332 -0
- package/data/components/Link.json +154 -0
- package/data/components/LinkButton.json +247 -0
- package/data/components/ListItem.json +101 -0
- package/data/components/Loading.json +323 -0
- package/data/components/Media.json +345 -0
- package/data/components/Menu.json +317 -0
- package/data/components/Message.json +303 -0
- package/data/components/Modal.json +444 -0
- package/data/components/Navbar.json +996 -0
- package/data/components/Notification.json +165 -0
- package/data/components/Numberinput.json +364 -0
- package/data/components/OrderedList.json +189 -0
- package/data/components/Pagination.json +619 -0
- package/data/components/Panel.json +582 -0
- package/data/components/Paragraph.json +107 -0
- package/data/components/Pre.json +114 -0
- package/data/components/Progress.json +137 -0
- package/data/components/Radio.json +164 -0
- package/data/components/Radios.json +240 -0
- package/data/components/Rate.json +466 -0
- package/data/components/Reveal.json +177 -0
- package/data/components/Section.json +123 -0
- package/data/components/Select.json +526 -0
- package/data/components/Sidebar.json +488 -0
- package/data/components/Skeleton.json +162 -0
- package/data/components/Slider.json +534 -0
- package/data/components/Span.json +100 -0
- package/data/components/Steps.json +452 -0
- package/data/components/Strong.json +105 -0
- package/data/components/SubTitle.json +233 -0
- package/data/components/Switch.json +291 -0
- package/data/components/Table.json +490 -0
- package/data/components/Tabs.json +592 -0
- package/data/components/Tag.json +197 -0
- package/data/components/Taginput.json +587 -0
- package/data/components/Tags.json +181 -0
- package/data/components/TextArea.json +483 -0
- package/data/components/Theme.json +65 -0
- package/data/components/TimeInput.json +906 -0
- package/data/components/Title.json +240 -0
- package/data/components/Toast.json +308 -0
- package/data/components/Tooltip.json +212 -0
- package/data/components/UnorderedList.json +149 -0
- package/data/components/Valid value constants.json +16 -0
- package/data/components/classNames.json +65 -0
- package/data/components/useBulmaClasses.json +169 -0
- package/data/components/usePrefixedClassNames.json +25 -0
- package/data/skills/bestax-custom-component/SKILL.md +196 -0
- package/data/skills/bestax-custom-component/examples/stat-card.tsx +105 -0
- package/data/skills/bestax-custom-component/references/api.md +90 -0
- package/data/skills/bestax-custom-component/references/component-catalog.md +149 -0
- package/data/skills/bestax-custom-component/references/library-contributor.md +395 -0
- package/data/skills/bestax-custom-component/references/patterns.md +136 -0
- package/data/skills/bestax-form/SKILL.md +254 -0
- package/data/skills/bestax-form/references/api.md +103 -0
- package/data/skills/bestax-form/references/patterns.md +210 -0
- package/data/skills/bestax-icons/SKILL.md +80 -0
- package/data/skills/bestax-icons/examples/icon-usage.tsx +80 -0
- package/data/skills/bestax-icons/references/icon-libraries.md +131 -0
- package/data/skills/bestax-layout-scaffold/SKILL.md +196 -0
- package/data/skills/bestax-layout-scaffold/examples/app-shell.tsx +86 -0
- package/data/skills/bestax-layout-scaffold/examples/card-grid.tsx +106 -0
- package/data/skills/bestax-layout-scaffold/examples/centered.tsx +56 -0
- package/data/skills/bestax-layout-scaffold/examples/content-page.tsx +90 -0
- package/data/skills/bestax-layout-scaffold/examples/landing.tsx +122 -0
- package/data/skills/bestax-layout-scaffold/references/archetypes.md +246 -0
- package/data/skills/bestax-layout-scaffold/references/layout-components.md +269 -0
- package/data/skills/bestax-migrate/SKILL.md +78 -0
- package/data/skills/bestax-migrate/references/component-map.md +91 -0
- package/data/skills/bestax-migrate/references/css-migration.md +100 -0
- package/data/skills/bestax-migrate/references/prop-map.md +48 -0
- package/data/skills/bestax-migrate/references/unmappables.md +124 -0
- package/data/skills/bestax-optimize/SKILL.md +131 -0
- package/data/skills/bestax-optimize/references/modular-build.md +173 -0
- package/data/skills/bestax-theming/SKILL.md +117 -0
- package/data/skills/bestax-theming/examples/dark-mode.tsx +38 -0
- package/data/skills/bestax-theming/examples/theme-config.tsx +60 -0
- package/data/skills/bestax-theming/references/css-variables.md +466 -0
- package/data/skills/bestax-theming/references/themeable-components.md +119 -0
- package/data/skills.json +148 -0
- package/dist/data.d.ts +131 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +111 -0
- package/dist/format.d.ts +37 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +178 -0
- package/dist/helper-props.d.ts +57 -0
- package/dist/helper-props.d.ts.map +1 -0
- package/dist/helper-props.js +249 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/search.d.ts +32 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +194 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +616 -0
- package/dist/version.d.ts +29 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +147 -0
- package/package.json +70 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
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, 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.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Building forms with bestax-bulma
|
|
8
|
+
|
|
9
|
+
This skill covers the form components in `@allxsmith/bestax-bulma` and how to compose them.
|
|
10
|
+
|
|
11
|
+
**Important:** bestax-bulma ships **no form/validation library** — there is no integration with
|
|
12
|
+
formik, react-hook-form, yup, or zod, and no `useForm`-style hook. You own your form state with
|
|
13
|
+
plain React (`useState` / `useReducer` or any library you choose) and feed validation results
|
|
14
|
+
back via each input's own `color`, `message`, and `messageColor` props on the convenience inputs
|
|
15
|
+
(`Input`, `Select`, `TextArea`, …). Always put validation state on the **input**: `Field` has no
|
|
16
|
+
`message`/`messageColor`, and although `FieldProps` types a `color`, `Field` discards it — it
|
|
17
|
+
renders no class, so setting it looks right and does nothing. (`FieldLabel`/`FieldBody` do honor
|
|
18
|
+
`color`, as the `has-text-*` helper.) See **Validation without a library** below.
|
|
19
|
+
|
|
20
|
+
## Use when
|
|
21
|
+
|
|
22
|
+
- Building a form out of bestax inputs, selects, checkboxes, switches, or advanced controls.
|
|
23
|
+
- Deciding between the convenience components (`<Input label=… />`) and explicit
|
|
24
|
+
`Field` + `Control` + `*Base` composition.
|
|
25
|
+
- Showing help text and error/success states on fields.
|
|
26
|
+
|
|
27
|
+
To build a brand-new input component (not just use the existing ones), use the
|
|
28
|
+
`bestax-custom-component` skill instead.
|
|
29
|
+
|
|
30
|
+
## Field / Control / Label / Help composition
|
|
31
|
+
|
|
32
|
+
Bulma forms are a three-tier structure. bestax models it directly:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Field // container + layout (horizontal / grouped / hasAddons)
|
|
36
|
+
├── label // rendered from Field's `label` prop, or <Field.Label> when horizontal
|
|
37
|
+
└── Control // wraps ONE input; adds icons + loading
|
|
38
|
+
├── InputBase / SelectBase / TextAreaBase // the raw styled element
|
|
39
|
+
└── <p class="help">…</p> // help / validation message
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
You rarely write all of this by hand. The convenience components (`Input`, `Select`,
|
|
43
|
+
`TextArea`, …) auto-wrap themselves in `Field` + `Control` when they aren't already inside one,
|
|
44
|
+
using context (`useInsideField` / `useInsideControl`) to detect their surroundings.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// Convenience: one line, auto-wrapped in Field + Control.
|
|
48
|
+
<Input label="Email" type="email" placeholder="you@example.com" />
|
|
49
|
+
|
|
50
|
+
// Explicit composition: full control over layout.
|
|
51
|
+
<Field label="Email">
|
|
52
|
+
<Control iconLeftName="envelope" hasIconsLeft>
|
|
53
|
+
<InputBase type="email" placeholder="you@example.com" />
|
|
54
|
+
</Control>
|
|
55
|
+
</Field>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Layout via Field
|
|
59
|
+
|
|
60
|
+
- `horizontal` — label and control side by side (wraps children in `Field.Body`; use
|
|
61
|
+
`Field.Label` / `Field.Body` directly for multi-control rows).
|
|
62
|
+
- `grouped` — `true | 'centered' | 'right' | 'multiline'`, controls in a row.
|
|
63
|
+
- `hasAddons` — `true | 'centered' | 'right'`, attached controls (input + button).
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
<Field hasAddons>
|
|
67
|
+
<Control isExpanded>
|
|
68
|
+
<InputBase placeholder="Search" />
|
|
69
|
+
</Control>
|
|
70
|
+
<Control>
|
|
71
|
+
<Button color="primary">Go</Button>
|
|
72
|
+
</Control>
|
|
73
|
+
</Field>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Component inventory
|
|
77
|
+
|
|
78
|
+
All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Field+Control;
|
|
79
|
+
`*Base` components are the raw styled elements for explicit composition.
|
|
80
|
+
|
|
81
|
+
| Component | What it is |
|
|
82
|
+
| ------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
83
|
+
| `Field`, `Field.Label`, `Field.Body` | Field container + horizontal label/body parts. |
|
|
84
|
+
| `Control` | Wraps one input; left/right icons, `isLoading`, `isExpanded`, `size`. |
|
|
85
|
+
| `Input` / `InputBase` | Text input (convenience / raw). |
|
|
86
|
+
| `Select` / `SelectBase` | Dropdown select. |
|
|
87
|
+
| `TextArea` / `TextAreaBase` | Multiline text. |
|
|
88
|
+
| `Checkbox` / `Checkboxes` | Single checkbox / managed group (array value). |
|
|
89
|
+
| `Radio` / `Radios` | Single radio / managed single-select group. |
|
|
90
|
+
| `Switch` | Toggle switch (`isRounded`, `isThin`, `isOutlined`, RTL). |
|
|
91
|
+
| `File` | File upload input with label/message. |
|
|
92
|
+
| `Autocomplete` | Input with filtered dropdown suggestions + keyboard nav. |
|
|
93
|
+
| `Slider` | Range slider; single/dual thumbs, steps, tooltips, vertical. |
|
|
94
|
+
| `Numberinput` | Numeric input with increment/decrement, min/max, step, stepper. |
|
|
95
|
+
| `Rate` | Star rating; `max`, `precision` (half/quarter), custom icons, `disabled`. |
|
|
96
|
+
| `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
|
|
97
|
+
| `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers. |
|
|
98
|
+
|
|
99
|
+
## Common props
|
|
100
|
+
|
|
101
|
+
Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
|
|
102
|
+
|
|
103
|
+
| Prop | Type | Purpose |
|
|
104
|
+
| -------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
105
|
+
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state — use `'danger'` for errors, `'success'` for valid. |
|
|
106
|
+
| `size` | `'small' \| 'medium' \| 'large'` | Input size. |
|
|
107
|
+
| `value` / `onChange` | controlled value + handler | Standard React controlled inputs. |
|
|
108
|
+
| `defaultValue` | uncontrolled initial value | When not controlling state. |
|
|
109
|
+
| `disabled`, `readOnly` | `boolean` | Native states (`readOnly` on `*Base`). |
|
|
110
|
+
| `label` | `ReactNode` | Field label (convenience components; auto-associated via `htmlFor`). |
|
|
111
|
+
| `message` | `ReactNode` | Help / validation text rendered as `<p class="help">`. |
|
|
112
|
+
| `messageColor` | a Bulma color | Colors the help text (`'danger'` for errors). |
|
|
113
|
+
| `iconLeftName` / `iconRightName` | `string` | Icon shortcuts; pair with `hasIconsLeft/Right`. |
|
|
114
|
+
| `isLoading` | `boolean` | Loading indicator on the Control. |
|
|
115
|
+
|
|
116
|
+
Plus the full Bulma **helper props** (`m`, `p`, `textColor`, `display`, …) on every component
|
|
117
|
+
via `useBulmaClasses`.
|
|
118
|
+
|
|
119
|
+
⚠️ Full-width casing is inconsistent across the library: `Select`, `File`, and `Table` take
|
|
120
|
+
`isFullwidth` (lowercase w); `Button` alone takes `isFullWidth`; `Tabs` takes bare `fullwidth`.
|
|
121
|
+
|
|
122
|
+
The `label` prop on the single-control convenience inputs (`Input`, `Select`, `TextArea`,
|
|
123
|
+
`File`, `Numberinput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`,
|
|
124
|
+
`Taginput`) wires `htmlFor`/`id` automatically — your `id` is used when provided, a
|
|
125
|
+
generated one otherwise, and an explicit `labelProps={{ htmlFor }}` wins. The wiring only
|
|
126
|
+
happens when the component renders its own `Field` (nested inside one, the `label` prop is
|
|
127
|
+
dropped); the date/time pickers skip it in `inline` mode and `Taginput` skips it at
|
|
128
|
+
`maxTags` (no visible input to label). The group inputs (`Checkboxes`, `Radios`, `Rate`)
|
|
129
|
+
associate their `label` too, but group-style: the wrapper gets `role="group"`/`"radiogroup"`
|
|
130
|
+
and `aria-labelledby` pointing at the label. Composing `Field` + bases yourself also
|
|
131
|
+
associates: `Field`'s own `label` wires to a single composed `InputBase`/`SelectBase`/
|
|
132
|
+
`TextAreaBase` (skipped for `grouped`/`hasAddons`). Pass `labelProps={{ htmlFor }}` plus a
|
|
133
|
+
matching `id` only when you want a stable id, or `labelProps={{ htmlFor: undefined }}` to
|
|
134
|
+
opt out — e.g. when the labeled `Field` wraps something that is not one of those bases.
|
|
135
|
+
|
|
136
|
+
## Convenience vs composed
|
|
137
|
+
|
|
138
|
+
- **Convenience** (`<Input label message … />`) — for typical, single-control fields. Fewer
|
|
139
|
+
lines, auto-wrapping, built-in `message`/`messageColor`. Default to this.
|
|
140
|
+
- **Composed** (`Field` + `Control` + `InputBase`) — when you need grouped controls, addons,
|
|
141
|
+
multiple controls per field, or custom layout. The convenience components detect they're
|
|
142
|
+
already inside a `Field`/`Control` and won't double-wrap, so you can mix the two.
|
|
143
|
+
|
|
144
|
+
## Validation without a library
|
|
145
|
+
|
|
146
|
+
There is no built-in validation. The pattern is: **own your state, compute errors yourself, and
|
|
147
|
+
reflect them with `color` + `message` + `messageColor`.**
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { useState } from 'react';
|
|
151
|
+
import { Input, Button } from '@allxsmith/bestax-bulma';
|
|
152
|
+
|
|
153
|
+
function SignupForm() {
|
|
154
|
+
const [email, setEmail] = useState('');
|
|
155
|
+
const [touched, setTouched] = useState(false);
|
|
156
|
+
|
|
157
|
+
const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email);
|
|
158
|
+
const error =
|
|
159
|
+
touched && !valid ? 'Please enter a valid email address.' : undefined;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<form
|
|
163
|
+
onSubmit={e => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
setTouched(true);
|
|
166
|
+
if (valid) {
|
|
167
|
+
// submit…
|
|
168
|
+
}
|
|
169
|
+
}}
|
|
170
|
+
>
|
|
171
|
+
<Input
|
|
172
|
+
label="Email"
|
|
173
|
+
type="email"
|
|
174
|
+
value={email}
|
|
175
|
+
onChange={e => setEmail(e.target.value)}
|
|
176
|
+
onBlur={() => setTouched(true)}
|
|
177
|
+
color={error ? 'danger' : undefined}
|
|
178
|
+
message={error}
|
|
179
|
+
messageColor={error ? 'danger' : undefined}
|
|
180
|
+
iconLeftName="envelope"
|
|
181
|
+
/>
|
|
182
|
+
<Button color="primary" type="submit" mt="3">
|
|
183
|
+
Sign up
|
|
184
|
+
</Button>
|
|
185
|
+
</form>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Rules of thumb:
|
|
191
|
+
|
|
192
|
+
- Set `color="danger"` on the input **and** `messageColor="danger"` on the help text so both the
|
|
193
|
+
control and the message read as an error. Use `'success'` to signal a valid field.
|
|
194
|
+
- Want a different validation library? Wire it up yourself — pass its `value`/`onChange`/error
|
|
195
|
+
string into these props. bestax does not prescribe one.
|
|
196
|
+
- For grouped controls, render the `<p class="help">` via the `message` prop of the convenience
|
|
197
|
+
component, or add it manually inside the `Field` when composing.
|
|
198
|
+
|
|
199
|
+
See `references/api.md` for per-component props and `references/patterns.md` for a full
|
|
200
|
+
multi-field form plus the advanced inputs.
|
|
201
|
+
|
|
202
|
+
## Reuse the shipped components
|
|
203
|
+
|
|
204
|
+
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch,
|
|
205
|
+
File, Autocomplete, Slider, Numberinput, Rate, Taginput, and the date/time inputs (see the
|
|
206
|
+
inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
207
|
+
reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
208
|
+
`docs/docs/api/form/` first — it's probably already there under a different name.
|
|
209
|
+
|
|
210
|
+
## What happens after submit
|
|
211
|
+
|
|
212
|
+
A form is not finished at the last field. The two components that carry the result are easy to
|
|
213
|
+
miss because Bulma has something that looks close:
|
|
214
|
+
|
|
215
|
+
- **Confirmation** — `Toast`, not `Notification`/`Message`. Mount
|
|
216
|
+
`<ToastContainer position="top-right" />` once at the app root, then call
|
|
217
|
+
`toast.success('Demo booked')` from the submit handler (`.danger` for a failed submit). It
|
|
218
|
+
self-dismisses; a `Notification` is a static element you place and tear down yourself.
|
|
219
|
+
- **"Are you sure?"** — `Dialog`, not `Modal`. Mount `<DialogContainer />` at the root, then
|
|
220
|
+
`if (await dialog.confirm({ title: 'Delete this key?', message: '…', type: 'danger' })) …`.
|
|
221
|
+
It resolves to a boolean, so a destructive action stays one `if` rather than a state machine.
|
|
222
|
+
`Modal` is an empty shell — with it you rebuild the title, message and button row by hand.
|
|
223
|
+
|
|
224
|
+
Both also work as plain controlled components (`<Toast message … onClose>`,
|
|
225
|
+
`<Dialog isOpen … onConfirm onCancel>`) when the state should live in your component.
|
|
226
|
+
|
|
227
|
+
## Visually inspect it in a browser
|
|
228
|
+
|
|
229
|
+
Forms have layout, spacing, and _stateful_ behavior that types and unit tests don't cover.
|
|
230
|
+
Before calling a form done, **render it and look at it**: run `pnpm storybook` (in `bulma-ui`)
|
|
231
|
+
or the docs dev server, open the form, and check field alignment/spacing, the help-text/error
|
|
232
|
+
states, and the validation flow (submit empty → fields turn `danger` with messages; fix → errors
|
|
233
|
+
clear). If claude-in-chrome or Playwright is available, drive the browser and screenshot the
|
|
234
|
+
valid and error states; otherwise eyeball it yourself. No browser at all (headless CI)? Fall
|
|
235
|
+
back to a production build plus a Node `renderToString` smoke render, grep the emitted HTML
|
|
236
|
+
for the expected classes/states, and say plainly that the visual pass is still owed.
|
|
237
|
+
|
|
238
|
+
## Checklist
|
|
239
|
+
|
|
240
|
+
- [ ] Built from the shipped form components (no hand-rolled inputs / reinvented controls).
|
|
241
|
+
- [ ] Every label is programmatically associated — the convenience `label` prop, the group
|
|
242
|
+
inputs, and `Field` + single-base composition all do this automatically; pass
|
|
243
|
+
`labelProps={{ htmlFor }}` plus a matching `id` only for a stable id, and label a
|
|
244
|
+
multi-control `Field`'s controls individually (`aria-label`, `aria-labelledby`, or
|
|
245
|
+
a `<label htmlFor>` matching each control's `id`).
|
|
246
|
+
- [ ] Controlled inputs have both `value` and `onChange` (or use `defaultValue` uncontrolled).
|
|
247
|
+
- [ ] Error state shows via `color="danger"` + `message` + `messageColor="danger"`.
|
|
248
|
+
- [ ] Grouped/addon layouts use explicit `Field` + `Control` composition.
|
|
249
|
+
- [ ] No assumption of a built-in validation/form library — state is owned by the app.
|
|
250
|
+
- [ ] Submit feedback is a `Toast` and any "are you sure?" is a `Dialog` — not a hand-placed
|
|
251
|
+
`Notification` or a `Modal` you filled in yourself.
|
|
252
|
+
- [ ] **Rendered and visually inspected in a browser** — layout and the error/validation states
|
|
253
|
+
look right, not just green tests. No browser available? The `renderToString` fallback above
|
|
254
|
+
counts only if you grepped the emitted classes/states **and** said the visual pass is owed.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Reference: form component props
|
|
2
|
+
|
|
3
|
+
All components import from `@allxsmith/bestax-bulma`. Source lives in `bulma-ui/src/form/`.
|
|
4
|
+
Every component also accepts the full Bulma helper props (`m`, `p`, `textColor`, `display`, …)
|
|
5
|
+
via `useBulmaClasses`.
|
|
6
|
+
|
|
7
|
+
## Field — `form/Field.tsx`
|
|
8
|
+
|
|
9
|
+
Container and layout. Compound parts: `Field.Label`, `Field.Body`.
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Notes |
|
|
12
|
+
| ----------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| `horizontal` | `boolean` | Label + control side by side. Auto-wraps children in `Field.Body`. |
|
|
14
|
+
| `grouped` | `boolean \| 'centered' \| 'right' \| 'multiline'` | Controls in a row. |
|
|
15
|
+
| `hasAddons` | `boolean \| 'centered' \| 'right'` | Attached controls. |
|
|
16
|
+
| `narrow` | `boolean` | Constrain to content width (inside horizontal bodies). |
|
|
17
|
+
| `label` | `ReactNode` | Convenience label; auto-associates with a single composed `InputBase`/`SelectBase`/`TextAreaBase` (skipped for `grouped`/`hasAddons`). |
|
|
18
|
+
| `labelSize` | `'small' \| 'normal' \| 'medium' \| 'large'` | Label size. |
|
|
19
|
+
| `labelProps` | label attributes | Props for the `<label>`. An explicit `htmlFor` (even `undefined`) takes over the association; otherwise it is set automatically. |
|
|
20
|
+
| `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
|
|
21
|
+
|
|
22
|
+
## Control — `form/Control.tsx`
|
|
23
|
+
|
|
24
|
+
Wraps a single input; adds icons and loading.
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Notes |
|
|
27
|
+
| -------------------------------- | -------------------------------- | ------------------------------- |
|
|
28
|
+
| `hasIconsLeft` / `hasIconsRight` | `boolean` | Icon containers. |
|
|
29
|
+
| `iconLeft` / `iconRight` | `IconProps` | Full icon config. |
|
|
30
|
+
| `iconLeftName` / `iconRightName` | `string` | Icon name shortcut. |
|
|
31
|
+
| `iconLeftSize` / `iconRightSize` | `'small' \| 'medium' \| 'large'` | Icon size. |
|
|
32
|
+
| `isLoading` | `boolean` | Loading spinner on the control. |
|
|
33
|
+
| `isExpanded` | `boolean` | Fill available width. |
|
|
34
|
+
| `size` | `'small' \| 'medium' \| 'large'` | Control size. |
|
|
35
|
+
| `as` | `'div' \| 'p'` | Root element (default `div`). |
|
|
36
|
+
|
|
37
|
+
## Input / InputBase — `form/Input.tsx`, `form/InputBase.tsx`
|
|
38
|
+
|
|
39
|
+
`Input` composes `Field` + `Control` + `InputBase`. Beyond `InputBase` props it adds Field-level
|
|
40
|
+
(`label`, `labelSize`, `labelProps`, `horizontal`), Control-level (`iconLeftName`,
|
|
41
|
+
`iconRightName`, `iconLeftSize`, `iconRightSize`, `hasIconsLeft`, `hasIconsRight`, `isLoading`,
|
|
42
|
+
`isExpanded`, `controlSize`), message (`message`, `messageColor`), and container class overrides
|
|
43
|
+
(`fieldClassName`, `controlClassName`).
|
|
44
|
+
|
|
45
|
+
`InputBase` props:
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Notes |
|
|
48
|
+
| ------------------------------------- | --------------------------------------------------------------------- | ------------------------------ |
|
|
49
|
+
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state. |
|
|
50
|
+
| `size` | `'small' \| 'medium' \| 'large'` | Size. |
|
|
51
|
+
| `isRounded` | `boolean` | Pill shape. |
|
|
52
|
+
| `isStatic` | `boolean` | Static, read-only-styled text. |
|
|
53
|
+
| `isLoading` | `boolean` | Loading state. |
|
|
54
|
+
| `value` / `defaultValue` / `onChange` | — | Standard React input. |
|
|
55
|
+
| `disabled` / `readOnly` | `boolean` | Native states. |
|
|
56
|
+
|
|
57
|
+
`messageColor` accepts `'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'`.
|
|
58
|
+
|
|
59
|
+
## Select / SelectBase, TextArea / TextAreaBase
|
|
60
|
+
|
|
61
|
+
Same convenience/raw split as Input. `Select` supports `isLoading` (on the control), `color`,
|
|
62
|
+
`size`, `isRounded`, `isFullwidth` (lowercase w — Button's is `isFullWidth`), `multiple` +
|
|
63
|
+
`multipleSize`, plus the Field/Control/message props. `TextArea` adds `rows` and
|
|
64
|
+
`hasFixedSize`.
|
|
65
|
+
|
|
66
|
+
## Checkbox / Checkboxes, Radio / Radios
|
|
67
|
+
|
|
68
|
+
- `Checkbox` / `Radio` — single controls with `color`, `size`, `checked`/`defaultChecked`,
|
|
69
|
+
`onChange`, `disabled`.
|
|
70
|
+
- `Checkboxes` — group wrapper managing an **array** value.
|
|
71
|
+
- `Radios` — group wrapper managing a **single** selected value.
|
|
72
|
+
|
|
73
|
+
## Switch — `form/Switch.tsx`
|
|
74
|
+
|
|
75
|
+
`color`, `size`, `checked`/`defaultChecked`, `onChange`, `isRounded`, `isThin`, `isOutlined`,
|
|
76
|
+
`passiveType`, plus RTL support.
|
|
77
|
+
|
|
78
|
+
## File — `form/File.tsx`
|
|
79
|
+
|
|
80
|
+
File input with `label`, `message`, color/size, and icon support.
|
|
81
|
+
|
|
82
|
+
## Advanced inputs
|
|
83
|
+
|
|
84
|
+
| Component | Key props |
|
|
85
|
+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable`, `openOnFocus`, `loading`. |
|
|
87
|
+
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
88
|
+
| `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size`. |
|
|
89
|
+
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
90
|
+
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
91
|
+
|
|
92
|
+
## Date / time inputs
|
|
93
|
+
|
|
94
|
+
`DateInput`, `TimeInput`, `DateTimeInput` (convenience) and their `*Base` variants. Field/Control
|
|
95
|
+
composition like the other convenience inputs, with picker UIs (native with custom fallback).
|
|
96
|
+
|
|
97
|
+
## Validation-related props (no library)
|
|
98
|
+
|
|
99
|
+
There is no validation engine. The props you use to reflect externally-computed validation are:
|
|
100
|
+
|
|
101
|
+
- `color` — `'danger'` for an error, `'success'` for valid.
|
|
102
|
+
- `message` — the help/validation text (rendered as `<p class="help">`).
|
|
103
|
+
- `messageColor` — colors that help text to match (`'danger'`, `'success'`, …).
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Reference: form patterns
|
|
2
|
+
|
|
3
|
+
## A complete multi-field form (controlled + manual validation)
|
|
4
|
+
|
|
5
|
+
No form library — state is plain React, validation is a function, errors surface through
|
|
6
|
+
`color` + `message` + `messageColor`.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import {
|
|
11
|
+
Input,
|
|
12
|
+
Select,
|
|
13
|
+
TextArea,
|
|
14
|
+
Checkbox,
|
|
15
|
+
Button,
|
|
16
|
+
Field,
|
|
17
|
+
} from '@allxsmith/bestax-bulma';
|
|
18
|
+
|
|
19
|
+
interface Values {
|
|
20
|
+
name: string;
|
|
21
|
+
email: string;
|
|
22
|
+
country: string;
|
|
23
|
+
bio: string;
|
|
24
|
+
agree: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function validate(v: Values) {
|
|
28
|
+
const errors: Partial<Record<keyof Values, string>> = {};
|
|
29
|
+
if (!v.name.trim()) errors.name = 'Name is required.';
|
|
30
|
+
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v.email))
|
|
31
|
+
errors.email = 'Enter a valid email.';
|
|
32
|
+
if (!v.country) errors.country = 'Pick a country.';
|
|
33
|
+
if (!v.agree) errors.agree = 'You must accept the terms.';
|
|
34
|
+
return errors;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function ProfileForm() {
|
|
38
|
+
const [values, setValues] = useState<Values>({
|
|
39
|
+
name: '',
|
|
40
|
+
email: '',
|
|
41
|
+
country: '',
|
|
42
|
+
bio: '',
|
|
43
|
+
agree: false,
|
|
44
|
+
});
|
|
45
|
+
const [submitted, setSubmitted] = useState(false);
|
|
46
|
+
|
|
47
|
+
const errors = validate(values);
|
|
48
|
+
const show = (k: keyof Values) => (submitted ? errors[k] : undefined);
|
|
49
|
+
const set = (k: keyof Values, val: Values[keyof Values]) =>
|
|
50
|
+
setValues(prev => ({ ...prev, [k]: val }));
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<form
|
|
54
|
+
onSubmit={e => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
setSubmitted(true);
|
|
57
|
+
if (Object.keys(errors).length === 0) {
|
|
58
|
+
// submit values…
|
|
59
|
+
}
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<Input
|
|
63
|
+
label="Name"
|
|
64
|
+
value={values.name}
|
|
65
|
+
onChange={e => set('name', e.target.value)}
|
|
66
|
+
color={show('name') ? 'danger' : undefined}
|
|
67
|
+
message={show('name')}
|
|
68
|
+
messageColor="danger"
|
|
69
|
+
/>
|
|
70
|
+
|
|
71
|
+
<Input
|
|
72
|
+
label="Email"
|
|
73
|
+
type="email"
|
|
74
|
+
value={values.email}
|
|
75
|
+
onChange={e => set('email', e.target.value)}
|
|
76
|
+
color={show('email') ? 'danger' : undefined}
|
|
77
|
+
message={show('email')}
|
|
78
|
+
messageColor="danger"
|
|
79
|
+
iconLeftName="envelope"
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<Select
|
|
83
|
+
label="Country"
|
|
84
|
+
value={values.country}
|
|
85
|
+
onChange={e => set('country', e.target.value)}
|
|
86
|
+
color={show('country') ? 'danger' : undefined}
|
|
87
|
+
message={show('country')}
|
|
88
|
+
messageColor="danger"
|
|
89
|
+
>
|
|
90
|
+
<option value="">Select…</option>
|
|
91
|
+
<option value="us">United States</option>
|
|
92
|
+
<option value="ca">Canada</option>
|
|
93
|
+
</Select>
|
|
94
|
+
|
|
95
|
+
<TextArea
|
|
96
|
+
label="Bio"
|
|
97
|
+
value={values.bio}
|
|
98
|
+
onChange={e => set('bio', e.target.value)}
|
|
99
|
+
rows={3}
|
|
100
|
+
/>
|
|
101
|
+
|
|
102
|
+
<Field>
|
|
103
|
+
<Checkbox
|
|
104
|
+
checked={values.agree}
|
|
105
|
+
onChange={e => set('agree', e.target.checked)}
|
|
106
|
+
>
|
|
107
|
+
{' '}
|
|
108
|
+
I accept the terms
|
|
109
|
+
</Checkbox>
|
|
110
|
+
{show('agree') && <p className="help is-danger">{show('agree')}</p>}
|
|
111
|
+
</Field>
|
|
112
|
+
|
|
113
|
+
<Button color="primary" type="submit" mt="4">
|
|
114
|
+
Save
|
|
115
|
+
</Button>
|
|
116
|
+
</form>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Notes:
|
|
122
|
+
|
|
123
|
+
- Validation runs every render as a pure function; `submitted` gates when errors are shown so the
|
|
124
|
+
form isn't red before the user interacts. You could gate per-field on blur instead.
|
|
125
|
+
- For the checkbox the convenience `message` prop isn't used; the help text is rendered manually
|
|
126
|
+
inside the `Field` — a good illustration of dropping to composition when needed.
|
|
127
|
+
|
|
128
|
+
## Grouped controls and addons
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
// Search box: input + button attached.
|
|
132
|
+
<Field hasAddons>
|
|
133
|
+
<Control isExpanded>
|
|
134
|
+
<InputBase placeholder="Search" />
|
|
135
|
+
</Control>
|
|
136
|
+
<Control>
|
|
137
|
+
<Button color="primary">Search</Button>
|
|
138
|
+
</Control>
|
|
139
|
+
</Field>
|
|
140
|
+
|
|
141
|
+
// Two controls on one row.
|
|
142
|
+
<Field grouped>
|
|
143
|
+
<Control>
|
|
144
|
+
<InputBase placeholder="First" />
|
|
145
|
+
</Control>
|
|
146
|
+
<Control>
|
|
147
|
+
<InputBase placeholder="Last" />
|
|
148
|
+
</Control>
|
|
149
|
+
</Field>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Horizontal field with explicit label/body
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
<Field horizontal>
|
|
156
|
+
<Field.Label size="normal">Email</Field.Label>
|
|
157
|
+
<Field.Body>
|
|
158
|
+
<Control iconLeftName="envelope" hasIconsLeft>
|
|
159
|
+
<InputBase type="email" placeholder="you@example.com" />
|
|
160
|
+
</Control>
|
|
161
|
+
</Field.Body>
|
|
162
|
+
</Field>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Advanced inputs
|
|
166
|
+
|
|
167
|
+
These are controlled the same way — own the value, pass `value`/`onChange`.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// Autocomplete — onInput fires on typing, onSelect when a suggestion is chosen.
|
|
171
|
+
const [city, setCity] = useState('');
|
|
172
|
+
<Autocomplete
|
|
173
|
+
value={city}
|
|
174
|
+
onInput={setCity}
|
|
175
|
+
onSelect={item =>
|
|
176
|
+
setCity(typeof item === 'string' ? item : (item?.value ?? ''))
|
|
177
|
+
}
|
|
178
|
+
data={['Austin', 'Boston', 'Chicago']}
|
|
179
|
+
openOnFocus
|
|
180
|
+
/>;
|
|
181
|
+
|
|
182
|
+
// Slider (single and dual thumb)
|
|
183
|
+
const [volume, setVolume] = useState(50);
|
|
184
|
+
<Slider value={volume} onChange={setVolume} min={0} max={100} tooltip="auto" />;
|
|
185
|
+
|
|
186
|
+
const [range, setRange] = useState<[number, number]>([20, 80]);
|
|
187
|
+
<Slider range value={range} onChange={setRange} min={0} max={100} />;
|
|
188
|
+
|
|
189
|
+
// Numberinput
|
|
190
|
+
const [qty, setQty] = useState(1);
|
|
191
|
+
<Numberinput value={qty} onChange={setQty} min={1} max={10} step={1} />;
|
|
192
|
+
|
|
193
|
+
// Rate
|
|
194
|
+
const [stars, setStars] = useState(3);
|
|
195
|
+
<Rate value={stars} onChange={setStars} max={5} />;
|
|
196
|
+
|
|
197
|
+
// Taginput — tags can be strings or { value, label } objects; onChange yields TaginputTag[].
|
|
198
|
+
const [tags, setTags] = useState<string[]>(['react']);
|
|
199
|
+
<Taginput
|
|
200
|
+
value={tags}
|
|
201
|
+
onChange={next =>
|
|
202
|
+
setTags(next.map(t => (typeof t === 'string' ? t : t.value)))
|
|
203
|
+
}
|
|
204
|
+
data={['react', 'bulma', 'ts']}
|
|
205
|
+
/>;
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Each renders inside Bulma's field/control structure already, so wrap them in a `Field` only when
|
|
209
|
+
you need a label or grouped layout. Reflect validation on them with the same `color` /
|
|
210
|
+
`message` / `messageColor` approach used for text inputs.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bestax-icons
|
|
3
|
+
description: Use icons in an app built with @allxsmith/bestax-bulma — the Icon/IconText components and the five supported libraries (Font Awesome, Material Design Icons, Ionicons, Google Material Icons, Material Symbols). Use when adding an icon, choosing or configuring the app-wide icon library, fixing an icon that renders blank, pairing icons with text, or making icons accessible (decorative vs labeled).
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Icons with @allxsmith/bestax-bulma
|
|
8
|
+
|
|
9
|
+
`Icon` renders a Bulma icon container (`span.icon`) around a glyph from any of five icon
|
|
10
|
+
libraries behind one normalized API. `IconText` pairs icons with text. The library ships **no
|
|
11
|
+
icon fonts** — the chosen library's package (or CDN script) must be installed in the app.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { ConfigProvider, Icon, IconText } from '@allxsmith/bestax-bulma';
|
|
17
|
+
|
|
18
|
+
// Set the library ONCE at the app root; <Icon> then never needs `library`.
|
|
19
|
+
<ConfigProvider iconLibrary="fa">
|
|
20
|
+
<App />
|
|
21
|
+
</ConfigProvider>;
|
|
22
|
+
|
|
23
|
+
// Inside the app:
|
|
24
|
+
<Icon name="rocket" ariaLabel="Launch" />;
|
|
25
|
+
<IconText iconProps={{ name: 'star', 'aria-hidden': 'true' }}>
|
|
26
|
+
Starred
|
|
27
|
+
</IconText>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## The five libraries
|
|
31
|
+
|
|
32
|
+
| Library | `iconLibrary` / `library` value | Name format | Example `name` |
|
|
33
|
+
| ---------------------- | ------------------------------- | ---------------------------- | --------------- |
|
|
34
|
+
| Font Awesome (default) | `'fa'` | kebab-case, no `fa-` prefix | `rocket` |
|
|
35
|
+
| Material Design Icons | `'mdi'` | kebab-case, no `mdi-` prefix | `rocket-launch` |
|
|
36
|
+
| Ionicons | `'ion'` | kebab-case | `rocket` |
|
|
37
|
+
| Google Material Icons | `'material-icons'` | snake_case (a text ligature) | `rocket_launch` |
|
|
38
|
+
| Material Symbols | `'material-symbols'` | snake_case (a text ligature) | `rocket_launch` |
|
|
39
|
+
|
|
40
|
+
⚠️ **The Ionicons value is `'ion'`, not `'ionicons'`.** The `npm create bestax` scaffold's
|
|
41
|
+
`--icon ionicons` flag maps to `iconLibrary="ion"` — passing `'ionicons'` to `ConfigProvider`
|
|
42
|
+
or `library` silently renders nothing.
|
|
43
|
+
|
|
44
|
+
**The same glyph has a different name per library** (`rocket` vs `rocket-launch` vs
|
|
45
|
+
`rocket_launch`). When an icon renders blank, the name format for the active library is the
|
|
46
|
+
first thing to check. A redundant `fa-`/`mdi-` prefix in `name` is tolerated (stripped), but
|
|
47
|
+
don't rely on it.
|
|
48
|
+
|
|
49
|
+
## Styling
|
|
50
|
+
|
|
51
|
+
- `size` — `'small' | 'medium' | 'large'` sizes the Bulma **container** (`is-small` ≈ 1rem,
|
|
52
|
+
`is-medium` ≈ 2rem, `is-large` ≈ 3rem box). To scale the **glyph**, use `features`
|
|
53
|
+
(Font Awesome `'fa-lg'`/`'fa-2x'`) or a Bulma text-size class (`'is-size-3'`).
|
|
54
|
+
- `variant` — per-library style: Font Awesome `solid` (default) / `regular` / `brands` /
|
|
55
|
+
`light` / `duotone` / `thin`; Material Icons `filled` (default) / `outlined` / `round` /
|
|
56
|
+
`sharp`; Material Symbols `outlined` (default) / `rounded` / `sharp`; Ionicons `outline` /
|
|
57
|
+
`sharp`. MDI has no variants. Note Material **Icons** uses `round`, Material **Symbols**
|
|
58
|
+
uses `rounded`.
|
|
59
|
+
- `features` — extra library classes, string or array: `'fa-spin'`, `['fa-lg', 'fa-border']`.
|
|
60
|
+
- Color via the helper props: `textColor="primary"`, `textColor="danger"`, etc.
|
|
61
|
+
|
|
62
|
+
## Accessibility
|
|
63
|
+
|
|
64
|
+
Every `Icon` renders `aria-label` (default `"icon"`), set via its camelCase `ariaLabel` prop.
|
|
65
|
+
Only a few components declare that prop (`Icon`, `Delete`, `Slider`, `Carousel`) — everything
|
|
66
|
+
else takes the standard `aria-label` attribute, e.g. `<Navbar.Burger aria-label="menu" />`.
|
|
67
|
+
|
|
68
|
+
- **Meaningful icon** (stands alone, conveys information): pass a descriptive
|
|
69
|
+
`ariaLabel="Delete item"`.
|
|
70
|
+
- **Decorative icon** (next to visible text that says the same thing, e.g. inside `IconText`
|
|
71
|
+
or a labeled `Button`): hide it from screen readers with `aria-hidden`:
|
|
72
|
+
`<Icon name="check" aria-hidden="true" />` — otherwise "icon" (or a duplicate label) is
|
|
73
|
+
announced alongside the text.
|
|
74
|
+
|
|
75
|
+
## References
|
|
76
|
+
|
|
77
|
+
- `references/icon-libraries.md` — per-library setup (install/import/CDN), the full
|
|
78
|
+
name-format and variant tables, `features` values, and the blank-icon troubleshooting list.
|
|
79
|
+
- `examples/icon-usage.tsx` — runnable example: ConfigProvider setup, sizes, variants,
|
|
80
|
+
colors, IconText, and decorative-vs-labeled patterns.
|