create-bestax 4.2.12 → 4.2.13

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.
Files changed (47) hide show
  1. package/README.md +11 -2
  2. package/dist/cli.d.ts +2 -2
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +3 -2
  5. package/dist/constants.d.ts +7 -1
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +18 -3
  8. package/dist/file-system.d.ts +7 -0
  9. package/dist/file-system.d.ts.map +1 -1
  10. package/dist/file-system.js +15 -0
  11. package/dist/project-creator.d.ts +21 -1
  12. package/dist/project-creator.d.ts.map +1 -1
  13. package/dist/project-creator.js +84 -51
  14. package/dist/prompts.d.ts +7 -2
  15. package/dist/prompts.d.ts.map +1 -1
  16. package/dist/prompts.js +8 -3
  17. package/dist/validators.d.ts +7 -0
  18. package/dist/validators.d.ts.map +1 -1
  19. package/dist/validators.js +13 -1
  20. package/package.json +6 -3
  21. package/templates/skills/bestax-custom-component/SKILL.md +21 -4
  22. package/templates/skills/bestax-custom-component/examples/stat-card.tsx +1 -2
  23. package/templates/skills/bestax-custom-component/references/api.md +103 -11
  24. package/templates/skills/bestax-custom-component/references/component-catalog.md +12 -5
  25. package/templates/skills/bestax-custom-component/references/library-contributor.md +23 -0
  26. package/templates/skills/bestax-form/SKILL.md +28 -17
  27. package/templates/skills/bestax-form/references/api.md +83 -23
  28. package/templates/skills/bestax-form/references/patterns.md +19 -0
  29. package/templates/skills/bestax-layout-scaffold/SKILL.md +17 -4
  30. package/templates/skills/bestax-layout-scaffold/references/layout-components.md +22 -15
  31. package/templates/skills/bestax-migrate/SKILL.md +12 -7
  32. package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +9 -9
  33. package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +19 -2
  34. package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +285 -41
  35. package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +125 -48
  36. package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +217 -39
  37. package/templates/skills/bestax-migrate/references/rbx/component-map.md +15 -15
  38. package/templates/skills/bestax-migrate/references/rbx/prop-map.md +17 -19
  39. package/templates/skills/bestax-migrate/references/rbx/unmappables.md +17 -0
  40. package/templates/skills/bestax-migrate/references/react-bulma-components/component-map.md +34 -34
  41. package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +34 -7
  42. package/templates/skills/bestax-optimize/SKILL.md +3 -2
  43. package/templates/skills/bestax-optimize/references/modular-build.md +25 -5
  44. package/templates/skills/bestax-theming/SKILL.md +4 -3
  45. package/templates/skills/bestax-theming/examples/dark-mode.tsx +4 -3
  46. package/templates/skills/bestax-theming/references/css-variables.md +49 -8
  47. package/templates/skills/bestax-theming/references/themeable-components.md +12 -10
@@ -40,28 +40,29 @@ Wraps a single input; adds icons and loading.
40
40
  (`label`, `labelSize`, `labelProps`, `horizontal`), Control-level (`iconLeftName`,
41
41
  `iconRightName`, `iconLeftSize`, `iconRightSize`, `hasIconsLeft`, `hasIconsRight`, `isLoading`,
42
42
  `isExpanded`, `controlSize`), message (`message`, `messageColor`), and container class overrides
43
- (`fieldClassName`, `controlClassName`).
43
+ (`fieldClassName`, `controlClassName`). Inside your own `Control` it renders no `Control` of its
44
+ own, so the Control-level props and `controlClassName` do nothing there and warn in development;
45
+ set them on that `Control` instead.
44
46
 
45
47
  `InputBase` props:
