create-bestax 4.2.12 → 4.2.14

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 (57) hide show
  1. package/README.md +22 -9
  2. package/dist/cli.d.ts +13 -3
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +20 -4
  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/index.js +2 -2
  12. package/dist/project-creator.d.ts +21 -1
  13. package/dist/project-creator.d.ts.map +1 -1
  14. package/dist/project-creator.js +84 -51
  15. package/dist/prompts.d.ts +7 -2
  16. package/dist/prompts.d.ts.map +1 -1
  17. package/dist/prompts.js +8 -3
  18. package/dist/validators.d.ts +7 -0
  19. package/dist/validators.d.ts.map +1 -1
  20. package/dist/validators.js +13 -1
  21. package/package.json +6 -3
  22. package/templates/skills/bestax-custom-component/SKILL.md +21 -4
  23. package/templates/skills/bestax-custom-component/examples/stat-card.tsx +3 -4
  24. package/templates/skills/bestax-custom-component/references/api.md +103 -11
  25. package/templates/skills/bestax-custom-component/references/component-catalog.md +12 -5
  26. package/templates/skills/bestax-custom-component/references/library-contributor.md +23 -0
  27. package/templates/skills/bestax-form/SKILL.md +28 -17
  28. package/templates/skills/bestax-form/references/api.md +83 -23
  29. package/templates/skills/bestax-form/references/patterns.md +19 -0
  30. package/templates/skills/bestax-icons/SKILL.md +8 -6
  31. package/templates/skills/bestax-icons/examples/icon-usage.tsx +3 -3
  32. package/templates/skills/bestax-icons/references/icon-libraries.md +3 -3
  33. package/templates/skills/bestax-layout-scaffold/SKILL.md +17 -4
  34. package/templates/skills/bestax-layout-scaffold/examples/app-shell.tsx +1 -1
  35. package/templates/skills/bestax-layout-scaffold/examples/card-grid.tsx +0 -1
  36. package/templates/skills/bestax-layout-scaffold/examples/centered.tsx +0 -1
  37. package/templates/skills/bestax-layout-scaffold/examples/content-page.tsx +0 -1
  38. package/templates/skills/bestax-layout-scaffold/examples/landing.tsx +1 -1
  39. package/templates/skills/bestax-layout-scaffold/references/layout-components.md +22 -15
  40. package/templates/skills/bestax-migrate/SKILL.md +12 -7
  41. package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +9 -9
  42. package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +19 -2
  43. package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +286 -41
  44. package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +125 -48
  45. package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +221 -39
  46. package/templates/skills/bestax-migrate/references/rbx/component-map.md +15 -15
  47. package/templates/skills/bestax-migrate/references/rbx/prop-map.md +17 -19
  48. package/templates/skills/bestax-migrate/references/rbx/unmappables.md +17 -0
  49. package/templates/skills/bestax-migrate/references/react-bulma-components/component-map.md +34 -34
  50. package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +34 -7
  51. package/templates/skills/bestax-optimize/SKILL.md +13 -8
  52. package/templates/skills/bestax-optimize/references/modular-build.md +29 -6
  53. package/templates/skills/bestax-theming/SKILL.md +4 -3
  54. package/templates/skills/bestax-theming/examples/dark-mode.tsx +5 -4
  55. package/templates/skills/bestax-theming/examples/theme-config.tsx +2 -2
  56. package/templates/skills/bestax-theming/references/css-variables.md +51 -8
  57. package/templates/skills/bestax-theming/references/themeable-components.md +12 -10
@@ -76,29 +76,75 @@ renders the same `<h2>`.
76
76
 
77
77
  ### Other components
78
78
 
