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
@@ -16,45 +16,172 @@ test holds it to that table. Render tests hold the table to the library itself.
16
16
 
17
17
  ## Components
18
18
 
19
- The **Tags** column is what the component can render: an element on any other tag gets a
20
- `tag:<Target>` TODO instead of a conversion.
21
-
22
- | Bulma class | bestax-bulma | Tags |
23
- | ---------------- | --------------- | -------------------------------------------------------------- |
24
- | `.block` | `Block` | `<div>` only |
25
- | `.box` | `Box` | `<div>` only |
26
- | `.button` | `Button` | `<button>`, any tag via `as` |
27
- | `.buttons` | `Buttons` | `<div>` only |
28
- | `.columns` | `Columns` | `<div>` only |
29
- | `.column` | `Column` | `<div>` only |
30
- | `.container` | `Container` | `<div>` only |
31
- | `.content` | `Content` | `<div>` only |
32
- | `.delete` | `Delete` | `<button>` only |
33
- | `.footer` | `Footer` | `<footer>`, `<div>` via `as` |
34
- | `.hero` | `Hero` | `<section>` only |
35
- | `.hero-head` | `Hero.Head` | `<div>` only |
36
- | `.hero-body` | `Hero.Body` | `<div>` only |
37
- | `.hero-foot` | `Hero.Foot` | `<div>` only |
38
- | `.level` | `Level` | `<nav>` only |
39
- | `.level-left` | `Level.Left` | `<div>` only |
40
- | `.level-right` | `Level.Right` | `<div>` only |
41
- | `.level-item` | `Level.Item` | `<div>`, `<p>`, `<a>` via `as` |
42
- | `.media` | `Media` | `<article>`, `<div>` via `as` |
43
- | `.media-left` | `Media.Left` | `<figure>`, `<div>` via `as` |
44
- | `.media-content` | `Media.Content` | `<div>` only |
45
- | `.media-right` | `Media.Right` | `<div>` only |
46
- | `.notification` | `Notification` | `<div>` only |
47
- | `.progress` | `Progress` | `<progress>` only |
48
- | `.section` | `Section` | `<section>` only |
49
- | `.subtitle` | `SubTitle` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<p>` via `as` |
50
- | `.table` | `Table` | `<table>` only |
51
- | `.tag` | `Tag` | `<span>` only |
52
- | `.tags` | `Tags` | `<div>` only |
53
- | `.title` | `Title` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<p>` via `as` |
19
+ The **Tags** column is what the codemod converts the class on: an element on any other tag gets
20
+ a `tag:<Target>` TODO instead of a conversion. A component's own `as` can take more. One exception:
21
+ a `<label>` or `<div>` `.panel-block` stays markup with no TODO, since bestax renders those as
22
+ `Panel.CheckboxBlock`, `Panel.InputBlock` and `Panel.ButtonBlock`, which build their own contents.
23
+
24
+ | Bulma class | bestax-bulma | Tags |
25
+ | ---------------------- | --------------------- | -------------------------------------------------------------- |
26
+ | `.block` | `Block` | `<div>` only |
27
+ | `.box` | `Box` | `<div>` only |
28
+ | `.button` | `Button` | `<button>`, any tag via `as` |
29
+ | `.buttons` | `Buttons` | `<div>` only |
30
+ | `.card` | `Card` | `<div>` only |
31
+ | `.card-header` | `Card.Header` | `<header>` only |
32
+ | `.card-header-title` | `Card.Header.Title` | `<div>`, `<p>`, `<h2>`, `<h3>`, `<h4>` via `as` |
33
+ | `.card-header-icon` | `Card.Header.Icon` | `<button>` only |
34
+ | `.card-image` | `Card.Image` | `<div>` only |
35
+ | `.card-content` | `Card.Content` | `<div>` only |
36
+ | `.card-footer` | `Card.Footer` | `<footer>` only |
37
+ | `.card-footer-item` | `Card.FooterItem` | `<span>`, `<a>`, `<button>` via `as` |
38
+ | `.columns` | `Columns` | `<div>` only |
39
+ | `.column` | `Column` | `<div>` only |
40
+ | `.grid` | `Grid` | `<div>` only |
41
+ | `.cell` | `Cell` | `<div>` only |
42
+ | `.container` | `Container` | `<div>` only |
43
+ | `.content` | `Content` | `<div>` only |
44
+ | `.delete` | `Delete` | `<button>` only |
45
+ | `.field` | `Field` | `<div>` only |
46
+ | `.field-label` | `Field.Label` | `<div>` only |
47
+ | `.field-body` | `Field.Body` | `<div>` only |
48
+ | `.control` | `Control` | `<div>`, `<p>` via `as` |
49
+ | `.input` | `InputBase` | `<input>` only |
50
+ | `.textarea` | `TextAreaBase` | `<textarea>` only |
51
+ | `.select` | `SelectBase` | `<div>` only |
52
+ | `.footer` | `Footer` | `<footer>`, `<div>` via `as` |
53
+ | `.hero` | `Hero` | `<section>` only |
54
+ | `.hero-head` | `Hero.Head` | `<div>` only |
55
+ | `.hero-body` | `Hero.Body` | `<div>` only |
56
+ | `.hero-foot` | `Hero.Foot` | `<div>` only |
57
+ | `.level` | `Level` | `<nav>` only |
58
+ | `.level-left` | `Level.Left` | `<div>` only |
59
+ | `.level-right` | `Level.Right` | `<div>` only |
60
+ | `.level-item` | `Level.Item` | `<div>`, `<p>`, `<a>` via `as` |
61
+ | `.media` | `Media` | `<article>`, `<div>` via `as` |
62
+ | `.media-left` | `Media.Left` | `<figure>`, `<div>` via `as` |
63
+ | `.media-content` | `Media.Content` | `<div>` only |
64
+ | `.media-right` | `Media.Right` | `<div>` only |
65
+ | `.navbar` | `Navbar` | `<nav>` only |
66
+ | `.navbar-brand` | `Navbar.Brand` | `<div>` only |
67
+ | `.navbar-menu` | `Navbar.Menu` | `<div>` only |
68
+ | `.navbar-start` | `Navbar.Start` | `<div>` only |
69
+ | `.navbar-end` | `Navbar.End` | `<div>` only |
70
+ | `.navbar-item` | `Navbar.Item` | `<a>`, any tag via `as` |
71
+ | `.navbar-dropdown` | `Navbar.DropdownMenu` | `<div>` only |
72
+ | `.navbar-divider` | `Navbar.Divider` | `<hr>` only |
73
+ | `.notification` | `Notification` | `<div>` only |
74
+ | `.progress` | `Progress` | `<progress>` only |
75
+ | `.section` | `Section` | `<section>` only |
76
+ | `.subtitle` | `SubTitle` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<p>` via `as` |
77
+ | `.table` | `Table` | `<table>` only |
78
+ | `.tag` | `Tag` | `<span>` only |
79
+ | `.tags` | `Tags` | `<div>` only |
80
+ | `.title` | `Title` | `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>`, `<h6>`, `<p>` via `as` |
81
+ | `.breadcrumb` | `Breadcrumb` | `<nav>` only |
82
+ | `.skeleton-block` | `Skeleton` | `<div>` only |
83
+ | `.skeleton-lines` | `Skeleton` | `<div>` only |
84
+ | `.file` | `File` | `<div>` only |
85
+ | `.icon` | `Icon` | `<span>` only |
86
+ | `.icon-text` | `IconText` | `<span>` only |
87
+ | `.image` | `Image` | `<div>`, `<figure>`, `<p>` via `as` |
88
+ | `.menu` | `Menu` | `<aside>` only |
89
+ | `.menu-label` | `Menu.Label` | `<p>` only |
90
+ | `.menu-list` | `Menu.List` | `<ul>` only |
91
+ | `.modal-background` | `Modal.Background` | `<div>` only |
92
+ | `.modal-content` | `Modal.Content` | `<div>` only |
93
+ | `.modal-card` | `Modal.Card` | `<div>` only |
94
+ | `.modal-card-head` | `Modal.Card.Head` | `<header>` only |
95
+ | `.modal-card-title` | `Modal.Card.Title` | `<p>` only |
96
+ | `.modal-card-body` | `Modal.Card.Body` | `<section>` only |
97
+ | `.modal-card-foot` | `Modal.Card.Foot` | `<footer>` only |
98
+ | `.pagination` | `Pagination` | `<nav>` only |
99
+ | `.pagination-list` | `Pagination.List` | `<ul>` only |
100
+ | `.pagination-previous` | `Pagination.Previous` | `<a>` only |
101
+ | `.pagination-next` | `Pagination.Next` | `<a>` only |
102
+ | `.pagination-link` | `Pagination.Link` | `<a>` only |
103
+ | `.pagination-ellipsis` | `Pagination.Ellipsis` | `<span>` only |
104
+ | `.panel` | `Panel` | `<nav>` only |
105
+ | `.panel-heading` | `Panel.Heading` | `<p>` only |
106
+ | `.panel-tabs` | `Panel.Tabs` | `<p>` only |
107
+ | `.panel-block` | `Panel.Block` | `<a>` only |
108
+ | `.tabs` | `Tabs` | `<div>` only |
54
109
 
55
110
  An element with two of these (`<div className="column box">`) becomes the layout one
56
111
  (`Column`), and the other class stays in `className`.
57
112
 
113
+ `Card` renders its children inside a `.card-content` of its own unless one of them is one of
114
+ its parts, and `Card.Header` its children inside a `.card-header-title` unless one of them is a
115
+ `Card.Header.Title`. So a `.card` with children converts only when one of the elements written directly
116
+ inside it converts to a part (or already is one), and a `.card-header` only when its title
117
+ does. Otherwise it gets a `children:<Target>` TODO. Bulma's own example card, with its
118
+ `<p className="card-header-title">` and `<a className="card-footer-item">` links, converts through
119
+ `as`. A `.card-footer-item` `<button>` converts only with its `type` written out as `button`,
120
+ `submit` or `reset`, since `Card.FooterItem` writes `type="button"` on a button in place of
121
+ anything else.
122
+
123
+ `Navbar` writes `role="navigation"` and `aria-label="main navigation"`, the attributes Bulma's
124
+ own navbar carries, so a `.navbar` that sets both converts (whatever the label says), and one
125
+ that doesn't gets a `defaults:Navbar` TODO. A `.navbar-item` `<button>` converts only with its
126
+ `type` written out as `button`, `submit` or `reset`, since `Navbar.Item` writes `type="button"` on
127
+ a button in place of anything else. A `.has-dropdown` item becomes a `Navbar.Item` that
128
+ keeps `has-dropdown` as a class, because a `Navbar.Dropdown` would give the `.navbar-link` inside
129
+ it dropdown semantics the markup didn't have. That link and the `.navbar-burger` stay markup with
130
+ a `family:<class>` TODO; converting either means building the dropdown or the toggle with bestax,
131
+ by hand.
132
+
133
+ Form markup converts piece by piece: `.field` to `Field`, `.control` to `Control`, the input
134
+ and textarea to `InputBase` and `TextAreaBase`, the controls without wrappers of their own, and a
135
+ `.select` with its `<select>` to `SelectBase` (below). A `.label` and a `.help` stay as they
136
+ are. An input's or a select's color (`is-danger`) stays a class, because `color` renders
137
+ `has-text-<color>` on it too. A `.field.is-horizontal` converts once its
138
+ `.field-label` and `.field-body` do, since `Field` wraps anything else in a `.field-body` of its
139
+ own. `Field` and `Control` tell bestax's form controls inside them to skip their own wrappers, so a
140
+ `.field` or `.control` that already holds a bestax component stays markup with a
141
+ `context:<Target>` TODO, and so does an input with no `id` inside a bestax `Field` or any other
142
+ component, which could hand it a labelled Field's generated one.
143
+
144
+ A `.tabs` converts around its `<ul>`, and the `<li>`s and `<a>`s inside stay as written: `Tabs.Tab`
145
+ renders its own `<a>` with tab roles and puts its label in a `<span>`, so converting the tabs
146
+ themselves is by hand (something that sat beside the text in the `<a>`, a `Tag` say, then wants a
147
+ `Span display="flex" alignItems="center"` around it and the label). A `.icon` converts around the
148
+ `<i>` inside it when it carries an `aria-label`, which `Icon` writes otherwise.
149
+
150
+ A `.menu` converts with its `.menu-label`s and `.menu-list`s, and so do the items in a list. An
151
+ item has no class to go by, so the codemod finds it by where it sits: a `<li>` whose nearest
152
+ element is a `.menu-list`, a bestax `Menu.List`, or the bare `<ul>` nested in one of those items,
153
+ so the `<li>`s a `.map()` renders count too. One handed to any other function doesn't, since that
154
+ function could render it anywhere. `Menu.Item` renders the `<li>` and its `<a>`
155
+ together, so an item converts when its `<li>` holds one `<a>` and at most one bare `<ul>` after
156
+ it, which becomes a `Menu.List`. `is-active` on the `<a>` becomes `active`, and the `<li>`'s own
157
+ classes stay in `className`, which `Menu.Item` puts on the `<li>`. It puts `id`, `title`, `role`,
158
+ `tabIndex`, `style` and `data-testid` there too, and everything else on the `<a>`, so an item
159
+ converts only when its attributes already sit that way. A `.menu-item` gets a
160
+ `family:menu-item` TODO. `Menu.List` drops `.menu-list` on a list inside another, so a
161
+ `.menu-list` inside another, or around a bestax `Menu.List`, stays markup with a
162
+ `context:Menu.List` TODO, and an item's nested list converts only inside a list that is a
163
+ `Menu.List` or becomes one.
164
+
165
+ ```tsx
166
+ <ul className="menu-list">
167
+ <li>
168
+ <a className="is-active" href="/team">Team</a>
169
+ <ul>
170
+ <li><a href="/team/members">Members</a></li>
171
+ </ul>
172
+ </li>
173
+ </ul>
174
+ // becomes
175
+ <Menu.List>
176
+ <Menu.Item active href="/team">
177
+ Team
178
+ <Menu.List>
179
+ <Menu.Item href="/team/members">Members</Menu.Item>
180
+ </Menu.List>
181
+ </Menu.Item>
182
+ </Menu.List>
183
+ ```
184
+
58
185
  ## Plain tags with helper classes
59
186
 
60
187
  A tag bestax wraps becomes that wrapper when at least one of its classes converts to a helper
@@ -77,17 +204,134 @@ prop: `<p className="has-text-centered mt-4">` → `<Paragraph textAlign="center
77
204
  bestax has no plain `<div>` wrapper, so `<div className="is-flex mt-4">` stays as it is, with