46
48
 
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. |
49
+ | Prop | Type | Notes |
50
+ | ------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------- |
51
+ | `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state. |
52
+ | `size` | `'small' \| 'medium' \| 'large'` | Size. |
53
+ | `isRounded` | `boolean` | Pill shape. |
54
+ | `isStatic` | `boolean` | Static-text styling; add `readOnly` to stop edits. |
55
+ | `isLoading` | `boolean` | No spinner here; set `isLoading` on the `Control`. |
56
+ | `value` / `defaultValue` / `onChange` | — | Standard React input. |
57
+ | `disabled` / `readOnly` | `boolean` | Native states. |
56
58
 
57
59
  `messageColor` accepts `'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'`.
58
60
 
59
61
  ## Select / SelectBase, TextArea / TextAreaBase
60
62
 
61
- Same convenience/raw split as Input. `Select` supports `isLoading` (on the control), `color`,
62
- `size`, `isRounded`, `isFullwidth`, `multiple` +
63
- `multipleSize`, plus the Field/Control/message props. `TextArea` adds `rows` and
64
- `hasFixedSize`.
63
+ Same convenience/raw split as Input. `Select` supports `isLoading` (a spinner in place of the
64
+ chevron), `color`, `size`, `isRounded`, `isFullwidth`, `multiple` + `multipleSize`, plus the
65
+ Field/Control/message props. `TextArea` adds `rows` and `hasFixedSize`.
65
66
 
66
67
  ## Checkbox / Checkboxes, Radio / Radios
67
68
 
@@ -81,18 +82,77 @@ File input with `label`, `message`, color/size, and icon support.
81
82
 
82
83
  ## Advanced inputs
83
84
 
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`. |
85
+ | Component | Key props |
86
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
87
+ | `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`. |
88
+ | `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
89
+ | `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). |
90
+ | `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
91
+ | `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
91
92
 
92
93
  ## Date / time inputs
93
94
 
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).
95
+ `DateInput`, `TimeInput`, `DateTimeInput`, `DateRangeInput` (convenience) and their `*Base`
96
+ variants. Field/Control composition like the other convenience inputs, with picker UIs (native
97
+ with custom fallback). An `inline` picker renders no `Control` anywhere, so leave its
98
+ Control-level props out: they do nothing there and warn in development.
99
+
100
+ Every one of them has a launcher button at the right edge (`triggerIcon`) that gives way to the loading
101
+ spinner of the `Control` they sit in, which shares that edge. A convenience input sits in the
102
+ `Control` it renders for its own `isLoading`, unless it is inside your own `Control`; a `*Base`
103
+ variant only ever sits in yours. Inside your own `Control`, put `isLoading` on that `Control`: the
104
+ convenience input renders no `Control` of its own there, so its own `isLoading` draws nothing and
105
+ warns in development.
106
+ Passing `triggerIcon` explicitly overrides this.
107
+
108
+ ```tsx
109
+ <Field label="Date">
110
+ <Control isLoading>
111
+ <DateInput />
112
+ </Control>
113
+ </Field>
114
+ ```
115
+
116
+ For a value that is a month or a year (card expiry, billing period, graduation year), use
117
+ `DateInput` with `granularity="month"` or `granularity="year"` rather than building a select or a
118
+ separate picker. The value is still a `Date`: the first day of the month, or 1 January of the
119
+ year. The default `format` becomes `'YYYY-MM'` or `'YYYY'`, and `min` / `max` /
120
+ `shouldDisableDate` judge whole periods (a month is selectable while any day in it is).
121
+
122
+ ```tsx
123
+ <DateInput
124
+ label="Card expiry"
125
+ granularity="month"
126
+ format="MM/YY"
127
+ min={new Date()}
128
+ value={expiry}
129
+ onChange={setExpiry}
130
+ />
131
+ ```
132
+
133
+ For a start and an end (a stay, a report period, a leave request), use `DateRangeInput`
134
+ rather than two `DateInput`s with hand-wired `min`/`max`. Its value is a `[start, end]` pair
135
+ of `Date | null`, kept in order. The calendar picks the start, then the end, and reports
136
+ only finished ranges; typing reports each end as it changes, so handle `[start, null]`.
137
+ `min`, `max`, `shouldDisableDate` and `unselectableDates` apply to both ends, and a range
138
+ can't include a disabled day unless you pass `allowDisabledInRange`. With a `name`, two
139
+ hidden inputs submit `name[start]` and `name[end]` as `YYYY-MM-DD` (`startName` / `endName`
140
+ rename them). `label` names the whole field as a group; the two inputs are named by
141
+ `labels.rangeStart` / `labels.rangeEnd`.
142
+
143
+ ```tsx
144
+ const [stay, setStay] = useState<DateRangeValue>([null, null]);
145
+
146
+ <DateRangeInput
147
+ label="Stay"
148
+ name="stay"
149
+ min={new Date()}
150
+ value={stay}
151
+ onChange={setStay}
152
+ color={stay[0] && !stay[1] ? 'danger' : undefined}
153
+ message={stay[0] && !stay[1] ? 'Pick a check-out date.' : undefined}
154
+ />;
155
+ ```
96
156
 