79
- | Component | Classes | Prop |
80
- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
81
- | `.content` → `Content` | `is-small`, `is-medium`, `is-large` | `size` |
82
- | `.delete` → `Delete` | `is-small`, `is-medium`, `is-large` | `size` |
83
- | `.hero` → `Hero` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-white`, `is-light`, `is-dark` | `color` |
84
- | `.hero` → `Hero` | `is-small`, `is-medium`, `is-large`, `is-fullheight`, `is-fullheight-with-navbar` | `size` |
85
- | `.level` → `Level` | `is-mobile` | `isMobile` |
86
- | `.notification` → `Notification` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-white`, `is-dark` | `color` |
87
- | `.notification` → `Notification` | `is-light` | `isLight` |
88
- | `.progress` → `Progress` | the ten `.hero` colors | `color` |
89
- | `.progress` → `Progress` | `is-small`, `is-medium`, `is-large` | `size` |
90
- | `.section` → `Section` | `is-medium`, `is-large` | `size` |
91
- | `.table` → `Table` | `is-bordered`, `is-striped`, `is-narrow`, `is-hoverable`, `is-fullwidth` | `isBordered`, `isStriped`, `isNarrow`, `isHoverable`, `isFullwidth` |
92
- | `.tag` → `Tag` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-dark`, `is-white` | `color` |
93
- | `.tag` → `Tag` | `is-light`, `is-rounded`, `is-hoverable` | `isLight`, `isRounded`, `isHoverable` |
94
- | `.tag` → `Tag` | `is-medium`, `is-large` | `size` |
79
+ | Component | Classes | Prop |
80
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
81
+ | `.breadcrumb` → `Breadcrumb` | `is-centered`, `is-right` | `alignment` |
82
+ | `.breadcrumb` → `Breadcrumb` | `has-arrow-separator`, `has-bullet-separator`, `has-dot-separator`, `has-succeeds-separator` | `separator` |
83
+ | `.breadcrumb` → `Breadcrumb` | `is-small`, `is-medium`, `is-large` | `size` |
84
+ | `.card-header-title` → `Card.Header.Title` | `is-centered` | `centered` |
85
+ | `.content` → `Content` | `is-small`, `is-medium`, `is-large` | `size` |
86
+ | `.delete` → `Delete` | `is-small`, `is-medium`, `is-large` | `size` |
87
+ | `.field` → `Field` | `is-horizontal`, `is-grouped`, `has-addons`, `is-narrow` | `horizontal`, `grouped`, `hasAddons`, `narrow` |
88
+ | `.grid` → `Grid` | `is-gap-{step}`, `is-column-gap-{step}`, `is-row-gap-{step}`, `0` to `8` in half steps (`is-gap-1.5`) | `gap`, `columnGap`, `rowGap` |
89
+ | `.grid` → `Grid` | `is-col-min-{1-32}` | `minCol` (a number: `minCol={8}`) |
90
+ | `.fixed-grid` → `Grid isFixed` (folded) | `has-{1-12}-cols`, `has-{1-12}-cols-{viewport}`, `has-auto-count` | `fixedCols`, `fixedCols{Viewport}` (numbers: `fixedCols={3}`), `fixedCols="auto"` |
91
+ | `.cell` → `Cell` | `is-col-start-{n}`, `is-col-from-end-{n}`, `is-col-span-{n}`, `is-row-start-{n}`, `is-row-from-end-{n}`, `is-row-span-{n}`, for `n` from 1 to 12 | `colStart`, `colFromEnd`, `colSpan`, `rowStart`, `rowFromEnd`, `rowSpan` (numbers: `colSpan={2}`) |
92
+ | `.field-label` → `Field.Label` | `is-small`, `is-normal`, `is-medium`, `is-large` | `size` |
93
+ | `.control` → `Control` | `has-icons-left`, `has-icons-right`, `is-loading`, `is-expanded` | `hasIconsLeft`, `hasIconsRight`, `isLoading`, `isExpanded` |
94
+ | `.control` → `Control`, `.input` → `InputBase`, `.textarea` → `TextAreaBase` | `is-small`, `is-medium`, `is-large` | `size` |
95
+ | `.input` → `InputBase`, `.textarea` → `TextAreaBase` | `is-rounded`, `is-static`, `is-hovered`, `is-focused` | `isRounded`, `isStatic`, `isHovered`, `isFocused` |
96
+ | `.input` → `InputBase` | `is-loading` | `isLoading` |
97
+ | `.textarea` → `TextAreaBase` | `is-active`, `has-fixed-size` | `isActive`, `hasFixedSize` |
98
+ | `.select` → `SelectBase` | `is-small`, `is-medium`, `is-large` | `size` |
99
+ | `.select` → `SelectBase` | `is-rounded`, `is-loading`, `is-active`, `is-fullwidth` | `isRounded`, `isLoading`, `isActive`, `isFullwidth` |
100
+ | `.select` → `SelectBase` | `is-multiple`, around a `<select multiple>` | `multiple` (the `<select>`'s `size` becomes the number `multipleSize`) |
101
+ | `.file` → `File` | `is-small`, `is-medium`, `is-large` | `size` |
102
+ | `.file` → `File` | `is-boxed`, `is-fullwidth`, `has-name`, `is-right` | `isBoxed`, `isFullwidth`, `hasName`, `isRight` |
103
+ | the `<select>` inside `.select` | `is-hovered`, `is-focused` | `isHovered`, `isFocused` |
104
+ | `.hero` → `Hero` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-white`, `is-light`, `is-dark` | `color` |
105
+ | `.hero` → `Hero` | `is-small`, `is-medium`, `is-large`, `is-fullheight`, `is-fullheight-with-navbar` | `size` |
106
+ | `.image` → `Image` | `is-16x16`, `is-24x24`, `is-32x32`, `is-48x48`, `is-64x64`, `is-96x96`, `is-128x128`, `is-square` | `size` (a ratio such as `is-4by3` stays a class, since `size` would add `has-ratio`) |
107
+ | the `<img>` inside `.image` | `is-rounded` | `isRounded` |
108
+ | `.icon` → `Icon` | `is-small`, `is-medium`, `is-large` | `size` |
109
+ | `.tabs` → `Tabs` | `is-centered`, `is-right`, `is-left` | `align` |
110
+ | `.tabs` → `Tabs` | `is-small`, `is-medium`, `is-large` | `size` |
111
+ | `.tabs` → `Tabs` | `is-fullwidth`, `is-boxed`, `is-toggle`, `is-toggle-rounded` | `isFullwidth`, `boxed`, `toggle`, `rounded` (`is-<color>` stays a class: `color` is deprecated) |
112
+ | the `<a>` in a menu item → `Menu.Item` | `is-active` | `active` |
113
+ | `.pagination` → `Pagination` | `is-small`, `is-medium`, `is-large` | `size` |
114
+ | `.pagination` → `Pagination` | `is-centered`, `is-right`, `is-rounded` | `align`, `rounded` (`is-<color>` stays a class: `color` is deprecated) |
115
+ | `.pagination-link` → `Pagination.Link` | `is-current`, beside an `aria-current` of the element's own | `active` (without one, it stays a class; `is-disabled` stays a class) |
116
+ | `.level` → `Level` | `is-mobile` | `isMobile` |
117
+ | `.navbar` → `Navbar` | the ten `.hero` colors | `color` |
118
+ | `.navbar` → `Navbar` | `is-fixed-top`, `is-fixed-bottom` | `fixed` (`top`, `bottom`) |
119
+ | `.navbar` → `Navbar` | `is-transparent` | `transparent` |
120
+ | `.navbar-menu` → `Navbar.Menu`, `.navbar-item` → `Navbar.Item` | `is-active` | `active` |
121
+ | `.navbar-dropdown` → `Navbar.DropdownMenu` | `is-right`, `is-up` | `right`, `up` |
122
+ | `.notification` → `Notification` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-white`, `is-dark` | `color` |
123
+ | `.notification` → `Notification` | `is-light` | `isLight` |
124
+ | `.progress` → `Progress` | the ten `.hero` colors | `color` |
125
+ | `.progress` → `Progress` | `is-small`, `is-medium`, `is-large` | `size` |
126
+ | `.section` → `Section` | `is-medium`, `is-large` | `size` |
127
+ | `.skeleton-lines` → `Skeleton variant="lines"` | its bare, empty `<div>`s | `lines` (their count, as a number: `lines={5}`) |
128
+ | `.table` → `Table` | `is-bordered`, `is-striped`, `is-narrow`, `is-hoverable`, `is-fullwidth` | `isBordered`, `isStriped`, `isNarrow`, `isHoverable`, `isFullwidth` |
129
+ | `.tag` → `Tag` | `is-primary`, `is-link`, `is-info`, `is-success`, `is-warning`, `is-danger`, `is-black`, `is-dark`, `is-white` | `color` |
130
+ | `.tag` → `Tag` | `is-light`, `is-rounded`, `is-hoverable` | `isLight`, `isRounded`, `isHoverable` |
131
+ | `.tag` → `Tag` | `is-medium`, `is-large` | `size` |
95
132
 
96
133
  `Progress` takes `value` and `max` as numbers: `value="40"` becomes `value={40}`. An
97
134
  expression (`value={percent}`) carries over as written, so if it holds a string the migrated
98
135
  file fails to typecheck while still rendering the same; wrap it in `Number(…)`. `Delete`
99
136
  converts only with `type` and `aria-label` set (see `defaults:<Target>` in the unmappables),
100
- and a `type="button"` is dropped, since bestax renders it by itself. `.tag`'s `is-delete` stays
101
- a class: `isDelete` turns the tag into a `<button>`.
137
+ and a `type="button"` is dropped, since bestax renders it by itself. `Card.Header.Icon` likewise
138
+ converts only with an `aria-label` and a `type` of `button`, `submit` or `reset`, since it renders
139
+ `aria-label="more options"` and `type="button"` otherwise.
140
+ `.tag`'s `is-delete` stays a class: `isDelete` turns the tag into a `<button>`. `.navbar`'s
141
+ `is-spaced` and `has-shadow`, and a `.navbar-item`'s `has-dropdown` and `is-hoverable`, stay
142
+ classes too: bestax has no prop for them on those components. So do a `.field`'s
143
+ `has-addons-centered`, `has-addons-right` and `is-grouped-*`, because the `hasAddons` and
144
+ `grouped` values that render them render `has-addons` and `is-grouped` as well, and an input's,
145
+ textarea's or select's color, because `color` renders `has-text-<color>` on it too. On a
146
+ `.columns`, the gap helpers (`is-gap-*`, `is-column-gap-*`, `is-row-gap-*`) stay classes as well:
147
+ `Columns`' own `gap` is its gutter (`is-<step>`), and it leaves the column and row gap helpers out.
102
148
 
103
149
  ## Helper classes
104
150
 
@@ -106,35 +152,59 @@ These convert on every component above and on the plain-tag wrappers.
106
152
 
107
153
  Colors are `primary`, `link`, `info`, `success`, `warning`, `danger`, `black`, `black-bis`,
108
154
  `black-ter`, `grey-darker`, `grey-dark`, `grey`, `grey-light`, `grey-lighter`, `white`,
109
- `white-bis`, `white-ter`, `light`, `dark`, `inherit` and `current`.
110
-
111
- | Classes | Prop |
112
- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
113
- | `m-{n}`, `mt-{n}`, `mr-{n}`, `mb-{n}`, `ml-{n}`, `mx-{n}`, `my-{n}` (`0` to `6`, `auto`) | `m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my` |
114
- | `p-{n}`, `pt-{n}`, `pr-{n}`, `pb-{n}`, `pl-{n}`, `px-{n}`, `py-{n}` | `p`, `pt`, `pr`, `pb`, `pl`, `px`, `py` |
115
- | `has-text-{color}` | `textColor` (on `Table`, `color`) |
116
- | `has-background-{color}` | `bgColor` (on `Tag`, `backgroundColor`) |
117
- | `is-size-1` … `is-size-7`, and `-{viewport}` | `textSize`, `textSize{Viewport}` |
118
- | `has-text-centered`, `-justified`, `-left`, `-right`, and `-{viewport}` | `textAlign`, `textAlign{Viewport}` |
119
- | `is-capitalized`, `is-lowercase`, `is-uppercase`, `is-italic` | `textTransform` |
120
- | `has-text-weight-light`, `-normal`, `-medium`, `-semibold`, `-bold` | `textWeight` |
121
- | `is-family-sans-serif`, `-monospace`, `-primary`, `-secondary`, `-code` | `fontFamily` |
122
- | `is-block`, `is-flex`, `is-inline`, `is-inline-block`, `is-inline-flex`, `is-grid` | `display` |
123
- | the same with any of the nine viewports, `touch` and the `-only` ones included | `display{Viewport}` |
124
- | `is-hidden`, `is-invisible`, `is-sr-only` | `visibility` |
125
- | `is-hidden-{viewport}`, `is-invisible-{viewport}`, all nine viewports | `visibility{Viewport}` |
126
- | `is-flex-direction-*`, `is-flex-wrap-*`, `is-justify-content-*`, `is-align-content-*`, `is-align-items-*` | `flexDirection`, `flexWrap`, `justifyContent`, `alignContent`, `alignItems` |
127
- | `is-align-self-*`, `is-flex-grow-*`, `is-flex-shrink-*` | `alignSelf`, `flexGrow`, `flexShrink` |
128
- | `is-pulled-left`, `is-pulled-right` | `float` |
129
- | `is-clipped` | `overflow="clipped"` |
130
- | `is-overlay`, `is-skeleton`, `is-clearfix`, `is-relative` | `overlay`, `skeleton`, `clearfix`, `relative` |
131
- | `is-unselectable`, `is-clickable` | `interaction` |
132
- | `is-radiusless`, `is-shadowless` | `radius`, `shadow` |
133
- | `is-mobile`, `is-narrow` (where the component has no prop of its own for them) | `responsive` |
155
+ `white-bis`, `white-ter`, `light`, `dark`, `inherit` and `current`. Gap steps are `0` to `8` in
156
+ half steps (`0`, `0.5`, `1`, … `7.5`, `8`). Overflow values are `auto`, `clip`, `hidden`, `scroll`
157
+ and `visible`. Ratios are `1by1`, `5by4`, `4by3`, `3by2`, `5by3`, `16by9`, `2by1`, `3by1`,
158
+ `4by5`, `3by4`, `2by3`, `3by5`, `9by16`, `1by2` and `1by3`.
159
+
160
+ | Classes | Prop |
161
+ | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
162
+ | `m-{n}`, `mt-{n}`, `mr-{n}`, `mb-{n}`, `ml-{n}`, `mx-{n}`, `my-{n}` (`0` to `6`, `auto`) | `m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my` |
163
+ | `p-{n}`, `pt-{n}`, `pr-{n}`, `pb-{n}`, `pl-{n}`, `px-{n}`, `py-{n}` | `p`, `pt`, `pr`, `pb`, `pl`, `px`, `py` |
164
+ | `has-text-{color}` | `textColor` (on `Menu`, `Menu.Label`, `Menu.List` and `Table`, `color`) |
165
+ | `has-background-{color}` | `bgColor` (on `Menu`, `Menu.Label`, `Menu.List`, `Tag`, `File`, `InputBase`, `TextAreaBase` and `SelectBase`, `backgroundColor`) |
166
+ | `is-size-1` … `is-size-7`, and `-{viewport}` | `textSize`, `textSize{Viewport}` |
167
+ | `has-text-centered`, `-justified`, `-left`, `-right`, and `-{viewport}` | `textAlign`, `textAlign{Viewport}` |
168
+ | `is-capitalized`, `is-lowercase`, `is-uppercase`, `is-italic` | `textTransform` |
169
+ | `has-text-weight-light`, `-normal`, `-medium`, `-semibold`, `-bold` | `textWeight` |
170
+ | `is-family-sans-serif`, `-monospace`, `-primary`, `-secondary`, `-code` | `fontFamily` |
171
+ | `is-block`, `is-flex`, `is-inline`, `is-inline-block`, `is-inline-flex`, `is-grid` | `display` |
172
+ | the same with any of the nine viewports, `touch` and the `-only` ones included | `display{Viewport}` |
173
+ | `is-hidden`, `is-invisible`, `is-sr-only` | `visibility` |
174
+ | `is-hidden-{viewport}`, `is-invisible-{viewport}`, all nine viewports | `visibility{Viewport}` |
175
+ | `is-flex-direction-*`, `is-flex-wrap-*`, `is-justify-content-*`, `is-align-content-*`, `is-align-items-*` | `flexDirection`, `flexWrap`, `justifyContent`, `alignContent`, `alignItems` |
176
+ | `is-align-self-*`, `is-flex-grow-*`, `is-flex-shrink-*` | `alignSelf`, `flexGrow`, `flexShrink` |
177
+ | `is-pulled-left`, `is-pulled-right` | `float` |
178
+ | `is-gap-{step}`, `is-column-gap-{step}`, `is-row-gap-{step}` | `gap`, `columnGap`, `rowGap` |
179
+ | `is-gapless` | `gapless` |
180
+ | `is-position-absolute`, `-fixed`, `-relative`, `-static`, `-sticky` | `pos` |
181
+ | `is-relative` | `relative` |
182
+ | `is-clipped` | `overflow="clipped"` |
183
+ | `is-overflow-{value}`, `is-overflow-x-{value}`, `is-overflow-y-{value}` | `overflow`, `overflowX`, `overflowY` |
184
+ | `is-overlay`, `is-skeleton`, `is-clearfix` | `overlay`, `skeleton`, `clearfix` |
185
+ | `is-unselectable`, `is-clickable` | `interaction` |
186
+ | `is-radiusless`, `has-radius-small`, `-normal`, `-large`, `-rounded` | `radius` |
187
+ | `is-shadowless` | `shadow` |
188
+ | `is-aspect-ratio-{ratio}` | `aspectRatio` |
189
+ | `is-mobile`, `is-narrow` (where the component has no prop of its own for them) | `responsive` |
134
190
 
135
191
  Where a component renders a color class through no typed prop, the class stays: `has-text-*`
136
- on `Hero`, `Progress`, `Tag` and `Tags`; `has-background-*` on `Notification`, `Progress`,
137
- `Table` and `Tags`.
192
+ on `Breadcrumb`, `Hero`, `Navbar.DropdownMenu`, `Navbar.Divider`, `Field.Label`, `Field.Body`,
193
+ `Control`, `File`, `InputBase`, `TextAreaBase`, `SelectBase`, `Progress`, `Skeleton`, `Tag`, `Tags`,
194
+ `Modal.Background`, `Modal.Content`, `Modal.Card`, `Modal.Card.Head`, `Modal.Card.Title`,
195
+ `Modal.Card.Body`, `Modal.Card.Foot`, `Pagination.Previous`, `Pagination.Next`,
196
+ `Pagination.Ellipsis`, `Panel`, `Panel.Heading`, `Panel.Tabs`, `Panel.Block` and `Tabs`;
197
+ `has-background-*` on `Breadcrumb`,
198
+ `Navbar.Brand`, `Navbar.Menu`, `Navbar.Start`, `Navbar.End`, `Navbar.DropdownMenu`,
199
+ `Navbar.Divider`, `Field.Label`, `Field.Body`, `Control`, `Notification`, `Progress`, `Skeleton`,
200
+ `Table`, `Tags`, `Modal.Background`, `Modal.Content`, `Modal.Card`, `Modal.Card.Head`,
201
+ `Modal.Card.Title`, `Modal.Card.Body`, `Modal.Card.Foot`, `Pagination.Previous`,
202
+ `Pagination.Next`, `Pagination.Ellipsis`, `Panel`, `Panel.Heading`, `Panel.Tabs`, `Panel.Block`
203
+ and `Tabs`. `Navbar.DropdownMenu`, `Navbar.Divider`, `Field.Label`, `Field.Body`, `Control`,
204
+ `Skeleton`, Modal's parts, `Pagination.Previous`, `Pagination.Next`, `Pagination.Ellipsis`,
205
+ `Panel.Heading`, `Panel.Tabs` and `Panel.Block` take no helper props the way the codemod needs, so every helper class on them
206
+ stays. A `.panel`'s `is-<color>` stays a class too, since `Panel`'s `color` renders
207
+ `has-text-<color>` beside it.
138
208
 
139
209
  Some classes stay put because of how bestax renders them:
140
210
 
@@ -142,12 +212,19 @@ Some classes stay put because of how bestax renders them:
142
212
  class: bestax drops the base `display` whenever a per-viewport one is set.
143
213
  - The flex-container classes (`is-justify-content-*` and the others in that row) convert only
144
214
  beside a flex `display` prop, the only place bestax renders them.
215
+ - `is-gapless` beside an `is-gap-*` keeps its class: bestax drops `gapless` when a `gap` is set.
216
+ On a `.grid` both convert, since `Grid`'s own `gap` renders its class beside `gapless`.
217
+ - `is-relative` beside an `is-position-*` keeps its class: `pos` decides the position, and bestax
218
+ drops `relative` beside it. `is-overlay` converts beside either.
219
+ - A both-axes overflow (`is-overflow-hidden`, `is-clipped`) beside an `is-overflow-x-*` or
220
+ `is-overflow-y-*` keeps its class: beside an axis prop, bestax writes `overflow` per axis
221
+ instead of as its own class.
145
222
  - `is-hidden` becomes `visibility="hidden"`, which renders the same class and doesn't interact
146
223
  with `display`.
147
224
 
148
225
  ## Classes that stay classes
149
226
 
150
227
  Color shades (`has-text-primary-65`), the `-touch` and `-only` breakpoints of text size and
151
- alignment, Grid and `.image` modifiers, and the Bulma helpers with no bestax prop
152
- (`is-display-*`, `is-overflow-*`, `is-position-*`, `is-float-*`, `has-radius-*`, …) stay in
153
- `className`. They render exactly as before.
228
+ alignment, Grid and `.image` modifiers away from their own element, an `.image` ratio, and the
229
+ Bulma helpers bestax has no prop for (`is-display-*`, `is-visibility-*`, `is-float-*`,
230
+ `is-clear-*`, …) stay in `className`. They render exactly as before.
@@ -12,20 +12,21 @@ attribute, never one of your own classes.
12
12
  The element spreads props (`<div className="box" {...rest}>`). A spread can carry anything,
13
13
  and bestax reads some names as its own props: a spread `className` merges with the component's
14
14
  classes, where on the plain element it replaced them. Convert by hand once you know what the
15
- spread carries, or leave it.
15
+ spread carries, or leave it. A spread on the `<select>` inside a `.select` counts too, since what it
16
+ carries becomes `SelectBase`'s.
16
17
 
17
18
  ### `ref:<Target>`
18
19
 
19
20
  The element has a `ref`, and the bestax component doesn't forward one, so the ref would stop
20
- reaching the DOM node. Only `Button` and `Link` forward refs among the components this source
21
- converts. Keep the element as markup.
21
+ reaching the DOM node. Keep the element as markup.
22
22
 
23
23
  ### `tag:<Target>`
24
24
 
25
- bestax renders that component on a fixed tag, or a short list of them through `as` (see the
25
+ The codemod converts that class on a fixed tag, or a short list of them through `as` (see the
26
26
  **Tags** column in [component-map.md](component-map.md)), and this element is on another one:
27
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.
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.
29
30
 
30
31
  ### `attr:<prop>`
31
32
 
@@ -35,19 +36,58 @@ color. Rename or drop the attribute, then re-run.
35
36
 
36
37
  It also covers an attribute the component's props type rejects. `Delete` takes no `type`
37
38
  (`type="button"` is the one value that converts, because `Delete` renders it anyway), and
38
- `Progress` types `value` and `max` as numbers, so `value="half"` stays. A number converts only
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
39
41
  when it is spelled the way it renders (`value="40"`, not `value="040"`).
40
42
 
41
43
  `attr:dangerouslySetInnerHTML` is the same kind of refusal: the element sets its own content,
42
44
  and some bestax components render content of their own beside `children`, which React
43
- rejects. Keep the element as markup.
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.
44
69
 
45
70
  ### `defaults:<Target>`
46
71
 
47
72
  The component renders attributes of its own when the element doesn't set them. `Delete`
48
73
  renders `type="button"` and `aria-label="Close"`. On a bare `<button className="delete">`,
49
74
  converting would add both, changing a submit button inside a form into a plain button. Add the
50
- attributes you want (usually both, with a real label), then re-run.
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` hides an icon with no name, no `role` and no `tabIndex`, with `aria-hidden="true"`, and
80
+ makes one named by `aria-label` or `aria-labelledby` an image, with `role="img"`. Give a decorative
81
+ `.icon` `aria-hidden="true"`, and a meaningful one an `aria-label` that says what it means and
82
+ `role="img"`. One whose name is an expression may render with no name, so it needs a `role` of its
83
+ own.
84
+ `Pagination` renders Bulma's own `role="navigation"` and `aria-label="pagination"`, and
85
+ `Pagination.Previous`, `Pagination.Next` and `Pagination.Link` render `tabIndex={0}`, so give each
86
+ element the attribute it's missing.
87
+ `Card.FooterItem`, `Card.Header.Icon` and `Navbar.Item` render `type="button"` on a `<button>` that
88
+ sets none, and `Card.Header.Icon` renders `aria-label="more options"` too. One whose `type` isn't
89
+ `button`, `submit` or `reset` gets an `attr:type` TODO instead, since bestax writes `button` in its
90
+ place. Write the type you mean, then re-run.
51
91
 