78
205
  no TODO: it is valid Bulma, and nothing unsafe was skipped.
79
206
 
207
+ ## Wrappers a component renders
208
+
209
+ Some wrappers are rendered by the component inside them, from a prop. The codemod folds the
210
+ wrapper into that component: the wrapper goes, and its class and modifiers become the
211
+ component's props (`<div className="fixed-grid has-3-cols">` around a `.grid` becomes
212
+ `<Grid isFixed fixedCols={3}>`).
213
+
214
+ | Bulma class | bestax-bulma |
215
+ | ------------------ | -------------------- |
216
+ | `.table-container` | `Table isResponsive` |
217
+ | `.fixed-grid` | `Grid isFixed` |
218
+
219
+ The component renders the wrapper bare, so it folds only when it holds that one element and
220
+ nothing else, and carries no attribute and no class but its own modifiers. Otherwise it stays
221
+ with a `children:<Target>` or `attr` TODO.
222
+
223
+ ## An element a component renders inside itself
224
+
225
+ `SelectBase` renders the `<select>` inside `.select` itself, `Breadcrumb` the `<ul>` inside
226
+ `.breadcrumb`, and `Image` the `<img>` inside `.image`. So each converts together with that one
227
+ element, and the component takes its place: `<div className="select is-small"><select name="plan">`
228
+ becomes `<SelectBase size="small" name="plan">` around the same `<option>`s, a breadcrumb's `<li>`s
229
+ go straight inside `Breadcrumb`, and `<figure className="image is-64x64"><img src="a.png">` becomes
230
+ `<Image as="figure" size="64x64" src="a.png" />`.
231
+
232
+ - `SelectBase` puts the attributes it's given on the `<select>`, so the `<select>`'s own move up
233
+ and the `.select` can carry none but a `key`. `is-hovered` and `is-focused` on the `<select>`
234
+ become `isHovered` and `isFocused`, a `multiple` `<select>` converts inside `.select.is-multiple`
235
+ as `multiple`, and its `size` becomes `multipleSize`.
236
+ - `Breadcrumb` renders its `<ul>` bare, and writes `aria-label="breadcrumbs"` unless it's given
237
+ one, so the `<ul>` carries nothing and the `.breadcrumb` needs an `aria-label` of its own.
238
+ - `Image` renders its `<img>` from `src` and `alt`, so the `<img>` carries nothing else, and
239
+ `is-rounded` on it becomes `isRounded`. The `.image` keeps its own attributes. A ratio
240
+ (`is-4by3`) stays a class, since `size` would add `has-ratio` as well. Around anything else (an
241
+ `<img>` with more attributes, an `<iframe>`), `Image` renders its children as given, so the
242
+ `.image` converts around them and they stay as written, as long as they're HTML elements written
243
+ out. An empty `.image` stays markup, since `Image` would render an `<img>` of its own, and so does
244
+ one around an expression (`{src && <img />}`), which can come out empty.
245
+
246
+ For `SelectBase` and `Breadcrumb`, anything else keeps both as markup, with a `children:<Target>`, `attr` or `defaults:<Target>`
247
+ TODO.
248
+
249
+ ## Children a component renders from a count
250
+
251
+ `Skeleton variant="lines"` renders `.skeleton-lines`' empty `<div>`s itself, `lines` of them. So
252
+ a `.skeleton-lines` whose children are just bare, empty `<div>`s becomes
253
+ `<Skeleton variant="lines" lines={N} />`, and the `<div>`s go. A class, an attribute or anything
254
+ inside one of them, or anything else beside them, keeps the element as markup with a
255
+ `children:Skeleton` TODO. A `.skeleton-block` converts like any other element, content and all.
256
+ `Skeleton` takes no helper props, so a helper class on either stays in `className`.
257
+
258
+ ## Icons a component builds from props
259
+
260
+ `IconText` builds its icons from props, so an `.icon-text` converts together with what's inside
261
+ it: each `.icon` becomes one icon's props, and a bare `<span>` of text right after one becomes that
262
+ icon's text. One icon is written as `iconProps`, with its text as the children, and more than one
263
+ as `items`.
264
+
265
+ ```jsx
266
+ <span className="icon-text">
267
+ <span className="icon" aria-label="Home">
268
+ <i className="fas fa-home"></i>
269
+ </span>
270
+ <span>Home</span>
271
+ </span>
272
+ // becomes
273
+ <IconText iconProps={{ library: "fa", name: "home", ariaLabel: "Home" }}>Home</IconText>
274
+ ```
275
+
276
+ The glyph is read from the `<i>`: its Font Awesome style class (`fas`, `far`, `fa-solid`, ...) or
277
+ `mdi`, the one class that names the glyph (`fa-home` as `name: "home"`), and the rest as
278
+ `features`. `library` is always written, so a `ConfigProvider` with another `iconLibrary` doesn't
279
+ change what renders. The `.icon`'s own classes and attributes join the glyph's props as they
280
+ would on `Icon`, with `aria-label` as `ariaLabel`.
281
+
282
+ It converts only when every `.icon` inside converts to `Icon` on its own and holds a bare, empty
283
+ `<i>` naming a Font Awesome or Material Design Icons glyph, and the only other children are those
284
+ texts, bare and static. Anything else keeps it as markup with a `children:IconText` TODO, and the
285
+ `.icon`s inside convert on their own.
286
+
287
+ ## A tree a component renders from props
288
+
289
+ `File` renders the whole `.file` tree from props, so a `.file` converts together with everything
290
+ inside it. The `<input>`'s attributes become `File`'s own (it puts the ones it doesn't read on the
291
+ `<input>`), its other classes become `inputClassName`, the `.file-label` `<span>`'s content becomes
292
+ `buttonLabel` (left out when it's `File`'s own default, `Choose a file…`), each `.file-icon`'s
293
+ content becomes `iconLeft` or `iconRight`, and the `.file-name`'s text becomes `fileName` beside
294
+ `hasName`.
295
+
296
+ ```jsx
297
+ <div className="field">
298
+ <div className="file has-name">
299
+ <label className="file-label">
300
+ <input className="file-input" type="file" name="cv" />
301
+ <span className="file-cta">
302
+ <span className="file-icon"><i className="fas fa-upload"></i></span>
303
+ <span className="file-label">Upload</span>
304
+ </span>
305
+ <span className="file-name">cv.pdf</span>
306
+ </label>
307
+ </div>
308
+ </div>
309
+ // becomes
310
+ <Field>
311
+ <File hasName name="cv" buttonLabel="Upload" iconLeft={<i className="fas fa-upload"></i>} fileName="cv.pdf" />
312
+ </Field>
313
+ ```
314
+
315
+ It converts only inside a `Field`, a bestax one already in the file or a `.field` that converts
316
+ in the same run, since outside one `File` renders a `.field` of its own. The tree has to be exactly
317
+ the one `File` renders, and the `.file` itself can carry no attribute but a `key`, since `File`
318
+ puts the attributes it's given on its `<input>`. A color class stays a class, since `color`
319
+ renders `has-text-<color>` too, and so does `is-centered`, which `isCentered` renders nothing of
320
+ beside `isRight`. Anything else keeps it as markup with a `children:File`, `context:File` or
321
+ `attr` TODO.
322
+
80
323
  ## Families this source leaves as markup