97
157
  ## Validation-related props (no library)
98
158
 
@@ -149,6 +149,25 @@ Notes:
149
149
  </Field>
150
150
  ```
151
151
 
152
+ Recipes built on addons, each written out in full in the docs:
153
+
154
+ - **Password reveal** ([docs](https://bestax.io/docs/api/form/input#password-reveal)): the
155
+ toggle is a `Button` in its own addon `Control`, never `iconRight`. Bulma gives control icons
156
+ `pointer-events: none`, so a click on one lands on the input. The button takes
157
+ `type="button"`, a fixed `aria-label` ("Show password") and `aria-pressed`, and switches the
158
+ input's `type` between `password` and `text`.
159
+ - **Copy button** ([docs](https://bestax.io/docs/api/form/input#copy-button)): a read-only input
160
+ and a `Button` calling `navigator.clipboard.writeText`. The clipboard is missing outside secure
161
+ contexts and the write can be refused, so catch the failure, select the text, and say so in a
162
+ `role="alert"` region. Confirm success in a `role="status"` region. Mount both regions empty,
163
+ before their text arrives.
164
+ - **Split button** ([docs](https://bestax.io/docs/api/components/dropdown#split-button)): a
165
+ `Button` and a `Dropdown` in `Field hasAddons`, one `Control` each. `Buttons hasAddons` does
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 }}` and a
168
+ matching `id` on the input. A label inside the `hasAddons` field sits in the row beside the
169
+ controls.
170
+
152
171
  ## Horizontal field with explicit label/body
153
172
 