52
92
  ### `drops:<Target>`
53
93
 
@@ -59,11 +99,133 @@ on an `<a>`. Remove the attribute, then re-run.
59
99
  (`<a className="button" disabled>`), but Bulma greys out a disabled `.button` on any tag, so
60
100
  dropping it would change how the element looks. Keep that element as markup.
61
101
 
102
+ So is `name` on an `<a>`, which `Card.FooterItem` drops: browsers still scroll a `#fragment` link
103
+ to it. Keep that element as markup, or move the target to an `id` (which it keeps), then re-run.
104
+
62
105
  ### `children:<Target>`
63
106
 
64
107
  The component's props type requires children, and the element has none (`Buttons`). An empty
65
108
  `.buttons` does nothing; delete it or give it buttons.
66
109
 
110
+ On `Card` and `Card.Header` it means the children decide. `Card` renders its children inside a
111
+ `.card-content` of its own unless one of them is one of its parts (`Card.Header`,
112
+ `Card.Header.Icon`, `Card.Image`, `Card.Content`, `Card.Footer`, `Card.FooterItem`), and
113
+ `Card.Header` renders a `.card-header-title` of its own unless one of its children is a
114
+ `Card.Header.Title`. None of this element's direct children converted to one, so converting it
115
+ would add that wrapper. Look at the TODOs on the children first: a title that stays markup keeps
116
+ its header as markup too. A card whose content sits straight inside it, with no
117
+ `.card-content`, has no part to find; wrap that content in `Card.Content` if the extra padding
118
+ is what you want, or keep the markup. A part inside an expression (`{open && <div className="card-content">}`)
119
+ doesn't count, because it may not render.
120
+
121
+ On a `.table-container` or `.fixed-grid` it's the wrapper case. `Table isResponsive` and
122
+ `Grid isFixed` render that wrapper themselves, around nothing but the table or grid, so the
123
+ wrapper folds into them only when it holds that one element and nothing else. A caption, a
124
+ comment or a second element beside it keeps the wrapper as markup; move it outside the wrapper,
125
+ then re-run. An attribute or an extra class on the wrapper keeps it too, because the component
126
+ renders the wrapper bare.
127
+
128
+ On a `.select`, a `.breadcrumb` or an `.image` it's the same shape from the other side. `SelectBase`
129
+ renders the `<select>` inside `.select` itself, `Breadcrumb` the `<ul>` inside `.breadcrumb` and
130
+ `Image` the `<img>` inside `.image`. So `SelectBase` and `Breadcrumb` convert only around that one
131
+ element, with nothing else beside it. `Image` renders anything else it's given as it is, so an
132
+ `.image` converts around HTML elements written out either way. It gets this TODO when it's empty,
133
+ or when what's inside is an expression or text, since `Image` renders an `<img>` of its own when
134
+ its children come out empty. Keep it as markup, or write the `Image` by hand if the expression
135
+ never is.
136
+
137
+ A menu item is the same from the `<li>`'s side: `Menu.Item` renders the `<a>` inside it, and a
138
+ `Menu.List` after that, so an item converts only when its `<li>` holds one `<a>` and at most one
139
+ `<ul>` after it, with nothing else beside them. Text or a comment beside the `<a>` keeps it as
140
+ markup, and so does a router link or a `<button>` in its place: write a router link by hand as
141
+ `<Menu.Item as={Link}>`. The nested `<ul>` converts only bare (no class, no attributes) and
142
+ holding its items, as Bulma nests one, and the `<a>` has to hold something, since `Menu.Item`
143
+ requires children.
144
+
145
+ On a `.pagination-ellipsis`, `Pagination.Ellipsis` renders its own `…` whatever it's given, so the
146
+ element converts only holding exactly that (`&hellip;` or the character), and closes itself.
147
+
148
+ On an `.icon-text`, `IconText` builds its icons from props and wraps each text in a `<span>` of
149
+ its own, so it converts only when its children are `.icon`s, each followed by at most one bare
150
+ `<span>` of static text. Each `.icon` has to convert to `Icon` on its own, so one with no
151
+ `aria-label` gets a `defaults:Icon` TODO of its own first: give it one that says what the icon
152
+ means, then re-run. Its `<i>` has to be bare and empty and name one Font Awesome or Material Design
153
+ Icons glyph, so an `aria-hidden` on the `<i>`, another icon font, or anything between the children
154
+ keeps it as markup, and the `.icon`s inside convert on their own. The markup renders the same as
155
+ it is; to use `IconText` anyway, write it by hand with `iconProps={{ name: ... }}` (the
156
+ `bestax-icons` skill covers `Icon`'s library and name props).
157
+
158
+ On a `.file`, `File` renders the whole tree from props, so it converts only when its tree is the one
159
+ `File` renders: one bare `.file-label` `<label>` holding a `.file-input` `<input type="file">`, a
160
+ bare `.file-cta` `<span>` with a bare `.file-label` `<span>` and at most one bare `.file-icon`
161
+ `<span>` on each side of it, and, with `has-name`, at most one bare `.file-name` `<span>` of
162
+ static text. A class or attribute on any part of the tree, text between the parts, or content
163
+ that isn't text and elements written out keeps it as markup. Rebuild it as one `File` by hand
164
+ (its API page lists the props for the button text, the file name and the icons), or keep the
165
+ markup, which renders the same.
166
+
167
+ On a `.skeleton-lines`, `Skeleton` renders the children itself: `lines` bare, empty `<div>`s. So
168
+ the element converts only when its children are just that, and a class, an attribute, text or a
169
+ comment in one of them, or anything else beside them, keeps it as markup. Keep it if the
170
+ children matter; otherwise make them bare `<div>`s, then re-run.
171
+
172
+ ### `context:<Target>`
173
+
174
+ `Field` and `Control` tell bestax's form controls inside them to skip their own `.field` and
175
+ `.control` wrappers. A `.field` or `.control` that already holds a bestax component (an `Input`
176
+ from an earlier, partial migration) would change how that component renders once converted, so
177
+ it stays markup. Convert it by hand and check that the component inside still renders what you
178
+ want, or leave it.
179
+
180
+ On a `.file` it's the other way round: `File` renders a `.field` of its own unless it sits inside
181
+ a `Field`, so a `.file` converts only inside a bestax `Field` or a `.field` that converts in the
182
+ same run. Convert the `.field` around it (its own TODO says what keeps it), then re-run.
183
+
184
+ On an input or textarea it's the other direction: inside a bestax `Field` with a `label`,
185
+ `InputBase` and `TextAreaBase` take the Field's generated `id` when they have none, which the
186
+ element didn't have. Context follows what renders, not what the file says, so an input with no
187
+ `id` inside any other component of the app stays markup too, since that component could render
188
+ a labelled `Field` around it. Give the element its own `id`, then re-run.
189
+
190
+ The codemod reads one file at a time, so it can't see a component in another file that renders
191
+ a bestax `Field` or form control around this markup. If the app already uses them that way, give
192
+ its inputs ids before running the codemod, and check its forms afterwards.
193
+
194
+ On a `.tabs` it's the same shape as a `.field`: `Tabs` passes its active tab to the `Tabs.Tab`s
195
+ and `Tabs.Content.Item`s inside it, and renders differently around a `Tabs.Content`, so a `.tabs`
196
+ that already holds one of those stays markup. Only the ones written in the file count: a
197
+ component of the app inside the `.tabs` that renders one itself would start following the
198
+ active tab once the `.tabs` converts, so check tabs built from your own components after the run.
199
+
200
+ On a `.modal-background`, `.modal-content` or `.modal-card` it's about the `Modal` around it. A
201
+ bestax `Modal` already in the file picks what it renders from its children: with one of those
202
+ parts among them it renders them as given, and without one it adds its own background, wrapper
203
+ and close button. So converting the part would change the `Modal`, and the part stays markup.
204
+ Convert the `Modal` and its children together by hand.
205
+
206
+ On a `.menu-list` it's about nesting: `Menu.List` renders `.menu-list` only on the outermost list,
207
+ and drops it on one inside another. So a `.menu-list` inside another `.menu-list` (or a bestax
208
+ `Menu.List`) stays markup, keeping its class, and so does one around a bestax `Menu.List`, which would
209
+ lose the class once the outer one converts. Only the elements in the same file count:
210
+ a `.menu-list` another component renders inside a `Menu.List` would lose the class, so check a
211
+ menu split across components after the run.
212
+
213
+ On a `.pagination-link` or a `.pagination-ellipsis` it's the `<li>` around it: `Pagination.Link`
214
+ and `Pagination.Ellipsis` render a bare `<li>` of their own, so each converts only as the only
215
+ thing inside a bare `<li>`, which it takes the place of, `key` and all. A class or an attribute
216
+ on the `<li>`, or anything beside the element in it, keeps it as markup. A `.pagination-link`'s
217
+ `is-current` becomes `active` only beside an `aria-current` of its own, since `active` renders
218
+ `aria-current="page"` otherwise; without one it stays a class. And `Pagination.Ellipsis` writes
219
+ a `className` it's given in place of `.pagination-ellipsis`, so another class on it keeps it as
220
+ markup with `attr:className`.
221
+
222
+ A menu item with a nested list is the same rule from inside: its nested `<ul>` becomes a
223
+ `Menu.List`, which renders `.menu-list` unless another `Menu.List` is around it. So the item
224
+ converts only inside a list that is a `Menu.List` already or becomes one in the same run, and an
225
+ item inside a `.menu-list` that stays markup (one that spreads props, say) keeps its nested list,
226
+ and itself, as markup with a `context:Menu.Item` TODO. An item with no nested list renders the
227
+ same anywhere, so it converts either way.
228
+
67
229
  ### `only-child:<Target>`
68
230
 
69
231
  The element is the only child of another component (`<Link href="/x"><a className="button">`),
@@ -73,20 +235,37 @@ Convert it by hand if the parent only renders its children.
73
235
 
74
236
  ### `dynamic-class:<Target>`
75
237
 
76
- The `className` is computed (`clsx(...)`, a ternary, a template with expressions), and with
77
- its classes written out the element would become bestax `<Target>`. The codemod converts
78
- static strings only. Convert by hand with the [prop map](prop-map.md), turning each condition
79
- into the prop:
238
+ The `className` is computed in a way the codemod can't read exactly, and with its classes
239
+ written out the element would become bestax `<Target>`.
240
+
241
+ What it can read is a `clsx` or `classnames` call made of class strings and classes added
242
+ under a condition: `busy && 'is-loading'`, `{ 'is-loading': busy }`, `busy ? 'is-loading' : ''`
243
+ (or `: null`). Those convert without a TODO. A condition on one of the component's flags
244
+ becomes that prop (`isLoading={busy}`, or `isLight={!quiet}` for `quiet ? '' : 'is-light'`),
245
+ anything else stays in the call, and the call goes once nothing is left in it. `Field`'s
246
+ `is-grouped` stays in the call too, since `grouped` renders it for `true` alone. A prop is
247
+ evaluated before the call, so a condition moves ahead of one left in the call only when neither
248
+ can have side effects (a call, an assignment); otherwise it stays. A condition that isn't a
249
+ boolean (`items.length && 'is-active'`) renders the same, but won't typecheck against the
250
+ boolean prop; wrap it in `Boolean(…)`.
251
+
252
+ So this TODO is a className built any other way: a ternary between two classes, a template
253
+ with expressions, a variable, another function (`classnames/bind`, an app's own `cn`), or the
254
+ component's class itself under a condition. Convert by hand with the [prop map](prop-map.md),
255
+ turning each condition into the prop:
80
256
 
81
257
  ```tsx
82
258
  // before
83
- <button className={clsx('button', busy && 'is-loading')}>Save</button>
259
+ <button className={primary ? 'button is-primary' : 'button is-light'}>Save</button>
84
260
  // after
85
- <Button isLoading={busy}>Save</Button>
261
+ <Button color={primary ? 'primary' : undefined} isLight={!primary}>Save</Button>
86
262
  ```
87
263
 
88
264
  The classes in a computed `className` still count for every other rule: a `clsx('box')` on a
89
- `<span>` gets `tag:Box`, and a `clsx('card', …)` gets `family:card`.
265
+ `<span>` gets `tag:Box`, and a `clsx('dropdown', …)` gets `family:dropdown`. A computed
266
+ `className` on the `<select>` inside a `.select`, the `<ul>` inside a `.breadcrumb` or the `<a>`
267
+ in a menu item gets this TODO too. For the `<a>`, a condition on `is-active` is `Menu.Item`'s
268
+ `active`: `<a className={on ? 'is-active' : undefined}>` becomes `<Menu.Item active={on}>`.
90
269
 
91
270
  ## A family it leaves as markup: `family:<class>`
92
271
 
@@ -94,34 +273,37 @@ The bestax component renders parts of its own, or adds attributes, so a one-elem
94
273
  conversion would change the markup. Convert the whole family by hand, and look at the result
95
274
  in the browser:
96
275
 
97
- - **`family:card`**: `Card` wraps any child that is not one of its parts in `.card-content`.
98
- Use its parts, `Card.Header` (with `Card.Header.Title` and `Card.Header.Icon`),
99
- `Card.Image`, `Card.Content`, `Card.Footer` and `Card.FooterItem`, together.
100
- - **`family:navbar`**: `Navbar` adds `role="navigation"` and an `aria-label`, and
101
- `Navbar.Burger` renders its own three spans. Its parts are `Navbar.Brand`, `Navbar.Item`,
102
- `Navbar.Link`, `Navbar.Burger`, `Navbar.Menu`, `Navbar.Start`, `Navbar.End`,
103
- `Navbar.Dropdown`, `Navbar.DropdownMenu` and `Navbar.Divider`.
104
- - **`family:field`**, **`family:input`**, **`family:textarea`**, **`family:select`**,
105
- **`family:file`**: bestax's form controls render their own `.field` and `.control` wrappers
106
- (and `File` its whole `.file-label` tree). Replace the whole `.field` block with the
107
- control, not each element in it. See the `bestax-form` skill.
276
+ - **`family:navbar-burger`**: `Navbar.Burger` is a `<button>` that renders the burger's spans
277
+ itself and sets `aria-expanded` from `active`. Replace the whole toggle with it, drive
278
+ `active` from the state the old click handler flipped, and pass the same state to
279
+ `Navbar.Menu`'s `active`. Don't give it spans of your own: Bulma positions every span in the
280
+ burger. Releases before 5.16.10 render three spans where Bulma v1 positions four, so upgrade
281
+ rather than adding one.
282
+ - **`family:navbar-link`**: inside a `Navbar.Dropdown`, `Navbar.Link` adds `aria-haspopup`,
283
+ `aria-expanded` and keyboard handling. The codemod turned the `.has-dropdown` item around it
284
+ into a `Navbar.Item` that keeps the class; to get the dropdown behavior, replace that item
285
+ with `Navbar.Dropdown` (`hoverable` for `is-hoverable`) and the link with `Navbar.Link`.
108
286
  - **`family:checkbox`**, **`family:radio`**, **`family:checkboxes`**, **`family:radios`**:
109
287
  bestax renders its own styled checkbox and radio markup, not Bulma's.
110
- - **`family:modal`**: `Modal` renders its own background and content parts, and adds dialog
111
- attributes. Rebuild it with `Modal` and its parts, and drive it with its open prop.
288
+ - **`family:modal`**: `Modal` writes a `data-testid` of its own and adds dialog attributes and
289
+ focus handling, so the `.modal` root stays markup while its background, content and card parts
290
+ convert on their own. Rebuild the root with `Modal` around them, and drive it with its open prop.
291
+ A `.modal-close` stays too, since `Modal.Close` renders `.delete` unless it's floating: write
292
+ `<Modal.Close variant="floating" />` in its place. When the
293
+ page has to look the same, keep `Modal` rather than `Dialog` or `Toast`: those render
294
+ bestax's own `.dialog` and `.toast` markup, which Bulma's stylesheet doesn't style.
112
295
  - **`family:dropdown`**: `Dropdown` renders its own trigger and menu from props.
113
- - **`family:icon`**, **`family:icon-text`**: `Icon` renders its own `<i>` and adds an
114
- `aria-label`. See the `bestax-icons` skill for its library and name props.
115
- - **`family:image`**: `Image` renders its own `<img>`.
116
- - **`family:menu`**: `Menu.Item` renders the `<li>` and the `<a>` together.
296
+ - **`family:menu-item`**: Bulma styles an item as `.menu-list a`, `.menu-list button` or
297
+ `.menu-list .menu-item`, the last for an item on any other tag. `Menu.Item` renders the element
298
+ inside its `<li>` with no class but `is-active`, and puts its `className` on the `<li>`, so the
299
+ class has nowhere to go. On an `<a>` inside a `.menu-list` it adds nothing, so drop it and
300
+ re-run, and the item can convert. On any other tag, keep the markup.
117
301
  - **`family:message`**: `Message` always wraps its children in `.message-body`.
118
- - **`family:pagination`**, **`family:panel`**, **`family:tabs`**, **`family:breadcrumb`**:
119
- each renders list items, links or roles of its own. Rebuild them from the component's docs.
120
- - **`family:grid`**, **`family:cell`**, **`family:fixed-grid`**: this source leaves Grid markup
121
- as written. `Grid` and `Cell` convert by hand; `Grid isFixed` renders the `.fixed-grid`
122
- wrapper itself.
123
- - **`family:table-container`**: bestax renders `.table-container` from `Table isResponsive`.
124
- - **`family:skeleton-block`**, **`family:skeleton-lines`**: `Skeleton` renders its own markup.
302
+ - **`family:panel-icon`**: `Panel.Icon` renders through `Icon`, which always writes an
303
+ `aria-label`, so write `<Panel.Icon>` by hand with the `<i>` inside. The rest of a panel converts,
304
+ but for a `<label>` or `<div>` `.panel-block`, which stays markup with no TODO: bestax renders
305
+ those as `Panel.CheckboxBlock`, `Panel.InputBlock` and `Panel.ButtonBlock`, which build their own
306
+ contents from props.
125
307
 
126
308
  ## A class Bulma v1 removed: `legacy:<class>`
127
309