81
324
 
82
325
  Their markup doesn't map element by element (the bestax component renders parts of its own, or
83
326
  adds attributes), so the family's outermost class gets a `family:<class>` TODO and the markup
84
- stays. [unmappables.md](unmappables.md) has the recipe for each. The families are Breadcrumb,
85
- Card, Checkbox, Checkboxes, Dropdown, Field (and Input, TextArea, Select, File), Grid and Cell,
86
- Icon and IconText, Image, Menu, Message, Modal, Navbar, Pagination, Panel, Radio, Radios,
87
- Skeleton, `.table-container` and Tabs.
327
+ stays. [unmappables.md](unmappables.md) has the recipe for each. The families are Checkbox,
328
+ Checkboxes, Dropdown, the menu's `.menu-item`, Message, Modal's root and close
329
+ button, the navbar's burger and dropdown link, the panel's icon, Radio and Radios.
88
330
 
89
331
  ## Classes left alone
90
332
 
91
- `.help`, `.label`, `.loader`, `.hero-buttons`, `.hero-video`, `.theme-dark`, `.theme-light`,
92
- `.fa`, `.marginless` and `.paddingless` are valid Bulma with nothing in bestax to convert to.
333
+ `.help`, `.label`, `.hero-buttons`, `.hero-video`, `.theme-dark`, `.theme-light`, `.fa`,
334
+ `.marginless`, `.paddingless`, `.navbar-content`, `.navbar-tabs` and `.panel-list` are valid
335
+ Bulma with nothing in bestax to convert to. `.loader` is left alone as well, although bestax's
336
+ `Loader` renders the same element: swap it in by hand for its progressbar role and accessible name.
93
337
  An element carrying one stays as written, and gets no TODO.