154
173
  ```tsx
@@ -46,10 +46,23 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
46
46
  - Rely on Bulma's responsive defaults: `Columns` sit side by side on tablet and up and stack on
47
47
  mobile. Add responsive `size*` props only to tune the breakpoints.
48
48
  - Interactive extras don't share a state API — never transfer one by analogy:
49
- `Collapse trigger={node} open/defaultOpen onOpen/onClose`, `Tabs value={i}/onChange`
49
+ `Collapse trigger={node} open/defaultOpen onOpenChange` (`onOpenChange(open)` reports every
50
+ trigger; `onOpen/onClose` fire only when the Collapse holds its own state), an accordion is
51
+ `Collapses value/defaultValue/onChange` around plain `Collapse` children indexed from 0 by
52
+ position (`number | null`, or `number[]` with `multiple`; hide an item with
53
+ `visibility="hidden"`, never `{cond && …}`, or the open state shifts to its neighbour) rather
54
+ than hand-rolled open-index state,
55
+ `Tabs value={i}/onChange`
50
56
  (each `Tabs.Tab`/`Tabs.Content.Item` requires `index={i}`, and `Tabs.Content` must be a
51
57
  **child of `<Tabs>`** — the active-tab context lives on it; a sibling panel never
52
- switches), `Dropdown active/onActiveChange`,
58
+ switches. The arrow keys, Home/End, Enter/Space, the single tab stop and the
59
+ `aria-controls`/`aria-labelledby` links come built in, so add no `onKeyDown` or ARIA
60
+ of your own; `onChange` fires when a tab is activated, never on
61
+ arrow-key focus), `Dropdown active/onActiveChange`,
62
+ `Popover trigger={<Button>…</Button>} open/defaultOpen onOpenChange` (`trigger` is one
63
+ element, not a node: Popover clones it to add the click and ARIA, so never wrap it in a
64
+ clickable of your own; build the panel from `Popover.Header`/`Body`/`Footer`/`Close`, and a
65
+ panel without a `Popover.Header` needs `ariaLabel`),
53
66
  `Steps value={i}/onStepClick items={[{label, icon?}]}` (child form is `Steps.Step`, not
54
67
  `Steps.Item`). `Reveal cascade` staggers only its **direct children** — to stagger a grid,
55
68
  put `<Reveal delay={i * 80}>` inside each `Cell`, not around the container.
@@ -121,7 +134,7 @@ every miss shipped the "not this" column instead.
121
134
 
122
135
  | You need | Use | Not this |
123
136
  | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
124
- | A brief confirmation after an action, self-dismissing | `Toast` — mount `<ToastContainer position="top-right" />` once at the app root, then `toast.success('Saved')` (also `.danger`/`.warning`/`.info`) from anywhere | `Notification`/`Message` — static page elements you have to place, position and dismiss yourself |
137
+ | A brief confirmation after an action, self-dismissing | `Toast` — mount `<ToastContainer position="top-right" />` once at the app root, then `toast.success('Saved')` (also `.danger`/`.warning`/`.info`) from anywhere | `Notification`/`Message` |
125
138
  | A confirm or alert the user must answer before anything | `Dialog` — mount `<DialogContainer />` at the root, then `if (await dialog.confirm({ title, message })) …` | `Modal` — an empty shell; the title, message, button row and confirm/cancel wiring are all yours to rebuild |
126
139
  | A control that reads as text or a link but _does_ something | `LinkButton` (`variant="text" \| "ghost" \| "underline"`, optional `color`) | `<a href="#">`/`<div onClick>` (no keyboard or screen-reader support) or `Button color="text"` (still button-shaped) |
127
140
 
@@ -153,7 +166,7 @@ background comes from the semantic `color` prop, not `bgColor`.
153
166
  | `display: 'flex'` + flex properties | same-named props: `display="flex"`, `flexDirection`, `justifyContent`, `alignItems`, `flexWrap` |
154
167
  | `height: '100%'` on a flex child | `flexGrow="1"` |
155
168
  | `display: 'none'` | `visibility="hidden"`, or responsive `display*` props (`displayMobile`, `displayTablet`, …) |
156
- | `gap: …` in a flex layout | no `gap` helper exists — space children with `m*` margins; `Grid` and `Columns` take a `gap` prop, so prefer those there |
169
+ | `gap: '1rem'` (flex or grid) | `gap="2"`, or one axis: `columnGap`, `rowGap`; gap scale: `1`=0.5rem, `2`=1rem, `4`=2rem, half steps (`"1.5"`) |
157
170
 
158
171
  No helper matches (e.g. `maxWidth`, a one-off gradient)? Add a named class to the project
159
172
  stylesheet (`src/App.css` in a scaffolded app) and pass it via `className` — still never
@@ -97,15 +97,15 @@ The responsive grid. `Columns` is the row; `Column` is a cell (also reachable as
97
97
 
98
98
  **Columns**
99
99
 
100
- | Prop | Type |
101
- | -------------------------------------------------------------------------------- | -------------------------------------------------------- |
102
- | `isMultiline` | `boolean` (wrap cells onto new rows) |
103
- | `isCentered` | `boolean` (center the row) |
104
- | `isVCentered` | `boolean` (vertical centering — capital V) |
105
- | `isGapless` | `boolean` |
106
- | `isMobile` | `boolean` (stay side-by-side on mobile) |
107
- | `isDesktop` | `boolean` |
108
- | `gap` / `gapMobile` / `gapTablet` / `gapDesktop` / `gapWidescreen` / `gapFullhd` | `0`–`8` (number or string, same scale as `Grid`'s `gap`) |
100
+ | Prop | Type |
101
+ | -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
102
+ | `isMultiline` | `boolean` (wrap cells onto new rows) |
103
+ | `isCentered` | `boolean` (center the row) |
104
+ | `isVCentered` | `boolean` (vertical centering — capital V) |
105
+ | `isGapless` | `boolean` |
106
+ | `isMobile` | `boolean` (stay side-by-side on mobile) |
107
+ | `isDesktop` | `boolean` |
108
+ | `gap` / `gapMobile` / `gapTablet` / `gapDesktop` / `gapWidescreen` / `gapFullhd` | `0`–`8` whole steps (number or string), the columns gutter |
109
109
 
110
110
  **Column**
111
111
 
@@ -182,12 +182,12 @@ per-breakpoint column _sizes_ (a 2/3 + 1/3 split, different counts per breakpoin
182
182
 
183
183
  **Grid**
184
184
 
185
- | Prop | Type |
186
- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
187
- | `gap` / `columnGap` / `rowGap` | `0`–`8` (number or string, same scale as Columns' `gap`) |
188
- | `minCol` | `1`–`32` (smart grid: min column width step, `is-col-min-X`) |
189
- | `isFixed` | `boolean` (fixed column count instead of auto-fill) |
190
- | `fixedCols` (+ `fixedColsMobile` / `fixedColsTablet` / `fixedColsDesktop` / `fixedColsWidescreen` / `fixedColsFullhd`) | `0`–`12` or `'auto'` (fixed grid only) |
185
+ | Prop | Type |
186
+ | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
187
+ | `gap` / `columnGap` / `rowGap` | `0`–`8` with half steps such as `1.5` (number or string), the same values as the `gap` helper props |
188
+ | `minCol` | `1`–`32` (smart grid: min column width step, `is-col-min-X`) |
189
+ | `isFixed` | `boolean` (fixed column count instead of auto-fill) |
190
+ | `fixedCols` (+ `fixedColsMobile` / `fixedColsTablet` / `fixedColsDesktop` / `fixedColsWidescreen` / `fixedColsFullhd`) | `0`–`12` or `'auto'` (fixed grid only) |
191
191
 
192
192
  **Cell**
193
193
 
@@ -282,3 +282,10 @@ subcomponents (`Card.Header` + `.Title`/`.Icon`, `Card.Image`, `Card.Content`, `
282
282
  | `footer` | `ReactNode \| ReactNode[]` |
