create-bestax 4.2.16 → 4.2.17
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/dist/constants.js +1 -1
- package/package.json +6 -6
- package/templates/skills/bestax-custom-component/SKILL.md +36 -1
- package/templates/skills/bestax-custom-component/references/api.md +36 -14
- package/templates/skills/bestax-custom-component/references/component-catalog.md +8 -2
- package/templates/skills/bestax-custom-component/references/library-contributor.md +4 -1
- package/templates/skills/bestax-form/SKILL.md +41 -29
- package/templates/skills/bestax-form/references/api.md +12 -10
- package/templates/skills/bestax-form/references/patterns.md +4 -3
- package/templates/skills/bestax-icons/SKILL.md +8 -1
- package/templates/skills/bestax-icons/references/icon-libraries.md +35 -2
- package/templates/skills/bestax-layout-scaffold/SKILL.md +34 -1
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +57 -0
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +34 -8
- package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +18 -0
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +31 -7
- package/templates/skills/bestax-migrate/references/rbx/unmappables.md +41 -14
- package/templates/skills/bestax-migrate/references/react-bulma-components/component-map.md +3 -3
- package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +22 -0
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +33 -8
- package/templates/skills/bestax-optimize/SKILL.md +2 -0
- package/templates/skills/bestax-optimize/references/modular-build.md +19 -10
- package/templates/skills/bestax-theming/SKILL.md +4 -1
- package/templates/skills/bestax-theming/references/css-variables.md +87 -2
- package/templates/skills/bestax-theming/references/themeable-components.md +6 -2
- package/templates/vite-ts/package.json +1 -0
- package/templates/vite-ts/tsconfig.app.json +10 -9
- package/templates/vite-ts/tsconfig.node.json +15 -5
- package/templates/vite-ts/src/vite-env.d.ts +0 -1
package/dist/constants.js
CHANGED
|
@@ -98,7 +98,7 @@ cases. Before writing \`style\`, translate each declaration with this table:
|
|
|
98
98
|
| \`textAlign: 'center'\` | \`textAlign="centered"\` (also \`left\`, \`right\`, \`justified\`) |
|
|
99
99
|
| \`color: '#…'\` | \`textColor\` with the nearest Bulma color: \`primary\`, \`link\`, \`info\`, \`success\`, \`warning\`, \`danger\`, \`white\`, \`black\`, \`grey\` (+ \`grey-light\`, \`grey-dark\`, …) |
|
|
100
100
|
| \`backgroundColor: '#…'\` | \`bgColor\` (same palette) |
|
|
101
|
-
| \`fontSize: …\` | \`textSize="1"\`…\`"7"\` (\`1\` largest)
|
|
101
|
+
| \`fontSize: …\` | \`textSize="1"\`…\`"7"\` (\`1\` largest). On \`Title\`/\`SubTitle\`, \`as\` sets the level and \`size\` picks it too, so an \`<h3>\` at size 4 is \`as="h3" textSize="4"\` |
|
|
102
102
|
| \`fontWeight: …\` | \`textWeight\`: \`light\`, \`normal\`, \`medium\`, \`semibold\`, \`bold\` |
|
|
103
103
|
| \`textTransform\`, italics | \`textTransform\`: \`uppercase\`, \`lowercase\`, \`capitalized\`, \`italic\` |
|
|
104
104
|
| \`display: 'flex'\` + flex properties | same-named props: \`display="flex"\`, \`flexDirection\`, \`justifyContent\`, \`alignItems\`, \`flexWrap\` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bestax",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.17",
|
|
4
4
|
"description": "Create a new bestax-bulma project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,21 +41,21 @@
|
|
|
41
41
|
"prompts": "^2.4.2",
|
|
42
42
|
"react": "^18.0.0 || ^19.0.0",
|
|
43
43
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
44
|
-
"@allxsmith/bestax-bulma": "^5.
|
|
44
|
+
"@allxsmith/bestax-bulma": "^5.34.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@jest/globals": "^30.0.0",
|
|
48
|
-
"@playwright/test": "^1.
|
|
48
|
+
"@playwright/test": "^1.63.0",
|
|
49
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
50
|
"@types/jest": "^30.0.0",
|
|
51
|
-
"@types/node": "^26.
|
|
51
|
+
"@types/node": "^26.6.4",
|
|
52
52
|
"@types/prompts": "^2.4.9",
|
|
53
53
|
"@types/wait-on": "^5.3.4",
|
|
54
54
|
"jest": "^30.2.0",
|
|
55
55
|
"semver": "^7.8.5",
|
|
56
|
-
"ts-jest": "^29.4.
|
|
56
|
+
"ts-jest": "^29.4.14",
|
|
57
57
|
"typescript": "^6.0.3",
|
|
58
|
-
"wait-on": "^9.1
|
|
58
|
+
"wait-on": "^9.5.1"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|
|
@@ -63,10 +63,45 @@ Wrap it in the library's `ClientOnly` rather than checking `typeof window`: it r
|
|
|
63
63
|
until the page has hydrated, so the server and client markup match. `references/api.md` has the
|
|
64
64
|
details.
|
|
65
65
|
|
|
66
|
+
A button that turns something on or off and stays that way (Bold, Mute, Show archived) is
|
|
67
|
+
`ToggleButton`: use it rather than `Button isActive`, which only draws a shade and sets no
|
|
68
|
+
`aria-pressed`. It's always `type="button"`, holds its own state (`defaultPressed`) or takes
|
|
69
|
+
`pressed` with `onPressedChange`, and fills with `color` (default `link`) only while pressed.
|
|
70
|
+
Its label stays the same in both states, and an icon-only one needs an `aria-label`. A set goes
|
|
71
|
+
in `Buttons hasAddons role="group" aria-label="…"`, where it's also `Buttons.ToggleButton`. A row
|
|
72
|
+
where only one can be on is a radio group, so it's `SegmentedControl` (below), never toggles that
|
|
73
|
+
switch each other off. A setting saved with a form is a `Switch` or a `Checkbox`.
|
|
74
|
+
|
|
75
|
+
A row of buttons where only one can be on (text alignment, a list or grid view, monthly or yearly
|
|
76
|
+
billing) is `SegmentedControl`: use it rather than `Buttons hasAddons` with `isActive` or a fill
|
|
77
|
+
you move yourself. Give it an `aria-label`, put one `SegmentedControl.Segment value="…"` inside per
|
|
78
|
+
option, and hold the choice with `defaultValue`, or `value` with `onChange(value)`. A screen reader
|
|
79
|
+
hears a radio group, and the single tab stop, the arrow keys, Home and End come built in, so add
|
|
80
|
+
no `onKeyDown` or `role` of your own. An icon-only segment needs its own `aria-label`, as an
|
|
81
|
+
icon-only `ToggleButton` does. The arrow keys check the segment they land on, so `onChange` fires
|
|
82
|
+
on each, and pressing the checked segment keeps it checked. It carries no form value: a choice
|
|
83
|
+
the form submits is `Radios`.
|
|
84
|
+
|
|
66
85
|
A panel opened from a button (a filter form, share options, an inline edit) is `Popover`: use
|
|
67
86
|
it rather than building one. It anchors the panel, moves focus in and back, and closes on
|
|
68
87
|
Escape and outside presses.
|
|
69
88
|
|
|
89
|
+
A preview that shows up on hover (a profile card on a username, a summary on a link) is
|
|
90
|
+
`HoverCard`: use it rather than wiring pointer events to a `Popover`. It opens on hover and
|
|
91
|
+
keyboard focus after a delay, lets the pointer move onto the card, closes on Escape, and ignores
|
|
92
|
+
touch unless `openOnTap` is on, which suits only a trigger with no tap action of its own (a
|
|
93
|
+
`Button` with no `onClick`, never a link). Its `trigger` has to be a link or a button, and a trigger in running text takes
|
|
94
|
+
`appendToBody`. A form never goes in one; that's `Popover`.
|
|
95
|
+
|
|
96
|
+
A menu of actions that opens on a right-click (Rename and Delete on a table row, Archive on a
|
|
97
|
+
card) is `ContextMenu`: use it rather than an `onContextMenu` handler that positions a
|
|
98
|
+
`Dropdown`. It opens at the point, also on Shift+F10, the context menu key and a long press,
|
|
99
|
+
and its items are `ContextMenu.Item` and `ContextMenu.Divider`, the same components as
|
|
100
|
+
`Dropdown.Item` and `Dropdown.Divider`. Its `trigger` is cloned, not wrapped, so a `Table.Tr`
|
|
101
|
+
works, and the menu needs `aria-label`. Put the same items in a visible `Dropdown` as well:
|
|
102
|
+
nothing tells a screen reader user a context menu is there, and on macOS only VoiceOver opens
|
|
103
|
+
it from the keyboard.
|
|
104
|
+
|
|
70
105
|
Building something else that floats (a command palette)? Render it through the library's
|
|
71
106
|
`Portal` rather than `createPortal`: it renders nothing on the server and during hydration, so
|
|
72
107
|
the server and client markup match. `references/api.md` has the details.
|
|
@@ -139,7 +174,7 @@ the children with `gap`. Before writing `style={{ … }}` anywhere, translate ea
|
|
|
139
174
|
| `textAlign: 'center'` | `textAlign="centered"` (also `left`, `right`, `justified`) |
|
|
140
175
|
| `color: '#…'` | `textColor` with the nearest Bulma color: `primary`, `link`, `info`, `success`, `warning`, `danger`, `white`, `black`, `grey` (+ `grey-light`, `grey-dark`, …) |
|
|
141
176
|
| `backgroundColor: '#…'` | `bgColor` (same palette) |
|
|
142
|
-
| `fontSize: …` | `textSize="1"`…`"7"` (`1` largest)
|
|
177
|
+
| `fontSize: …` | `textSize="1"`…`"7"` (`1` largest). On `Title`/`SubTitle`, `as` sets the level and `size` picks it too, so an `<h3>` at size 4 is `as="h3" textSize="4"` |
|
|
143
178
|
| `fontWeight: …` | `textWeight`: `light`, `normal`, `medium`, `semibold`, `bold` |
|
|
144
179
|
| `textTransform`, italics | `textTransform`: `uppercase`, `lowercase`, `capitalized`, `italic` |
|
|
145
180
|
| `display: 'flex'` + flex properties | same-named props: `display="flex"`, `flexDirection`, `justifyContent`, `alignItems`, `flexWrap` |
|
|
@@ -88,7 +88,8 @@ union (a compile error beats a silent no-op).
|
|
|
88
88
|
|
|
89
89
|
## `ConfigProvider` / `Theme` — `helpers/Config.tsx`, `helpers/Theme.tsx`
|
|
90
90
|
|
|
91
|
-
`ConfigProvider` provides the runtime `classPrefix` (and `iconLibrary`
|
|
91
|
+
`ConfigProvider` provides the runtime `classPrefix` (and `iconLibrary` and `portalContainer`)
|
|
92
|
+
consumed via `useConfig`;
|
|
92
93
|
`classPrefix` feeds `usePrefixedClassNames` (opt-in class prefixing to avoid collisions). `Theme`
|
|
93
94
|
overrides `--bulma-*` custom properties at runtime —
|
|
94
95
|
which is exactly why component SCSS must register its vars via `cv.register-vars` rather than
|
|
@@ -114,17 +115,23 @@ hard-coding values.
|
|
|
114
115
|
## Floating content: `Portal`
|
|
115
116
|
|
|
116
117
|
A panel anchored to the button that opens it is `Popover`, which already portals
|
|
117
|
-
(`appendToBody`), traps focus and dismisses itself
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
(`appendToBody`), traps focus and dismisses itself, a preview that opens on hover is
|
|
119
|
+
`HoverCard`, which portals the same way, and a menu that opens at a right-click or a long press
|
|
120
|
+
is `ContextMenu`, which always portals and places itself at the point. `Portal` is for the
|
|
121
|
+
overlays they don't cover.
|
|
122
|
+
|
|
123
|
+
`<Portal container?>` (`helpers/portal.tsx`) renders its children into `container` (an element or
|
|
124
|
+
a selector), or without one into the `portalContainer` a `ConfigProvider` sets, and
|
|
125
|
+
`document.body` when none is set, so floating content escapes an ancestor's `overflow`,
|
|
122
126
|
`transform` or stacking context. It renders nothing on the server and during hydration, so the
|
|
123
127
|
first client render matches; `disabled` renders in place instead. Use it rather than calling
|
|
124
128
|
`createPortal` yourself, which React's server renderer can't render.
|
|
125
129
|
|
|
126
|
-
Focus follows the DOM, not the React tree: portaled content
|
|
127
|
-
|
|
130
|
+
Focus follows the DOM, not the React tree: portaled content sits in the Tab order where its
|
|
131
|
+
container is, which for the default `document.body` is last, so an overlay you build that takes
|
|
132
|
+
focus moves it in when it opens and back to its trigger when it closes. A `HoverCard` moves no
|
|
133
|
+
focus when it opens, and a `ContextMenu` moves it to its first item and closes on Tab, handing
|
|
134
|
+
focus back. To trap portaled content,
|
|
128
135
|
put the trap's ref on the element inside the `Portal`, as below with
|
|
129
136
|
`useFocusTrap(panelRef, { active: open, restoreFocus: buttonRef })`. A focus trap doesn't cover
|
|
130
137
|
what a `Portal` inside it renders, so render nested overlays inside the trapped element.
|
|
@@ -141,17 +148,28 @@ what a `Portal` inside it renders, so render nested overlays inside the trapped
|
|
|
141
148
|
}
|
|
142
149
|
```
|
|
143
150
|
|
|
151
|
+
Content in `document.body` loses a scoped `Theme`'s variables and a shadow root's styles. In a web
|
|
152
|
+
component or a themed section, wrap the area in a `ConfigProvider` whose `portalContainer` is an
|
|
153
|
+
element inside it, which also covers what the library's components portal, such as `Popover`'s
|
|
154
|
+
`appendToBody` panel or a `Modal` with `portal`. In a shadow root, pass the
|
|
155
|
+
element itself, held in state from a ref callback, since a selector can't reach inside one. What
|
|
156
|
+
moves there is `position: fixed`, so the element must not sit under an ancestor with a
|
|
157
|
+
`transform`, `filter`, `backdrop-filter`, `perspective`, `will-change: transform`, or a `contain`
|
|
158
|
+
that includes `paint` or `layout`: that ancestor becomes the containing block, and overlays and
|
|
159
|
+
anchored panels land against it instead of the viewport.
|
|
160
|
+
|
|
144
161
|
## Holding focus: `useFocusTrap`
|
|
145
162
|
|
|
146
163
|
`useFocusTrap(ref, { active, initialFocusRef, restoreFocus })` (`helpers/useFocusTrap.ts`) moves
|
|
147
164
|
focus into `ref` when `active` turns on, wraps Tab at the first and last tab stops (the ones the
|
|
148
165
|
browser visits, so hidden, disabled, inert and `tabIndex={-1}` elements are skipped and a radio
|
|
149
166
|
group counts once) and restores focus when it turns off. It handles Tab only: wire Escape to
|
|
150
|
-
close
|
|
151
|
-
the
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
close, and call `preventDefault()` on the Escape it closes on, so a `Modal` or `Sidebar` around
|
|
168
|
+
the panel stays open for the next one. Pass the trigger's ref as `restoreFocus` for a panel
|
|
169
|
+
opened from a button. It looks for the container again each time the component calling it
|
|
170
|
+
renders, so `active` can follow `open` alone even when the panel mounts a render later, as
|
|
171
|
+
portaled content can. The example below shows the hook's wiring; for a real filter panel opened
|
|
172
|
+
from a button, use `Popover`, which does all of this.
|
|
155
173
|
|
|
156
174
|
```tsx
|
|
157
175
|
function FilterPanel({ children }: { children: React.ReactNode }) {
|
|
@@ -176,7 +194,11 @@ function FilterPanel({ children }: { children: React.ReactNode }) {
|
|
|
176
194
|
role="dialog"
|
|
177
195
|
aria-label="Filters"
|
|
178
196
|
tabIndex={-1}
|
|
179
|
-
onKeyDown={e =>
|
|
197
|
+
onKeyDown={e => {
|
|
198
|
+
if (e.key !== 'Escape') return;
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
setOpen(false);
|
|
201
|
+
}}
|
|
180
202
|
>
|
|
181
203
|
{children}
|
|
182
204
|
</div>
|
|
@@ -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
|
+
100 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
|
|
@@ -55,6 +55,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
55
55
|
- [Icon](https://bestax.io/docs/api/elements/icon) — The `Icon` component is a Bulma-styled wrapper for displaying icons from various libraries (Font Awesome, Material Design Icons, Ionicons…
|
|
56
56
|
- [IconText](https://bestax.io/docs/api/elements/icontext) — The `IconText` component provides a Bulma-styled horizontal arrangement of one or more `Icon` components and optional text.
|
|
57
57
|
- [Image](https://bestax.io/docs/api/elements/image) — The `Image` component wraps images, iframes, or custom content in a Bulma-styled container, supporting fixed sizes, aspect ratios, rounded corners…
|
|
58
|
+
- [Kbd](https://bestax.io/docs/api/elements/kbd) — The `Kbd` component renders a `<kbd>` drawn as a keycap, for a key the reader presses.
|
|
58
59
|
- [Link](https://bestax.io/docs/api/elements/link) — The `Link` component renders a styled anchor (`<a>`) element with Bulma helper class integration.
|
|
59
60
|
- [LinkButton](https://bestax.io/docs/api/elements/linkbutton) — The `LinkButton` component renders a `<button>` that visually looks like text or a link.
|
|
60
61
|
- [ListItem](https://bestax.io/docs/api/elements/listitem) — The `ListItem` component renders a styled list item (`<li>`) element with Bulma helper class integration.
|
|
@@ -72,6 +73,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
72
73
|
- [Tag](https://bestax.io/docs/api/elements/tag) — The `Tag` component renders a Bulma-styled label or badge.
|
|
73
74
|
- [Tags](https://bestax.io/docs/api/elements/tags) — The `Tags` component groups multiple `Tag` components together in a Bulma-styled container that wraps onto multiple lines by default.
|
|
74
75
|
- [Title](https://bestax.io/docs/api/elements/title) — The `Title` component renders a Bulma-styled title (heading), supporting sizes `1`-`6`, spacing, and rendering as any heading or paragraph element (`h1`-`h6`…
|
|
76
|
+
- [ToggleButton](https://bestax.io/docs/api/elements/togglebutton) — The `ToggleButton` component renders a button that stays pressed until it's pressed again, and says whether it's on through `aria-pressed`.
|
|
75
77
|
- [UnorderedList](https://bestax.io/docs/api/elements/unorderedlist) — The `UnorderedList` component renders a styled unordered list (`<ul>`) element with Bulma helper class integration.
|
|
76
78
|
|
|
77
79
|
## Components
|
|
@@ -84,8 +86,11 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
84
86
|
- [Carousel](https://bestax.io/docs/api/components/carousel) — The `Carousel` component provides an image/content slider with navigation arrows and indicators.
|
|
85
87
|
- [Collapse](https://bestax.io/docs/api/components/collapse) — The `Collapse` component provides an expandable/collapsible content panel.
|
|
86
88
|
- [Collapses](https://bestax.io/docs/api/components/collapses) — The `Collapses` component groups `Collapse` items into an accordion that keeps one item open at a time, or any number with `multiple`.
|
|
89
|
+
- [ContextMenu](https://bestax.io/docs/api/components/contextmenu) — The `ContextMenu` component opens a menu of actions where you right-click an element, such as a table row or a card, or long press it on a touch screen.
|
|
87
90
|
- [Dialog](https://bestax.io/docs/api/components/dialog) — The `Dialog` component provides ready-made confirm and alert dialogs, so a destructive action stays one `await dialog.confirm()` call away.
|
|
88
91
|
- [Dropdown](https://bestax.io/docs/api/components/dropdown) — The `Dropdown` component provides Bulma's versatile dropdown menu for your Bulma React UI.
|
|
92
|
+
- [EmptyState](https://bestax.io/docs/api/components/emptystate) — The `EmptyState` component renders the block an app shows where content would be: an empty list, a search with no results, or a first-run screen.
|
|
93
|
+
- [HoverCard](https://bestax.io/docs/api/components/hovercard) — The `HoverCard` component shows a card previewing what's behind a link or a button while the pointer rests on it or keyboard focus is on it.
|
|
89
94
|
- [Loading](https://bestax.io/docs/api/components/loading) — The `Loading` component provides a loading overlay with a spinner animation.
|
|
90
95
|
- [Menu](https://bestax.io/docs/api/components/menu) — The `Menu` component provides Bulma's vertical navigation menu: a simple, accessible sidebar or section menu for your Bulma React UI.
|
|
91
96
|
- [Message](https://bestax.io/docs/api/components/message) — The `Message` component provides Bulma's flexible notice/message box for your Bulma React UI.
|
|
@@ -95,6 +100,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
95
100
|
- [Panel](https://bestax.io/docs/api/components/panel) — The `Panel` component implements Bulma's versatile panel block for React.
|
|
96
101
|
- [Popover](https://bestax.io/docs/api/components/popover) — The `Popover` component opens a panel of interactive content, such as a filter form or a set of share options, anchored to the element that toggles it.
|
|
97
102
|
- [Reveal](https://bestax.io/docs/api/components/reveal) — The `Reveal` component animates its content into view as it scrolls into the viewport, backed by `IntersectionObserver`.
|
|
103
|
+
- [SegmentedControl](https://bestax.io/docs/api/components/segmentedcontrol) — The `SegmentedControl` component renders a row of buttons where only one can be on, and tells a screen reader it's a radio group.
|
|
98
104
|
- [Sidebar](https://bestax.io/docs/api/components/sidebar) — The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
|
|
99
105
|
- [Steps](https://bestax.io/docs/api/components/steps) — The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.
|
|
100
106
|
- [Tabs](https://bestax.io/docs/api/components/tabs) — The `Tabs` component provides flexible and fully-featured Bulma tab navigation for your Bulma React UI.
|
|
@@ -147,7 +153,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
147
153
|
|
|
148
154
|
- [ClientOnly](https://bestax.io/docs/api/helpers/clientonly) — `ClientOnly` renders its children only in the browser, after hydration, and a `fallback` until then…
|
|
149
155
|
- [ConfigProvider](https://bestax.io/docs/api/helpers/config) — The `ConfigProvider` component provides a React context for configuring global settings across all Bulma UI components.
|
|
150
|
-
- [Portal](https://bestax.io/docs/api/helpers/portal) — `Portal` renders its children into another part of the page, `document.body` unless `container` says otherwise…
|
|
156
|
+
- [Portal](https://bestax.io/docs/api/helpers/portal) — `Portal` renders its children into another part of the page, `document.body` unless `container` or a `ConfigProvider` says otherwise…
|
|
151
157
|
- [Theme](https://bestax.io/docs/api/helpers/theme) — The `Theme` component provides a powerful way to customize Bulma's appearance using CSS custom properties (CSS variables).
|
|
152
158
|
- [Valid value constants](https://bestax.io/docs/api/helpers/valid-values) — The helper tuples enumerate the core accepted values for the shared Bulma helper props — public API for building prop types and validation.
|
|
153
159
|
- [classNames](https://bestax.io/docs/api/helpers/classnames) — `classNames` is a utility function for conditionally joining class names together.
|
|
@@ -168,7 +168,10 @@ Rules that keep components consistent:
|
|
|
168
168
|
place in the DOM uses `Portal` (`helpers/portal.tsx`) rather than its own `createPortal` and
|
|
169
169
|
`typeof document` check. `Portal` renders nothing on the server or during hydration, so the
|
|
170
170
|
first client render matches the server markup. Test that with `renderToString` plus
|
|
171
|
-
`hydrateRoot` and a `console.error` spy, as `portal.test.tsx` does.
|
|
171
|
+
`hydrateRoot` and a `console.error` spy, as `portal.test.tsx` does. A component that has to
|
|
172
|
+
call `createPortal` itself, as `Modal` does to render in place until hydration, resolves its
|
|
173
|
+
target with `usePortalContainer` from the same module, so the `portalContainer` a
|
|
174
|
+
`ConfigProvider` sets reaches it too. Add it to `src/__tests__/portal-container.test.tsx`.
|
|
172
175
|
|
|
173
176
|
- **Focus management builds on `useFocusTrap`.** A new component that holds focus while it is
|
|
174
177
|
open uses `useFocusTrap` (`helpers/useFocusTrap.ts`) rather than its own Tab handler, so it
|
|
@@ -125,30 +125,41 @@ Full-width is `isFullwidth` on every component that supports it (`Button`, `Link
|
|
|
125
125
|
spellings compile only where they historically existed: `isFullWidth` everywhere except
|
|
126
126
|
`Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
`
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`
|
|
150
|
-
|
|
151
|
-
|
|
128
|
+
**Labels.** Default to the convenience `label` prop, with `horizontal` for a side-by-side row.
|
|
129
|
+
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `Slider`, `DateInput`, `TimeInput`,
|
|
130
|
+
`DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label and wire
|
|
131
|
+
`htmlFor`/`id`, to your `id` when you pass one (`<Input label="Email" id="email" horizontal />`);
|
|
132
|
+
an explicit `labelProps={{ htmlFor }}` wins. The group inputs (`Checkboxes`, `Radios`, `Rate`,
|
|
133
|
+
`DateRangeInput`) name their `role="group"`/`"radiogroup"` wrapper through `aria-labelledby`
|
|
134
|
+
instead. Inside a `Field` a convenience input drops its `label`, and an `inline` date/time picker
|
|
135
|
+
or a `Taginput` at `maxTags` has no visible input to wire.
|
|
136
|
+
|
|
137
|
+
Composed, a labeled `Field` names the one control it holds: a composed `InputBase`/`SelectBase`/
|
|
138
|
+
`TextAreaBase`, a composed `DateInputBase`/`TimeInputBase`/`DateTimeInputBase` that is not
|
|
139
|
+
`inline`, or any input above takes its generated id, and a group (a composed `DateRangeInputBase`
|
|
140
|
+
too, `inline` or not) points `aria-labelledby` at the label's own id. **A control with an `id` of
|
|
141
|
+
its own never takes that id**, so `<Field label="Email"><Input id="email" /></Field>` renders a
|
|
142
|
+
label that names nothing: leave the `id` off, or pass the same value as
|
|
143
|
+
`labelProps={{ htmlFor: 'email' }}`. Wire it that way too where the association is skipped, in a
|
|
144
|
+
`grouped`/`hasAddons` row and in a horizontal `Field` whose body holds an inner `Field` (a nested
|
|
145
|
+
`Field` starts its own scope). For a group there, put `labelProps={{ id, htmlFor: undefined }}` on
|
|
146
|
+
the outer `Field` and a matching `aria-labelledby` on the group; the `htmlFor: undefined` keeps an
|
|
147
|
+
unmatched `for` out of server-rendered HTML.
|
|
148
|
+
|
|
149
|
+
A `range` `Slider` points the `for` at its low thumb and starts both thumbs' names with the label
|
|
150
|
+
("Price range Minimum value"); its `ariaLabel={[low, high]}` replaces those names outright, so
|
|
151
|
+
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s or
|
|
152
|
+
`Taginput`'s suggestion list point `aria-labelledby` at the label's `id`, which a label wired by
|
|
153
|
+
hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
154
|
+
fallback names ("Minimum value"/"Maximum value", "Suggestions"). A `<label htmlFor>` you put in
|
|
155
|
+
`Field.Label` yourself (`references/patterns.md`) names its control through the `for` alone, so
|
|
156
|
+
label a row holding either with the `Field`'s `label` prop. An `aria-label` or `aria-labelledby`
|
|
157
|
+
you give a group wins over any label, and a `Checkbox`, `Radio` or `Switch` takes nothing from a
|
|
158
|
+
`Field` on its own: its children name it, and a `Field`'s label names one only when you wire it as
|
|
159
|
+
you would any control with an `id` of its own, `labelProps={{ htmlFor }}` plus that control's
|
|
160
|
+
`id`. A labeled `Field` whose `for` nothing takes (a group, a `Checkbox`, `Radio` or `Switch`, an
|
|
161
|
+
input with an `id` of its own, your own markup) drops it once mounted, though server-rendered HTML
|
|
162
|
+
keeps it. `labelProps={{ htmlFor: undefined }}` opts out of the association entirely.
|
|
152
163
|
|
|
153
164
|
## Convenience vs composed
|
|
154
165
|
|
|
@@ -265,11 +276,12 @@ for the expected classes/states, and say plainly that the visual pass is still o
|
|
|
265
276
|
## Checklist
|
|
266
277
|
|
|
267
278
|
- [ ] Built from the shipped form components (no hand-rolled inputs / reinvented controls).
|
|
268
|
-
- [ ] Every label is programmatically associated
|
|
269
|
-
inputs, and `Field` + single-
|
|
270
|
-
`
|
|
271
|
-
multi-control `Field`'s controls
|
|
272
|
-
a `<label htmlFor>` matching
|
|
279
|
+
- [ ] Every label is programmatically associated: the convenience `label` prop, the group
|
|
280
|
+
inputs, and `Field` + single-control composition do it on their own. A composed labeled
|
|
281
|
+
`Field` names a control with an `id` of its own, or one in an inner `Field`, only when its
|
|
282
|
+
`labelProps={{ htmlFor }}` matches that `id`, and a multi-control `Field`'s controls are
|
|
283
|
+
labeled individually (`aria-label`, `aria-labelledby`, or a `<label htmlFor>` matching
|
|
284
|
+
each control's `id`).
|
|
273
285
|
- [ ] Controlled inputs have both `value` and `onChange` (or use `defaultValue` uncontrolled).
|
|
274
286
|
- [ ] Error state shows via `color="danger"` + `message` + `messageColor="danger"`.
|
|
275
287
|
- [ ] Grouped/addon layouts use explicit `Field` + `Control` composition.
|
|
@@ -8,16 +8,18 @@ via `useBulmaClasses`.
|
|
|
8
8
|
|
|
9
9
|
Container and layout. Compound parts: `Field.Label`, `Field.Body`.
|
|
10
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` | Names the one control it holds: a composed base or convenience input
|
|
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.
|
|
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` | Names the one control it holds: a composed base or convenience input with no `id` of its own through a generated id, a group through `aria-labelledby` (skipped for `grouped`/`hasAddons`; a nested `Field` starts its own scope). |
|
|
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. For its `id`, see below. |
|
|
20
|
+
| `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
|
|
21
|
+
|
|
22
|
+
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete` or `Taginput` suggestion list's `aria-labelledby` points at. A label wired by hand with `htmlFor` gets no generated id, so give it one, `labelProps={{ htmlFor, id }}`, for a range `Slider`, an `Autocomplete` or a `Taginput`. No group points at a label wired by hand.
|
|
21
23
|
|
|
22
24
|
## Control — `form/Control.tsx`
|
|
23
25
|
|
|
@@ -164,9 +164,10 @@ Recipes built on addons, each written out in full in the docs:
|
|
|
164
164
|
- **Split button** ([docs](https://bestax.io/docs/api/components/dropdown#split-button)): a
|
|
165
165
|
`Button` and a `Dropdown` in `Field hasAddons`, one `Control` each. `Buttons hasAddons` does
|
|
166
166
|
not join the Dropdown's trigger. Keep the default color, since the trigger takes no `color`.
|
|
167
|
-
- To label an addon row, put the label on an outer `Field` with `labelProps={{ htmlFor }}`
|
|
168
|
-
matching `id` on the input.
|
|
169
|
-
|
|
167
|
+
- To label an addon row, put the label on an outer `Field` with `labelProps={{ htmlFor, id }}`
|
|
168
|
+
and a matching `id` on the input. The label's `id` names an `Autocomplete`'s or `Taginput`'s
|
|
169
|
+
suggestion list or a range `Slider`'s thumbs, which point `aria-labelledby` at it. A label
|
|
170
|
+
inside the `hasAddons` field sits in the row beside the controls.
|
|
170
171
|
|
|
171
172
|
## Horizontal field with explicit label/body
|
|
172
173
|
|
|
@@ -46,6 +46,12 @@ or `library` silently renders nothing.
|
|
|
46
46
|
first thing to check. A redundant `fa-`/`mdi-` prefix in `name` is tolerated (stripped), but
|
|
47
47
|
don't rely on it.
|
|
48
48
|
|
|
49
|
+
**Material Symbols' font is several megabytes however few icons the app shows.** Keep the
|
|
50
|
+
default `material-symbols/outlined.css` import. When the user cares about download size,
|
|
51
|
+
offer a smaller font as an optimisation (a Google Fonts `icon_names` subset, a self-hosted
|
|
52
|
+
subset, or a single-weight package): `references/icon-libraries.md` has each one and its
|
|
53
|
+
trade-offs.
|
|
54
|
+
|
|
49
55
|
## Styling
|
|
50
56
|
|
|
51
57
|
- `size` — `'small' | 'medium' | 'large'` sizes the Bulma **container** (`is-small` ≈ 1rem,
|
|
@@ -105,6 +111,7 @@ everything else takes the standard `aria-label` attribute, e.g.
|
|
|
105
111
|
## References
|
|
106
112
|
|
|
107
113
|
- `references/icon-libraries.md` — per-library setup (install/import/CDN), the full
|
|
108
|
-
name-format and variant tables, `features` values,
|
|
114
|
+
name-format and variant tables, `features` values, making the Material Symbols font
|
|
115
|
+
smaller, and the blank-icon troubleshooting list.
|
|
109
116
|
- `examples/icon-usage.tsx` — runnable example: ConfigProvider setup, sizes, variants,
|
|
110
117
|
colors, IconText, and decorative-vs-labeled patterns.
|
|
@@ -44,7 +44,7 @@ Set it once on `ConfigProvider` and omit `library` everywhere else.
|
|
|
44
44
|
|
|
45
45
|
## Ionicons — `'ion'` ⚠️ value is `ion`, not `ionicons`
|
|
46
46
|
|
|
47
|
-
- **Setup:** a CDN script
|
|
47
|
+
- **Setup:** a CDN script in `index.html` (no npm package — it registers the
|
|
48
48
|
`<ion-icon>` web component):
|
|
49
49
|
|
|
50
50
|
```html
|
|
@@ -89,6 +89,39 @@ Set it once on `ConfigProvider` and omit `library` everywhere else.
|
|
|
89
89
|
Material Icons' `round`.
|
|
90
90
|
- **Features:** Bulma classes like `'is-size-1'`.
|
|
91
91
|
|
|
92
|
+
### Making the font smaller
|
|
93
|
+
|
|
94
|
+
`outlined.css` loads the whole variable font (every symbol at every fill, weight, grade and
|
|
95
|
+
optical size), several megabytes however few icons the app shows. Keep that import as the
|
|
96
|
+
default: it is one line, works offline, and any name renders. Offer a smaller font only when
|
|
97
|
+
the user cares about download size (a size budget, a Lighthouse flag, slow networks), and
|
|
98
|
+
present it as an optimisation with the trade-offs below:
|
|
99
|
+
|
|
100
|
+
- **One stylesheet per style rendered.** If the app has the bare `import 'material-symbols';`,
|
|
101
|
+
replace it with `outlined.css`, adding `rounded.css` or `sharp.css` only where a `variant`
|
|
102
|
+
uses them. The bare import ships every style's font in the build (a browser fetches each one
|
|
103
|
+
only when a page renders it). This is where the scaffold already stands, and the style left
|
|
104
|
+
is still the whole variable font, which the options below shrink.
|
|
105
|
+
- **Google Fonts subset.** Replace the import with a stylesheet link in `index.html`:
|
|
106
|
+
`<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&icon_names=home,palette,settings&display=block" />`.
|
|
107
|
+
`icon_names` takes the ligature names comma-separated and sorted alphabetically, and keep
|
|
108
|
+
`&display=block` (it stops the names flashing as text). The returned CSS defines the same
|
|
109
|
+
`material-symbols-outlined` class, so components don't change. Costs: the browser fetches
|
|
110
|
+
the icons from Google (privacy, no offline use), and the list must hold every name the app
|
|
111
|
+
renders, since a missing one shows as plain text. `variant="rounded"`/`"sharp"` need
|
|
112
|
+
`Material+Symbols+Rounded`/`Material+Symbols+Sharp` requested too, and an app that sets
|
|
113
|
+
`font-variation-settings` must request those axes in the URL.
|
|
114
|
+
- **Self-hosted subset.** Cut a subset from the package's `.woff2` with a font subsetting tool
|
|
115
|
+
(fontTools' subsetter, for one), keeping the glyphs for the names the app renders, and load
|
|
116
|
+
it with the app's own `@font-face` plus the `.material-symbols-outlined` rules. No request to
|
|
117
|
+
Google, same list upkeep.
|
|
118
|
+
- **Fewer variations.** `@material-symbols/font-400`, from the same project, keeps every
|
|
119
|
+
symbol with weight, grade and optical size fixed, at a fraction of the size. Import
|
|
120
|
+
`@material-symbols/font-400/outlined.css` in place of `material-symbols/outlined.css`; it
|
|
121
|
+
defines the same class, and there is no list to maintain.
|
|
122
|
+
|
|
123
|
+
Docs: https://bestax.io/docs/guides/getting-started/alternative-icons#making-the-font-smaller
|
|
124
|
+
|
|
92
125
|
## One glyph, five names
|
|
93
126
|
|
|
94
127
|
| Glyph | fa | mdi | ion | material-icons / material-symbols |
|
|
@@ -105,7 +138,7 @@ Set it once on `ConfigProvider` and omit `library` everywhere else.
|
|
|
105
138
|
2. **Name format for that library** — kebab vs snake_case (see the table above); for
|
|
106
139
|
material-* a wrong name renders as literal text instead of a glyph.
|
|
107
140
|
3. **The library's CSS/script is actually loaded** — the import in `main.tsx` (or the
|
|
108
|
-
Ionicons
|
|
141
|
+
Ionicons script in `index.html`) must exist; bestax ships none of them.
|
|
109
142
|
4. **Variant availability** — Font Awesome free has no `light`/`duotone`/`thin`; brand
|
|
110
143
|
glyphs need `variant="brands"`.
|
|
111
144
|
|
|
@@ -59,13 +59,44 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
59
59
|
`aria-controls`/`aria-labelledby` links come built in, so add no `onKeyDown` or ARIA
|
|
60
60
|
of your own; `onChange` fires when a tab is activated, never on
|
|
61
61
|
arrow-key focus), `Dropdown active/onActiveChange`,
|
|
62
|
+
`ToggleButton pressed/defaultPressed onPressedChange` for a button that stays on or off (not
|
|
63
|
+
`Button isActive`, which sets no `aria-pressed`; the label stays the same in both states; a
|
|
64
|
+
set goes in `Buttons hasAddons role="group"` with an `aria-label`, where it's also
|
|
65
|
+
`Buttons.ToggleButton`, and a row where only one can be on is `SegmentedControl`),
|
|
66
|
+
`SegmentedControl aria-label="…" value/defaultValue onChange` for a row of buttons where
|
|
67
|
+
only one can be on, such as a list or grid view (one `SegmentedControl.Segment value="…"` per
|
|
68
|
+
option; a radio group with the single tab stop and the arrow keys built in, so add no
|
|
69
|
+
`onKeyDown` of your own; an icon-only segment needs its own `aria-label`; the arrows check as
|
|
70
|
+
they move, so `onChange(value)` fires on each, and it can't be cleared; it carries no form
|
|
71
|
+
value, so a choice a form submits is `Radios`),
|
|
62
72
|
`Popover trigger={<Button>…</Button>} open/defaultOpen onOpenChange` (`trigger` is one
|
|
63
73
|
element, not a node: Popover clones it to add the click and ARIA, so never wrap it in a
|
|
64
74
|
clickable of your own; build the panel from `Popover.Header`/`Body`/`Footer`/`Close`, and a
|
|
65
75
|
panel without a `Popover.Header` needs `ariaLabel`),
|
|
76
|
+
`HoverCard trigger={<Link href="…">…</Link>}` for a preview on hover or keyboard focus
|
|
77
|
+
(`trigger` is one link or button, cloned the same way; the card's content goes straight in as
|
|
78
|
+
children with no sub-components; a trigger inside a `Paragraph` needs `appendToBody`; hover
|
|
79
|
+
and focus only unless `openOnTap` is set, which suits only a trigger with no tap action of its
|
|
80
|
+
own, such as a `Button` with no `onClick`; on touch a link trigger just follows the link, so
|
|
81
|
+
what the card shows must be on that page too, and a form is a `Popover`),
|
|
82
|
+
`ContextMenu aria-label="…" trigger={<Table.Tr>…</Table.Tr>}` for actions on a right-click
|
|
83
|
+
or a long press (`trigger` is one element, a row or a card, cloned rather than wrapped; the
|
|
84
|
+
items are `ContextMenu.Item`/`ContextMenu.Divider`, which are `Dropdown`'s; no `open` prop,
|
|
85
|
+
`onOpenChange` reports it; always put the same items in a visible `Dropdown` on the row too,
|
|
86
|
+
since a context menu is only a shortcut),
|
|
66
87
|
`Steps value={i}/onStepClick items={[{label, icon?}]}` (child form is `Steps.Step`, not
|
|
67
88
|
`Steps.Item`). `Reveal cascade` staggers only its **direct children** — to stagger a grid,
|
|
68
89
|
put `<Reveal delay={i * 80}>` inside each `Cell`, not around the container.
|
|
90
|
+
- **An empty list, table or search with no results is `EmptyState`**, not a hand-built `Box`
|
|
91
|
+
of `Icon`, `Title` and `Buttons isCentered`:
|
|
92
|
+
`<EmptyState icon="folder-open" title="No projects yet" titleAs="h3" description="…">` with
|
|
93
|
+
its one `<Button>` as a child (two or more go in `EmptyState.Actions`). Its `title` is always a
|
|
94
|
+
heading, so set `titleAs` to the level under the list's own heading. It has no live region:
|
|
95
|
+
after a search or a filter, keep a `Paragraph` with `role="status"`, `aria-atomic="true"` and
|
|
96
|
+
`visibility="sr-only"` beside the search field the whole time, saying the count, and have
|
|
97
|
+
"Clear search" put focus back in the field. While data loads show a `Skeleton` or a `Loader`,
|
|
98
|
+
and a failed load is a `Message` or a `Notification`, never an EmptyState. The archetypes
|
|
99
|
+
reference has the full pattern under Card grid.
|
|
69
100
|
- Link lists (footer nav, sidebars): a bare `UnorderedList` of `ListItem`s is already
|
|
70
101
|
marker-less and flush — Bulma's reset unstyles `ul` — so no prop or CSS is needed;
|
|
71
102
|
bullets appear only inside `Content`.
|
|
@@ -160,7 +191,7 @@ background comes from the semantic `color` prop, not `bgColor`.
|
|
|
160
191
|
| `textAlign: 'center'` | `textAlign="centered"` (also `left`, `right`, `justified`) |
|
|
161
192
|
| `color: '#…'` | `textColor` with the nearest Bulma color: `primary`, `link`, `info`, `success`, `warning`, `danger`, `white`, `black`, `grey` (+ `grey-light`, `grey-dark`, …) |
|
|
162
193
|
| `backgroundColor: '#…'` | `bgColor` (same palette) |
|
|
163
|
-
| `fontSize: …` | `textSize="1"`…`"7"` (`1` largest)
|
|
194
|
+
| `fontSize: …` | `textSize="1"`…`"7"` (`1` largest). On `Title`/`SubTitle`, `as` sets the level and `size` picks it too, so an `<h3>` at size 4 is `as="h3" textSize="4"` |
|
|
164
195
|
| `fontWeight: …` | `textWeight`: `light`, `normal`, `medium`, `semibold`, `bold` |
|
|
165
196
|
| `textTransform`, italics | `textTransform`: `uppercase`, `lowercase`, `capitalized`, `italic` |
|
|
166
197
|
| `display: 'flex'` + flex properties | same-named props: `display="flex"`, `flexDirection`, `justifyContent`, `alignItems`, `flexWrap` |
|
|
@@ -200,6 +231,8 @@ inline `style`.
|
|
|
200
231
|
- [ ] Do not use `Tile` — it is not shipped.
|
|
201
232
|
- [ ] Action feedback goes through `Toast`, a confirmation through `Dialog`, a text-styled
|
|
202
233
|
action through `LinkButton` — not `Notification`, `Modal` or a bare `<a>`.
|
|
234
|
+
- [ ] An empty list or a search with no results renders `EmptyState`, and a search or a filter
|
|
235
|
+
keeps a `role="status"` region beside its field that says the count.
|
|
203
236
|
- [ ] Style with helper props, not inline `style` — translate via the mapping table; values
|
|
204
237
|
with no helper get a named class in the stylesheet, never `style={{}}`. No raw Bulma
|
|
205
238
|
`className`s either (`Span`/`Paragraph` wrap bare text; `Th`/`Td` take `textAlign`/`textWeight`).
|