create-bestax 4.2.11 → 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 (48) 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 +19 -4
  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 +32 -14
  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 +337 -0
  35. package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +230 -0
  36. package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +348 -0
  37. package/templates/skills/bestax-migrate/references/css-migration.md +8 -1
  38. package/templates/skills/bestax-migrate/references/rbx/component-map.md +15 -15
  39. package/templates/skills/bestax-migrate/references/rbx/prop-map.md +17 -19
  40. package/templates/skills/bestax-migrate/references/rbx/unmappables.md +17 -0
  41. package/templates/skills/bestax-migrate/references/react-bulma-components/component-map.md +34 -34
  42. package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +34 -7
  43. package/templates/skills/bestax-optimize/SKILL.md +3 -2
  44. package/templates/skills/bestax-optimize/references/modular-build.md +25 -5
  45. package/templates/skills/bestax-theming/SKILL.md +4 -3
  46. package/templates/skills/bestax-theming/examples/dark-mode.tsx +4 -3
  47. package/templates/skills/bestax-theming/references/css-variables.md +49 -8
  48. package/templates/skills/bestax-theming/references/themeable-components.md +12 -10
@@ -0,0 +1,348 @@
1
+ # Bulma classes → bestax-bulma: what the codemod leaves for you
2
+
3
+ Every `TODO(bestax-migrate)` this source writes, by rule, with what to do about it. The markup
4
+ under a TODO is exactly as it was, so the app still renders the same while you work through
5
+ them. The rule after the colon names a Bulma class, a bestax component or prop, or an
6
+ attribute, never one of your own classes.
7
+
8
+ ## An element it would not convert
9
+
10
+ ### `spread:<Target>`
11
+
12
+ The element spreads props (`<div className="box" {...rest}>`). A spread can carry anything,
13
+ and bestax reads some names as its own props: a spread `className` merges with the component's
14
+ classes, where on the plain element it replaced them. Convert by hand once you know what the
15
+ spread carries, or leave it. A spread on the `<select>` inside a `.select` counts too, since what it
16
+ carries becomes `SelectBase`'s.
17
+
18
+ ### `ref:<Target>`
19
+
20
+ The element has a `ref`, and the bestax component doesn't forward one, so the ref would stop
21
+ reaching the DOM node. Keep the element as markup.
22
+
23
+ ### `tag:<Target>`
24
+
25
+ The codemod converts that class on a fixed tag, or a short list of them through `as` (see the
26
+ **Tags** column in [component-map.md](component-map.md)), and this element is on another one:
27
+ `<div className="section">`, `<p className="notification">`. Changing the tag changes the
28
+ markup, so decide whether you want it; if you do, change it and re-run. If the component's
29
+ own `as` takes the tag, writing it by hand keeps the markup instead.
30
+
31
+ ### `attr:<prop>`
32
+
33
+ An attribute on the element is also a prop of the bestax component, which would read it
34
+ differently: `<div className="box" color="red">` would lose its `color` attribute to Box's text
35
+ color. Rename or drop the attribute, then re-run.
36
+
37
+ It also covers an attribute the component's props type rejects. `Delete` takes no `type`
38
+ (`type="button"` is the one value that converts, because `Delete` renders it anyway), and
39
+ `Progress` types `value` and `max` as numbers, so `value="half"` stays. Every component types
40
+ `tabIndex` as a number too, so `tabIndex="0"` converts as `tabIndex={0}`. A number converts only
41
+ when it is spelled the way it renders (`value="40"`, not `value="040"`).
42
+
43
+ `attr:dangerouslySetInnerHTML` is the same kind of refusal: the element sets its own content,
44
+ and some bestax components render content of their own beside `children`, which React
45
+ rejects. Keep the element as markup. `attr:children` is too: the codemod reads an element's
46
+ children from the JSX inside it, so children passed as an attribute are content it can't see.
47
+ Move them inside the element, then re-run.
48
+
49
+ On a `.select` or a `.breadcrumb` it's about the element inside, which the component renders
50
+ itself. `SelectBase` gives its `<select>` every attribute it's given and no class but
51
+ `is-hovered` or `is-focused`, so an attribute on the `.select` (an `attr` TODO) would move onto
52
+ the `<select>`, and another class on the `<select>` (`attr:className`) would be lost, as would
53
+ the `class=""` an empty `className` renders. Move the
54
+ attribute onto the `<select>` if that's where you want it, then re-run. A `multiple` `<select>`
55
+ converts only inside a `.select.is-multiple`, with `multiple` written bare, and its `size` only
56
+ beside it, as a number written out (`size={4}`): `SelectBase` writes it back only when it holds a
57
+ number, which the codemod can't tell of an expression. `Breadcrumb` renders its `<ul>` bare, so an attribute or class on the `<ul>` keeps
58
+ both as markup.
59
+
60
+ On a menu item it's where each attribute lands. `Menu.Item` puts `className`, `id`, `title`,
61
+ `role`, `tabIndex`, `style` and `data-testid` on the `<li>`, and everything else on the `<a>`, so
62
+ an item converts only when its attributes already sit that way: an `onClick` on the `<li>`, or a
63
+ `title` or a `key` on the `<a>`, keeps it as markup. A `ref` on the `<a>` converts, since
64
+ `Menu.Item` forwards it there, and one on the `<li>` doesn't. The `<a>` takes no class but
65
+ `is-active`, since the item's `className` goes on the `<li>`, so any other class on it, or an
66
+ empty one, keeps the item as markup too, and so does an empty `className` on the `<li>`, since
67
+ nothing is left to render its `class=""`. Move the attribute to where `Menu.Item` puts it if
68
+ that's what you want, then re-run.
69
+
70
+ ### `defaults:<Target>`
71
+
72
+ The component renders attributes of its own when the element doesn't set them. `Delete`
73
+ renders `type="button"` and `aria-label="Close"`. On a bare `<button className="delete">`,
74
+ converting would add both, changing a submit button inside a form into a plain button. Add the
75
+ attributes you want (usually both, with a real label), then re-run. `Navbar` renders
76
+ `role="navigation"` and `aria-label="main navigation"`, which Bulma's own navbar markup carries;
77
+ give the `<nav>` both, with your own label if you like. `Breadcrumb` renders
78
+ `aria-label="breadcrumbs"`, so give its `<nav>` an `aria-label`, whatever it says.
79
+ `Icon` renders `aria-label="icon"`, so give a `.icon` an `aria-label` that says what the icon means.
80
+ `Pagination` renders Bulma's own `role="navigation"` and `aria-label="pagination"`, and
81
+ `Pagination.Previous`, `Pagination.Next` and `Pagination.Link` render `tabIndex={0}`, so give each
82
+ element the attribute it's missing.
83
+ `Card.FooterItem`, `Card.Header.Icon` and `Navbar.Item` render `type="button"` on a `<button>` that
84
+ sets none, and `Card.Header.Icon` renders `aria-label="more options"` too. One whose `type` isn't
85
+ `button`, `submit` or `reset` gets an `attr:type` TODO instead, since bestax writes `button` in its
86
+ place. Write the type you mean, then re-run.
87
+
88
+ ### `drops:<Target>`
89
+
90
+ The component drops an attribute on this tag. Most of these do nothing there anyway: `Button`
91
+ drops `href`, `target` and `rel` on a `<button>`, and `Level.Item` keeps link attributes only
92
+ on an `<a>`. Remove the attribute, then re-run.
93
+
94
+ `disabled` is the exception. `Button` drops it on a tag with no disabled state
95
+ (`<a className="button" disabled>`), but Bulma greys out a disabled `.button` on any tag, so
96
+ dropping it would change how the element looks. Keep that element as markup.
97
+
98
+ So is `name` on an `<a>`, which `Card.FooterItem` drops: browsers still scroll a `#fragment` link
99
+ to it. Keep that element as markup, or move the target to an `id` (which it keeps), then re-run.
100
+
101
+ ### `children:<Target>`
102
+
103
+ The component's props type requires children, and the element has none (`Buttons`). An empty
104
+ `.buttons` does nothing; delete it or give it buttons.
105
+
106
+ On `Card` and `Card.Header` it means the children decide. `Card` renders its children inside a
107
+ `.card-content` of its own unless one of them is one of its parts (`Card.Header`,
108
+ `Card.Header.Icon`, `Card.Image`, `Card.Content`, `Card.Footer`, `Card.FooterItem`), and
109
+ `Card.Header` renders a `.card-header-title` of its own unless one of its children is a
110
+ `Card.Header.Title`. None of this element's direct children converted to one, so converting it
111
+ would add that wrapper. Look at the TODOs on the children first: a title that stays markup keeps
112
+ its header as markup too. A card whose content sits straight inside it, with no
113
+ `.card-content`, has no part to find; wrap that content in `Card.Content` if the extra padding
114
+ is what you want, or keep the markup. A part inside an expression (`{open && <div className="card-content">}`)
115
+ doesn't count, because it may not render.
116
+
117
+ On a `.table-container` or `.fixed-grid` it's the wrapper case. `Table isResponsive` and
118
+ `Grid isFixed` render that wrapper themselves, around nothing but the table or grid, so the
119
+ wrapper folds into them only when it holds that one element and nothing else. A caption, a
120
+ comment or a second element beside it keeps the wrapper as markup; move it outside the wrapper,
121
+ then re-run. An attribute or an extra class on the wrapper keeps it too, because the component
122
+ renders the wrapper bare.
123
+
124
+ On a `.select`, a `.breadcrumb` or an `.image` it's the same shape from the other side. `SelectBase`
125
+ renders the `<select>` inside `.select` itself, `Breadcrumb` the `<ul>` inside `.breadcrumb` and
126
+ `Image` the `<img>` inside `.image`. So `SelectBase` and `Breadcrumb` convert only around that one
127
+ element, with nothing else beside it. `Image` renders anything else it's given as it is, so an
128
+ `.image` converts around HTML elements written out either way. It gets this TODO when it's empty,
129
+ or when what's inside is an expression or text, since `Image` renders an `<img>` of its own when
130
+ its children come out empty. Keep it as markup, or write the `Image` by hand if the expression
131
+ never is.
132
+
133
+ A menu item is the same from the `<li>`'s side: `Menu.Item` renders the `<a>` inside it, and a
134
+ `Menu.List` after that, so an item converts only when its `<li>` holds one `<a>` and at most one
135
+ `<ul>` after it, with nothing else beside them. Text or a comment beside the `<a>` keeps it as
136
+ markup, and so does a router link or a `<button>` in its place: write a router link by hand as
137
+ `<Menu.Item as={Link}>`. The nested `<ul>` converts only bare (no class, no attributes) and
138
+ holding its items, as Bulma nests one, and the `<a>` has to hold something, since `Menu.Item`
139
+ requires children.
140
+
141
+ On a `.pagination-ellipsis`, `Pagination.Ellipsis` renders its own `…` whatever it's given, so the
142
+ element converts only holding exactly that (`&hellip;` or the character), and closes itself.
143
+
144
+ On an `.icon-text`, `IconText` builds its icons from props and wraps each text in a `<span>` of
145
+ its own, so it converts only when its children are `.icon`s, each followed by at most one bare
146
+ `<span>` of static text. Each `.icon` has to convert to `Icon` on its own, so one with no
147
+ `aria-label` gets a `defaults:Icon` TODO of its own first: give it one that says what the icon
148
+ means, then re-run. Its `<i>` has to be bare and empty and name one Font Awesome or Material Design
149
+ Icons glyph, so an `aria-hidden` on the `<i>`, another icon font, or anything between the children
150
+ keeps it as markup, and the `.icon`s inside convert on their own. The markup renders the same as
151
+ it is; to use `IconText` anyway, write it by hand with `iconProps={{ name: ... }}` (the
152
+ `bestax-icons` skill covers `Icon`'s library and name props).
153
+
154
+ On a `.file`, `File` renders the whole tree from props, so it converts only when its tree is the one
155
+ `File` renders: one bare `.file-label` `<label>` holding a `.file-input` `<input type="file">`, a
156
+ bare `.file-cta` `<span>` with a bare `.file-label` `<span>` and at most one bare `.file-icon`
157
+ `<span>` on each side of it, and, with `has-name`, at most one bare `.file-name` `<span>` of
158
+ static text. A class or attribute on any part of the tree, text between the parts, or content
159
+ that isn't text and elements written out keeps it as markup. Rebuild it as one `File` by hand
160
+ (its API page lists the props for the button text, the file name and the icons), or keep the
161
+ markup, which renders the same.
162
+
163
+ On a `.skeleton-lines`, `Skeleton` renders the children itself: `lines` bare, empty `<div>`s. So
164
+ the element converts only when its children are just that, and a class, an attribute, text or a
165
+ comment in one of them, or anything else beside them, keeps it as markup. Keep it if the
166
+ children matter; otherwise make them bare `<div>`s, then re-run.
167
+
168
+ ### `context:<Target>`
169
+
170
+ `Field` and `Control` tell bestax's form controls inside them to skip their own `.field` and
171
+ `.control` wrappers. A `.field` or `.control` that already holds a bestax component (an `Input`
172
+ from an earlier, partial migration) would change how that component renders once converted, so
173
+ it stays markup. Convert it by hand and check that the component inside still renders what you
174
+ want, or leave it.
175
+
176
+ On a `.file` it's the other way round: `File` renders a `.field` of its own unless it sits inside
177
+ a `Field`, so a `.file` converts only inside a bestax `Field` or a `.field` that converts in the
178
+ same run. Convert the `.field` around it (its own TODO says what keeps it), then re-run.
179
+
180
+ On an input or textarea it's the other direction: inside a bestax `Field` with a `label`,
181
+ `InputBase` and `TextAreaBase` take the Field's generated `id` when they have none, which the
182
+ element didn't have. Context follows what renders, not what the file says, so an input with no
183
+ `id` inside any other component of the app stays markup too, since that component could render
184
+ a labelled `Field` around it. Give the element its own `id`, then re-run.
185
+
186
+ The codemod reads one file at a time, so it can't see a component in another file that renders
187
+ a bestax `Field` or form control around this markup. If the app already uses them that way, give
188
+ its inputs ids before running the codemod, and check its forms afterwards.
189
+
190
+ On a `.tabs` it's the same shape as a `.field`: `Tabs` passes its active tab to the `Tabs.Tab`s
191
+ and `Tabs.Content.Item`s inside it, and renders differently around a `Tabs.Content`, so a `.tabs`
192
+ that already holds one of those stays markup. Only the ones written in the file count: a
193
+ component of the app inside the `.tabs` that renders one itself would start following the
194
+ active tab once the `.tabs` converts, so check tabs built from your own components after the run.
195
+
196
+ On a `.modal-background`, `.modal-content` or `.modal-card` it's about the `Modal` around it. A
197
+ bestax `Modal` already in the file picks what it renders from its children: with one of those
198
+ parts among them it renders them as given, and without one it adds its own background, wrapper
199
+ and close button. So converting the part would change the `Modal`, and the part stays markup.
200
+ Convert the `Modal` and its children together by hand.
201
+
202
+ On a `.menu-list` it's about nesting: `Menu.List` renders `.menu-list` only on the outermost list,
203
+ and drops it on one inside another. So a `.menu-list` inside another `.menu-list` (or a bestax
204
+ `Menu.List`) stays markup, keeping its class, and so does one around a bestax `Menu.List`, which would
205
+ lose the class once the outer one converts. Only the elements in the same file count:
206
+ a `.menu-list` another component renders inside a `Menu.List` would lose the class, so check a
207
+ menu split across components after the run.
208
+
209
+ On a `.pagination-link` or a `.pagination-ellipsis` it's the `<li>` around it: `Pagination.Link`
210
+ and `Pagination.Ellipsis` render a bare `<li>` of their own, so each converts only as the only
211
+ thing inside a bare `<li>`, which it takes the place of, `key` and all. A class or an attribute
212
+ on the `<li>`, or anything beside the element in it, keeps it as markup. A `.pagination-link`'s
213
+ `is-current` becomes `active` only beside an `aria-current` of its own, since `active` renders
214
+ `aria-current="page"` otherwise; without one it stays a class. And `Pagination.Ellipsis` writes
215
+ a `className` it's given in place of `.pagination-ellipsis`, so another class on it keeps it as
216
+ markup with `attr:className`.
217
+
218
+ A menu item with a nested list is the same rule from inside: its nested `<ul>` becomes a
219
+ `Menu.List`, which renders `.menu-list` unless another `Menu.List` is around it. So the item
220
+ converts only inside a list that is a `Menu.List` already or becomes one in the same run, and an
221
+ item inside a `.menu-list` that stays markup (one that spreads props, say) keeps its nested list,
222
+ and itself, as markup with a `context:Menu.Item` TODO. An item with no nested list renders the
223
+ same anywhere, so it converts either way.
224
+
225
+ ### `only-child:<Target>`
226
+
227
+ The element is the only child of another component (`<Link href="/x"><a className="button">`),
228
+ which may reach into it with `cloneElement`: next/link's legacy behavior, a tooltip, a Radix
229
+ `asChild` trigger. A bestax component would not take those props or that ref the same way.
230
+ Convert it by hand if the parent only renders its children.
231
+
232
+ ### `dynamic-class:<Target>`
233
+
234
+ The `className` is computed in a way the codemod can't read exactly, and with its classes
235
+ written out the element would become bestax `<Target>`.
236
+
237
+ What it can read is a `clsx` or `classnames` call made of class strings and classes added
238
+ under a condition: `busy && 'is-loading'`, `{ 'is-loading': busy }`, `busy ? 'is-loading' : ''`
239
+ (or `: null`). Those convert without a TODO. A condition on one of the component's flags
240
+ becomes that prop (`isLoading={busy}`, or `isLight={!quiet}` for `quiet ? '' : 'is-light'`),
241
+ anything else stays in the call, and the call goes once nothing is left in it. `Field`'s
242
+ `is-grouped` stays in the call too, since `grouped` renders it for `true` alone. A prop is
243
+ evaluated before the call, so a condition moves ahead of one left in the call only when neither
244
+ can have side effects (a call, an assignment); otherwise it stays. A condition that isn't a
245
+ boolean (`items.length && 'is-active'`) renders the same, but won't typecheck against the
246
+ boolean prop; wrap it in `Boolean(…)`.
247
+
248
+ So this TODO is a className built any other way: a ternary between two classes, a template
249
+ with expressions, a variable, another function (`classnames/bind`, an app's own `cn`), or the
250
+ component's class itself under a condition. Convert by hand with the [prop map](prop-map.md),
251
+ turning each condition into the prop:
252
+
253
+ ```tsx
254
+ // before
255
+ <button className={primary ? 'button is-primary' : 'button is-light'}>Save</button>
256
+ // after
257
+ <Button color={primary ? 'primary' : undefined} isLight={!primary}>Save</Button>
258
+ ```
259
+
260
+ The classes in a computed `className` still count for every other rule: a `clsx('box')` on a
261
+ `<span>` gets `tag:Box`, and a `clsx('dropdown', …)` gets `family:dropdown`. A computed
262
+ `className` on the `<select>` inside a `.select`, the `<ul>` inside a `.breadcrumb` or the `<a>`
263
+ in a menu item gets this TODO too. For the `<a>`, a condition on `is-active` is `Menu.Item`'s
264
+ `active`: `<a className={on ? 'is-active' : undefined}>` becomes `<Menu.Item active={on}>`.
265
+
266
+ ## A family it leaves as markup: `family:<class>`
267
+
268
+ The bestax component renders parts of its own, or adds attributes, so a one-element-at-a-time
269
+ conversion would change the markup. Convert the whole family by hand, and look at the result
270
+ in the browser:
271
+
272
+ - **`family:navbar-burger`**: `Navbar.Burger` is a `<button>` that renders the burger's spans
273
+ itself and sets `aria-expanded` from `active`. Replace the whole toggle with it, drive
274
+ `active` from the state the old click handler flipped, and pass the same state to
275
+ `Navbar.Menu`'s `active`. Don't give it spans of your own: Bulma positions every span in the
276
+ burger. Releases before 5.16.10 render three spans where Bulma v1 positions four, so upgrade
277
+ rather than adding one.
278
+ - **`family:navbar-link`**: inside a `Navbar.Dropdown`, `Navbar.Link` adds `aria-haspopup`,
279
+ `aria-expanded` and keyboard handling. The codemod turned the `.has-dropdown` item around it
280
+ into a `Navbar.Item` that keeps the class; to get the dropdown behavior, replace that item
281
+ with `Navbar.Dropdown` (`hoverable` for `is-hoverable`) and the link with `Navbar.Link`.
282
+ - **`family:checkbox`**, **`family:radio`**, **`family:checkboxes`**, **`family:radios`**:
283
+ bestax renders its own styled checkbox and radio markup, not Bulma's.
284
+ - **`family:modal`**: `Modal` writes a `data-testid` of its own and adds dialog attributes and
285
+ focus handling, so the `.modal` root stays markup while its background, content and card parts
286
+ convert on their own. Rebuild the root with `Modal` around them, and drive it with its open prop.
287
+ A `.modal-close` stays too, since `Modal.Close` renders `.delete` unless it's floating: write
288
+ `<Modal.Close variant="floating" />` in its place. When the
289
+ page has to look the same, keep `Modal` rather than `Dialog` or `Toast`: those render
290
+ bestax's own `.dialog` and `.toast` markup, which Bulma's stylesheet doesn't style.
291
+ - **`family:dropdown`**: `Dropdown` renders its own trigger and menu from props.
292
+ - **`family:menu-item`**: Bulma styles an item as `.menu-list a`, `.menu-list button` or
293
+ `.menu-list .menu-item`, the last for an item on any other tag. `Menu.Item` renders the element
294
+ inside its `<li>` with no class but `is-active`, and puts its `className` on the `<li>`, so the
295
+ class has nowhere to go. On an `<a>` inside a `.menu-list` it adds nothing, so drop it and
296
+ re-run, and the item can convert. On any other tag, keep the markup.
297
+ - **`family:message`**: `Message` always wraps its children in `.message-body`.
298
+ - **`family:panel-icon`**: `Panel.Icon` renders through `Icon`, which always writes an
299
+ `aria-label`, so write `<Panel.Icon>` by hand with the `<i>` inside. The rest of a panel converts,
300
+ but for a `<label>` or `<div>` `.panel-block`, which stays markup with no TODO: bestax renders
301
+ those as `Panel.CheckboxBlock`, `Panel.InputBlock` and `Panel.ButtonBlock`, which build their own
302
+ contents from props.
303
+
304
+ ## A class Bulma v1 removed: `legacy:<class>`
305
+
306
+ `legacy:tile`: Bulma v1 has no tiles. Rebuild the layout with `Grid` and `Cell`; the Bulma
307
+ 0.9 to 1 guide shows the translation. Until then the element has no styles at all under
308
+ Bulma v1.
309
+
310
+ ## A whole file it left alone
311
+
312
+ These come only when the file has an element the codemod would convert (a computed className
313
+ counts), so a re-run after the fix has something to do. A non-React file gets `jsx-runtime`
314
+ alone: every other TODO names a React component to use.
315
+
316
+ - **`rsc`**: the file is in a Next.js App Router project (a package with `next` and an
317
+ `app/` directory) and has no `'use client'`, so it may render as a server component, and
318
+ bestax's components are client components. That covers files outside `app/` too: a
319
+ component under `components/` is a server component when a server page renders it. Files
320
+ under `pages/` are always client code and convert. Add `'use client'` if the file can be a
321
+ client module (no `async` component, no server-only calls), then re-run.
322
+ - **`styled-jsx`**: the component scopes its styles with `<style jsx>`, which adds its scoping
323
+ class to plain elements and not to an imported component, so a converted element would lose
324
+ its styles. Move those
325
+ styles out of `<style jsx>`, or scope them with `:global()`, then re-run.
326
+ - **`imports`**: the file is CommonJS (`require` or `module.exports`, no ES `import` or
327
+ `export`), and the codemod adds an ES `import`. Move the file to ES modules, then re-run.
328
+ - **`jsx-runtime`**: the file's JSX isn't React's: a `@jsx` pragma or `@jsxImportSource` naming
329
+ another runtime (Preact, Solid), or a package that sets one in its tsconfig or depends on one
330
+ instead of React. bestax-bulma components are React components.
331
+
332
+ ## Things that get no TODO
333
+
334
+ - Helper classes on a `<div>` (`<div className="is-flex mt-4">`): bestax has no plain `<div>`
335
+ wrapper, and the classes are valid Bulma, so the element stays.
336
+ - `.help`, `.label`, `.loader` and the other classes in the component map's "left alone" list.
337
+ - A class with no bestax prop on a converted element: it stays in `className`.
338
+
339
+ ## After the codemod
340
+
341
+ Class order changes on converted elements (bestax writes its own classes first), so snapshot
342
+ tests that compare class strings will churn while the rendered page stays the same. Update the
343
+ snapshots after reviewing the diff.
344
+
345
+ If the build runs PurgeCSS, the report says so: a converted element's classes now come from
346
+ bestax's code, some of them built from props at runtime (`mt="4"` renders `mt-4`), so the
347
+ PurgeCSS config has to scan bestax's dist and safelist those patterns. The docs' optimizing CSS
348
+ guide has the config.
@@ -8,7 +8,14 @@ stylesheet layer; this reference explains what it did and how to finish what it
8
8
  Coming from rbx or bloomer you are crossing **two** Bulma majors rather than one, so expect more
9
9
  visual drift than the 0.9 → 1 notes below describe on their own.
10
10
 
11
- ## What the codemod already did (default `--css bestax`)
11
+ An app that writes Bulma's classes directly (`bulma-classes`) keeps its stylesheet by
12
+ default: that source runs as `--css keep`, because the app's own Bulma already styles every
13
+ class a converted element renders, so its stylesheet imports, Sass and Bulma version stay as
14
+ they were. Moving to Bulma v1 is a separate step, taken with `--css bestax` or `--css bulma`.
15
+ From a 0.9.x it then follows these notes as written; on Bulma v1 already, only the bundle
16
+ choice below applies.
17
+
18
+ ## What the codemod already did (`--css bestax`, the library sources' default)
12
19
 
13
20
  - **CSS imports**: `bulma/css/bulma(.min).css` and v3-era
14
21
  the source library's own CSS imports (`react-bulma-components/dist/*.css`,
@@ -142,21 +142,21 @@ test holds to rbx's own export surface in both directions.
142
142
 
143
143
  ## The renames worth memorising
144
144
 
145
- | rbx | bestax-bulma |
146
- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
147
- | `Tag.Group` | `Tags` |
148
- | `Button.Group` | `Buttons` |
149
- | `Column.Group` | `Columns` |
150
- | `Card.Footer.Item` | `Card.FooterItem` |
151
- | `Table.Head` / `.Body` / `.Foot` | `Table.Thead` / `.Tbody` / `.Tfoot` |
152
- | `Table.Row` / `.Cell` / `.Heading` | `Table.Tr` / `.Td` / `.Th` |
153
- | `Textarea` | `TextArea` |
154
- | `PageLoader` | `Loading` with `isFullPage` |
155
- | `Loader` | a plain `<div className="loader">` — bestax's `Loading` is an overlay that renders nothing unless `active` |
156
- | `Menu.List.Item` | `Menu.Item` |
157
- | `Tab` / `Tab.Group` | `Tabs.Item` / `Tabs` |
158
- | `Panel.Tab.Group` | `Panel.Tabs` |
159
- | `Title subtitle` | `SubTitle` |
145
+ | rbx | bestax-bulma |
146
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
147
+ | `Tag.Group` | `Tags` |
148
+ | `Button.Group` | `Buttons` |
149
+ | `Column.Group` | `Columns` |
150
+ | `Card.Footer.Item` | `Card.FooterItem` |
151
+ | `Table.Head` / `.Body` / `.Foot` | `Table.Thead` / `.Tbody` / `.Tfoot` |
152
+ | `Table.Row` / `.Cell` / `.Heading` | `Table.Tr` / `.Td` / `.Th` |
153
+ | `Textarea` | `TextArea` |
154
+ | `PageLoader` | `Loading` with `isFullPage` |
155
+ | `Loader` | a plain `<div className="loader">` — bestax's `Loading` is an overlay that renders nothing unless `active`; bestax's `Loader` renders the same ring as a named progressbar, so swap it in by hand |
156
+ | `Menu.List.Item` | `Menu.Item` |
157
+ | `Tab` / `Tab.Group` | `Tabs.Item` / `Tabs` |
158
+ | `Panel.Tab.Group` | `Panel.Tabs` |
159
+ | `Title subtitle` | `SubTitle` |
160
160
 
161
161
  ## Value-chosen targets
162
162
 
@@ -140,26 +140,26 @@ that is what keeps the `href`. Dropping the `as` does not, on this component.
140
140
  rbx reaches a component's rendered element with `innerRef`. bestax uses a plain forwarded
141
141
  `ref` on the roots that support one, so the codemod renames the prop:
142
142
 
143
- | rbx | bestax-bulma | note |
144
- | ----------------------------------------------------- | ------------ | ------------------------------------------- |
145
- | `innerRef` on `Button`, `Dropdown`, `Modal`, `Navbar` | `ref` | same node — each root's own element |
146
- | `innerRef` on `Navbar.Burger`, `Navbar.Link` | `ref` | these two sub-components forward one too |
147
- | `innerRef` on `Navbar.Item` **with `dropdown`** | `ref` | that one becomes bestax's `Navbar.Dropdown` |
148
- | `innerRef` on `Modal.Container` | `ref` | that one becomes bestax's `Modal` root |
143
+ | rbx | bestax-bulma | note |
144
+ | ----------------------------------------------------- | ------------ | -------------------------------------------- |
145
+ | `innerRef` on `Button`, `Dropdown`, `Modal`, `Navbar` | `ref` | same node — each root's own element |
146
+ | `innerRef` on `Navbar.Burger`, `Navbar.Link` | `ref` | these two sub-components forward one too |
147
+ | `innerRef` on `Navbar.Item` | `ref` | with `dropdown` it becomes `Navbar.Dropdown` |
148
+ | `innerRef` on `Modal.Container` | `ref` | that one becomes bestax's `Modal` root |
149
149
 
150
150
  The third row is the `Navbar.Dropdown` collision, and it runs both ways. Your rbx
151
151
  `Navbar.Dropdown` is the menu itself (`div.navbar-dropdown`), so it maps to bestax's
152
152
  `Navbar.DropdownMenu`, which forwards no ref — `innerRef` there is left alone. bestax reserves
153
153
  the name `Navbar.Dropdown` for the outer container, which is what `<Navbar.Item dropdown>`
154
- becomes. The rename stays conditional on the `dropdown` prop because only that branch knows
155
- which of the two targets was picked, not because the other branch lacks a ref: a plain
156
- `<Navbar.Item>` forwards one too since #661, so its `innerRef` is a rename the codemod leaves
157
- for you (see the gap below).
154
+ becomes.
155
+
156
+ Beside a `ref` the element already has, a rename leaves `innerRef` in place with a `prop:innerRef`
157
+ TODO rather than write a second `ref`; keep whichever one you meant.
158
158
 
159
159
  An existing `ref` is passed through untouched — which is safe only where the bestax target
160
160
  forwards one. rbx forwards a ref on every component; bestax does so on the form controls, plus
161
161
  `Avatar`, `Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`,
162
- `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`,
162
+ `Loader`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`,
163
163
  `Navbar.Link`, `Sidebar` and `Toast`. "The form controls" there means the inputs themselves:
164
164
  the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios` wrappers around them
165
165
  forward nothing, and `Field` is a target this codemod emits.
@@ -174,11 +174,9 @@ outcome is one to build on: put the ref on an element you control.
174
174
  **The codemod does not flag this**, because neither the universal prop table nor any
175
175
  per-component table has a `ref` entry, so check every `ref` you carried over against the list
176
176
  above rather than assuming the silent pass-through means it works. The renames above are for
177
- `innerRef`, and apply only on those eight entries.
178
-
179
- Note the gap that leaves. An rbx `innerRef` can only land on a component rbx itself has, so of
180
- the ref-forwarding targets above the reachable ones are the form controls, `Menu.Item` and a
181
- plain `Navbar.Item`. There the codemod leaves `innerRef` in place and flags it rather than
182
- renaming it, so the rename is yours to make and the target will take it. Everywhere the eight
183
- renames above do not reach, `innerRef` is flagged the same way — but on a target that forwards
184
- nothing the answer is a wrapping element you control, not a rename.
177
+ `innerRef`, and apply only where the table says.
178
+
179
+ Note the gap that leaves. Where no row above reaches, `innerRef` stays in place and is flagged.
180
+ On a target that forwards a ref (the form controls, `Menu.Item`) the rename is yours to make and
181
+ the target will take it. On one that forwards nothing, the answer is a wrapping element you
182
+ control, not a rename.
@@ -95,6 +95,23 @@ these either. `Level.Item` is not narrower: it declares the anchor's whole attri
95
95
  every `as` and forwards it only on the `<a>`, so all of these survive at `as="a"` and none of
96
96
  them reaches a `<p>` or a `<div>`.
97
97
 
98
+ ## `prop:type` — a `<button>` item that no longer submits its form
99
+
100
+ `Navbar.Item`, `Navbar.Link`, `Menu.Item` and `Dropdown.Item` write `type="button"` on the
101
+ `<button>` an `as` makes them render, whenever the element sets no `type` or one HTML doesn't
102
+ define. A plain `<button>` with neither submits the form around it, so an item you rendered as a
103
+ button with `as="button"` stops submitting once migrated. The codemod can't tell whether one sits
104
+ in a form, so each gets a TODO:
105
+
106
+ ```jsx
107
+ <Navbar.Item as="button">Search</Navbar.Item>
108
+ <Navbar.Item as="button">Search</Navbar.Item>
109
+ ```
110
+
111
+ Add `type="submit"` if it should still submit, or `type="button"` to say it shouldn't, then
112
+ delete the comment. An item whose `type` is written out as `button`, `submit` or `reset`, or
113
+ comes from a spread or an expression, gets no TODO.
114
+
98
115
  ## `component:Generic` — rbx's base element
99
116
 
100
117
  `Generic` is rbx's untyped passthrough. Render the underlying tag, and move its helper props to
@@ -6,40 +6,40 @@ codemod converts it; **Flagged** = it leaves a `TODO(bestax-migrate)` comment (s
6
6
 
7
7
  ## Top-level components
8
8
 
9
- | RBC | bestax | Notes |
10
- | -------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
11
- | `Block` | `Block` | Auto |
12
- | `Box` | `Box` | Auto |
13
- | `Breadcrumb` | `Breadcrumb` | Auto; `align` → `alignment` (`center`→`centered`) |
14
- | `Button` | `Button` | Auto for literal `remove` (by value, not presence — truthy → `<Delete/>`, `remove={false}` stays `Button`); dynamic flagged |
15
- | `Card` | `Card` | Auto |
16
- | `Columns` | `Columns` | Auto; `breakpoint="mobile"`→`isMobile`, `multiline`→`isMultiline`, … |
17
- | `Container` | `Container` | Auto; `breakpoint="fluid"`→`fluid`, `max`→`isMax` |
18
- | `Content` | `Content` | Auto |
19
- | `Dropdown` | `Dropdown` | Structure auto; controlled `value`/`onChange` flagged |
20
- | `Element` | — | Flagged: no generic element; use a semantic component + helper props |
21
- | `Footer` | `Footer` | Auto |
22
- | `Form.*` | flat imports | See Form table below |
23
- | `Heading` | `Title` / `SubTitle` / `<p class="heading">` | Auto for literal `subtitle`/`heading` (by value, not presence — `subtitle={false}` is `Title`); dynamic value flagged |
24
- | `Hero` | `Hero` | Auto; `hasNavbar`→`fullheightWithNavbar`; `halfheight`/`gradient` flagged |
25
- | `Icon` | `Icon` | `<i class="fas fa-x">` children → `name`/`library`/`variant` props; else flagged |
26
- | `Image` | `Image` | Auto; numeric `size={128}` → `size="128x128"`, `rounded`→`isRounded` |
27
- | `Level` | `Level` | Auto; `breakpoint="mobile"`→`isMobile` |
28
- | `Loader` | plain `<div className="loader">` | Auto (Bulma v1 still ships the class) |
29
- | `Media` | `Media` | Auto |
30
- | `Menu` | `Menu` | Auto |
31
- | `Message` | `Message` | Auto; `size` flagged |
32
- | `Modal` | `Modal` | `show`→`active`, `closeOnEsc`→`closeOnEscape`; `closeOnBlur`/`showClose` flagged (compound form renders neither) |
33
- | `Navbar` | `Navbar` | Auto; see dropdown note below |
34
- | `Notification` | `Notification` | Auto; `light`→`isLight` |
35
- | `Pagination` | `Pagination` | `onChange`→`onPageChange`; `delta`/labels/`showFirstLast`/`autoHide` flagged |
36
- | `Panel` | `Panel` | Auto |
37
- | `Progress` | `Progress` | Auto |
38
- | `Section` | `Section` | Auto |
39
- | `Table` | `Table` | Auto; `size="fullwidth"`→`isFullwidth`, `striped`→`isStriped`, … |
40
- | `Tabs` | `Tabs` | Auto; children wrapped in `Tabs.List`; `type="toggle-rounded"`→`toggle rounded` |
41
- | `Tag` | `Tag` | Auto; `remove`→`isDelete`, `rounded`→`isRounded` |
42
- | `Tile` | — | Flagged: Bulma v1 replaced tiles with `Grid`/`Cell` |
9
+ | RBC | bestax | Notes |
10
+ | -------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `Block` | `Block` | Auto |
12
+ | `Box` | `Box` | Auto |
13
+ | `Breadcrumb` | `Breadcrumb` | Auto; `align` → `alignment` (`center`→`centered`) |
14
+ | `Button` | `Button` | Auto for literal `remove` (by value, not presence — truthy → `<Delete/>`, `remove={false}` stays `Button`); dynamic flagged |
15
+ | `Card` | `Card` | Auto |
16
+ | `Columns` | `Columns` | Auto; `breakpoint="mobile"`→`isMobile`, `multiline`→`isMultiline`, … |
17
+ | `Container` | `Container` | Auto; `breakpoint="fluid"`→`fluid`, `max`→`isMax` |
18
+ | `Content` | `Content` | Auto |
19
+ | `Dropdown` | `Dropdown` | Structure auto; controlled `value`/`onChange` flagged |
20
+ | `Element` | — | Flagged: no generic element; use a semantic component + helper props |
21
+ | `Footer` | `Footer` | Auto |
22
+ | `Form.*` | flat imports | See Form table below |
23
+ | `Heading` | `Title` / `SubTitle` / `<p class="heading">` | Auto for literal `subtitle`/`heading` (by value, not presence — `subtitle={false}` is `Title`); dynamic value flagged |
24
+ | `Hero` | `Hero` | Auto; `hasNavbar`→`fullheightWithNavbar`; `halfheight`/`gradient` flagged |
25
+ | `Icon` | `Icon` | `<i class="fas fa-x">` children → `name`/`library`/`variant` props; else flagged |
26
+ | `Image` | `Image` | Auto; numeric `size={128}` → `size="128x128"`, `rounded`→`isRounded` |
27
+ | `Level` | `Level` | Auto; `breakpoint="mobile"`→`isMobile` |
28
+ | `Loader` | plain `<div className="loader">` | Auto (Bulma v1 still ships the class); swap in bestax's `Loader` by hand for its progressbar role and accessible name |
29
+ | `Media` | `Media` | Auto |
30
+ | `Menu` | `Menu` | Auto |
31
+ | `Message` | `Message` | Auto; `size` flagged |
32
+ | `Modal` | `Modal` | `show`→`active`, `closeOnEsc`→`closeOnEscape`; `closeOnBlur`/`showClose` flagged (compound form renders neither) |
33
+ | `Navbar` | `Navbar` | Auto; see dropdown note below |
34
+ | `Notification` | `Notification` | Auto; `light`→`isLight` |
35
+ | `Pagination` | `Pagination` | `onChange`→`onPageChange`, `delta`→`siblingCount`, `next`/`previous`→`nextLabel`/`previousLabel`; `showFirstLast`/`showPrevNext`/`autoHide` flagged |
36
+ | `Panel` | `Panel` | Auto |
37
+ | `Progress` | `Progress` | Auto |
38
+ | `Section` | `Section` | Auto |
39
+ | `Table` | `Table` | Auto; `size="fullwidth"`→`isFullwidth`, `striped`→`isStriped`, … |
40
+ | `Tabs` | `Tabs` | Auto; children wrapped in `Tabs.List`; `type="toggle-rounded"`→`toggle rounded` |
41
+ | `Tag` | `Tag` | Auto; `remove`→`isDelete`, `rounded`→`isRounded` |
42
+ | `Tile` | — | Flagged: Bulma v1 replaced tiles with `Grid`/`Cell` |
43
43
 
44
44
  ## Compound sub-components
45
45