283
283
  | `hasShadow` | `boolean` (default `true`) |
284
284
  | `headerCentered` / `headerIcon` | `boolean` / `ReactNode` |
285
+
286
+ `Card.Header.Title` and `Card.FooterItem` both take an `as` prop for Bulma's own card markup —
287
+ `Card.Header.Title` renders `div` by default (`as="p"` or a heading `h2`-`h4` also work), and
288
+ `Card.FooterItem` renders `span` by default (`as="a"` or `as="button"` also work). For a footer
289
+ action, prefer `<Card.FooterItem as="a" href="...">Save</Card.FooterItem>` over nesting a link
290
+ inside the default `span` — Bulma puts the padding and flex centering on `.card-footer-item`
291
+ itself, so nesting a link only makes the text clickable instead of the whole cell.
@@ -73,9 +73,12 @@ Run these steps in order. Don't hand-convert what the codemod converts automatic
73
73
 
74
74
  4. **Resolve every TODO**: `grep -rn "TODO(bestax-migrate)" src/`. Each comment names the
75
75
  prop/component and a hint. Recipes for every recurring case are in
76
- `references/<source>/unmappables.md`; the full tables are in
77
- `references/<source>/component-map.md` and `references/<source>/prop-map.md`. Delete each
78
- comment as you resolve it.
76
+ `references/<source>/unmappables.md`. Before writing a component by hand, look it up in
77
+ `references/<source>/component-map.md` and its classes in `references/<source>/prop-map.md`:
78
+ they name the component and the prop for each class, which the library's type declarations
79
+ only tell you slowly. For form markup (`.field`, `.control`, inputs, selects), load the
80
+ `bestax-form` skill too: it covers the label, `id` and help-text wiring. Delete each comment
81
+ as you resolve it.
79
82
 
80
83
  5. **Finish the stylesheet layer** — flagged Sass cases (computed variables,
81
84
  indented-syntax `.sass` files), CSS flavor choice, and Bulma 0.9→1 styling changes:
@@ -111,16 +114,18 @@ code per the references, or deliberately keep the old markup with `className` st
111
114
  - **bulma-classes**: there is no library to remove. An element converts only when the bestax
112
115
  component renders the same markup (tag, classes, attributes), so a converted page renders
113
116
  the same HTML; everything else stays as written, with a TODO when there is something to
114
- decide. Only static class strings convert (`clsx(...)` and ternaries get
115
- `dynamic-class:<Target>`, naming the component the element would become), files a Next.js
117
+ decide. Static class strings convert, and so does a `clsx` or `classnames` call of strings
118
+ and conditional classes, where a condition on a flag becomes its prop (`isLoading={busy}`);
119
+ any other computed className gets `dynamic-class:<Target>`, naming the component the element
120
+ would become. Files a Next.js
116
121
  App Router may render as server components are left alone (`rsc`), and the manifest gains
117
122
  `@allxsmith/bestax-bulma` while the app's Bulma and stylesheets stay (the default
118
123
  `--css keep`). Its rules are `kind:<name>` shaped; see
119
124
  `references/bulma-classes/unmappables.md`.
120
125
  - **bloomer**: every export is a flat name and most become dotted bestax compounds
121
126
  (`CardHeaderTitle` → `Card.Header.Title`). Most of its `is*` booleans already are bestax's
122
- (`isActive` becomes `active` where bestax names it so; `isFullWidth` survives only on
123
- Button, Select, Table and Tabs); the work is in `isSize`/`isColor`/`isAlign` (renamed per component), the `isDisplay`/`isHidden`
127
+ (`isActive` becomes `active` where bestax names it so; `isFullWidth` becomes `isFullwidth`
128
+ where bestax has the prop); the work is in `isSize`/`isColor`/`isAlign` (renamed per component), the `isDisplay`/`isHidden`
124
129
  helpers (flattened, arrays and objects included), `tag` (→ `as` where bestax has one) and
125
130
  `render` (always a TODO). Its icons are className-based and Font Awesome 4 — the biggest
126
131
  visual risk, see `references/bloomer/unmappables.md`. The app declared its own Bulma 0.6,
@@ -4,7 +4,7 @@ Most of bloomer's boolean modifiers are already bestax's names — `isLoading`,
4
4
  `isInverted`, `isStatic`, `isHovered`, `isFocused`, `isBordered`, `isStriped`, `isNarrow`,
5
5
  `isMultiline`, `isVCentered`, `isMobile`, … pass through untouched. The exceptions are in
6
6
  the tables below: `isActive` becomes `active` on the components whose bestax counterpart
7
- names it that way, `isFullWidth` survives only where bestax declares it, and a few
7
+ names it that way, `isFullWidth` becomes `isFullwidth` where bestax declares it, and a few
8
8
  (`Input isActive`, `NavbarLink isActive`, `PageLink isActive`) have no counterpart and become
9
9
  the Bulma class in `className` instead (see the end of this page). What else changes is the value-carrying props, the helper props every component
10
10
  inherited from `withHelpersModifiers`, and the two props bestax spells differently on every
@@ -24,7 +24,7 @@ bloomer's `withHelpersModifiers` mixed these into every component.
24
24
  | `isUnselectable` | `interaction="unselectable"` | |
25
25
  | `isMarginless` | `m="0"` | bestax expresses the `is-*less` helpers as spacing |
26
26
  | `isPaddingless` | `p="0"` | |
27
- | `isFullWidth` | `isFullWidth` | on Button, Select, Table, Tabs; a TODO elsewhere |
27
+ | `isFullWidth` | `isFullwidth` | on Button, Select, Table, Tabs |
28
28
  | `isDisplay`, `isHidden` | `display*`, `visibility*` | flattened — see below |
29
29
  | `tag` | `as` | where bestax declares one — see below |
30
30
  | `render` | — | always a TODO |
@@ -151,8 +151,8 @@ A few bestax parts extend only React's HTML attributes and take no Bulma helper
151
151
  `Pagination.Previous`/`Next`/`Ellipsis`, `Navbar.Dropdown`/`DropdownMenu`/`Divider`,
152
152
  `Panel.Heading`/`Tabs`/`Block`, `Tabs.List`/`Item`, `Message.Header`/`Body` and the `Modal`
153
153
  parts. A bloomer helper on one of those becomes the Bulma class in `className` — except on
154
- `Navbar.Divider`, `Pagination.Ellipsis` and `Dropdown.Divider`, which write their own className
155
- last or take no props at all: there the helper is named in a TODO instead, and an element
154
+ `Pagination.Ellipsis` and `Dropdown.Divider`, which write their own className last or take no
155
+ props at all: there the helper is named in a TODO instead, and an element
156
156
  carrying a spread is left as bloomer's. Otherwise it is the Bulma class in `className` (`is-pulled-right`,
157
157
  `m-0`, `is-hidden-mobile`, …), since Bulma v1 still ships every one of them; only a dynamic
158
158
  value is flagged. The same conversion covers the modifiers bestax has no prop for anywhere —
@@ -162,11 +162,11 @@ value is flagged. The same conversion covers the modifiers bestax has no prop fo
162
162
  ## Refs
163
163
 
164
164
  bloomer forwarded no refs. bestax forwards a ref from the form controls, plus `Avatar`,
165
- `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Menu.Item`,
166
- `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`, `Navbar.Link`, `Sidebar`
167
- and `Toast` — pass `ref` directly on those. "The form controls" means the inputs
168
- themselves: the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers
169
- around them forward nothing.
165
+ `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Loader`,
166
+ `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`,
167
+ `Navbar.Link`, `Sidebar` and `Toast` — pass `ref` directly on those. "The form controls" means
168
+ the inputs themselves: the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios`
169
+ wrappers around them forward nothing.
170
170
 
171
171
  A `ref` on anything else is unsupported, and the two React majors fail differently: React 18
172
172
  drops it and logs "Function components cannot be given refs", while React 19 hands it to the
@@ -160,6 +160,23 @@ these either. `Level.Item` is not narrower: it declares the anchor's whole attri
160
160
  every `as` and forwards it only on the `<a>`, so all of these survive at `as="a"` and none of
161
161
  them reaches a `<p>` or a `<div>`.
162
162
 
163
+ ## `prop:type` — a `<button>` item that no longer submits its form
164
+
165
+ `Navbar.Item`, `Navbar.Link`, `Menu.Item` and `Dropdown.Item` write `type="button"` on the
166
+ `<button>` an `as` makes them render, whenever the element sets no `type` or one HTML doesn't
167
+ define. A plain `<button>` with neither submits the form around it, so an item you rendered as a
168
+ button with `tag="button"` stops submitting once migrated. The codemod can't tell whether one sits
169
+ in a form, so each gets a TODO:
170
+
171
+ ```jsx
172
+ <NavbarItem tag="button">Search</NavbarItem>
173
+ <Navbar.Item as="button">Search</Navbar.Item>
174
+ ```
175
+
176
+ Add `type="submit"` if it should still submit, or `type="button"` to say it shouldn't, then
177
+ delete the comment. An item whose `type` is written out as `button`, `submit` or `reset`, or
178
+ comes from a spread or an expression, gets no TODO.
179
+
163
180
  ## `prop:render` — the render prop
164
181
 
165
182
  bloomer's `render` handed the computed props (`className` included) to your own renderer:
@@ -191,8 +208,8 @@ bloomer rendered, but Bulma v1 no longer ships styles for that class, so restyle
191
208
  ## `prop:isFullWidth` — not a universal helper in bestax
192
209
 
193
210
  bloomer accepted `isFullWidth` on everything; Bulma's `is-fullwidth` only means something on a
194
- few elements, and bestax declares `isFullWidth` on exactly those — `Button`, `Select`, `Table`,
195
- `Tabs` — where it passes through. Elsewhere, drop it, or add `className="is-fullwidth"` if your
211
+ few elements. Where bestax has a prop for it (`Button`, `Select`, `Table`, `Tabs`), it becomes
212
+ `isFullwidth`. Elsewhere, drop it, or add `className="is-fullwidth"` if your
196
213
  own CSS styled the class.
197
214
 
198
215
  ## Modifiers bestax has no prop for — converted to classes