create-bestax 4.2.17 → 4.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -2
- package/dist/display.d.ts +9 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +19 -1
- package/dist/file-system.d.ts +21 -4
- package/dist/file-system.d.ts.map +1 -1
- package/dist/file-system.js +52 -6
- package/dist/library-range.d.ts +16 -0
- package/dist/library-range.d.ts.map +1 -0
- package/dist/library-range.js +40 -0
- package/dist/project-creator.d.ts +16 -1
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +39 -7
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +3 -2
- package/dist/validators.d.ts +8 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +18 -1
- package/package.json +8 -8
- package/templates/skills/bestax-custom-component/references/component-catalog.md +5 -1
- package/templates/skills/bestax-form/SKILL.md +45 -16
- package/templates/skills/bestax-form/references/api.md +13 -8
- package/templates/skills/bestax-form/references/patterns.md +75 -3
- package/templates/skills/bestax-layout-scaffold/SKILL.md +34 -6
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +2 -2
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +52 -5
- package/templates/skills/bestax-migrate/SKILL.md +34 -5
- package/templates/skills/bestax-migrate/references/bloomer/component-map.md +1 -1
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +12 -9
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +153 -105
- package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +48 -17
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +501 -16
- package/templates/skills/bestax-migrate/references/css-migration.md +12 -1
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +23 -17
- package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +27 -22
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +5 -5
- package/templates/skills/bestax-optimize/references/modular-build.md +57 -15
- package/templates/skills/bestax-theming/SKILL.md +19 -8
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +99 -17
- package/templates/skills/bestax-theming/examples/theme-config.tsx +13 -8
- package/templates/skills/bestax-theming/references/css-variables.md +231 -18
- package/templates/skills/bestax-theming/references/themeable-components.md +4 -0
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/main.jsx +1 -1
- package/templates/vite-ts/package.json +1 -1
- package/templates/vite-ts/src/main.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bestax",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.19",
|
|
4
4
|
"description": "Create a new bestax-bulma project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,26 +34,26 @@
|
|
|
34
34
|
"url": "https://github.com/allxsmith/bestax/issues"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"chalk": "^6.0.
|
|
37
|
+
"chalk": "^6.0.1",
|
|
38
38
|
"commander": "^15.0.0",
|
|
39
39
|
"figures": "^6.1.0",
|
|
40
|
-
"fs-extra": "^11.4.
|
|
40
|
+
"fs-extra": "^11.4.1",
|
|
41
41
|
"prompts": "^2.4.2",
|
|
42
42
|
"react": "^18.0.0 || ^19.0.0",
|
|
43
43
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
44
|
-
"@allxsmith/bestax-bulma": "^5.
|
|
44
|
+
"@allxsmith/bestax-bulma": "^5.44.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@jest/globals": "^30.0.0",
|
|
48
|
-
"@playwright/test": "^1.
|
|
48
|
+
"@playwright/test": "^1.64.1",
|
|
49
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
50
|
"@types/jest": "^30.0.0",
|
|
51
|
-
"@types/node": "^26.6.
|
|
51
|
+
"@types/node": "^26.6.5",
|
|
52
52
|
"@types/prompts": "^2.4.9",
|
|
53
53
|
"@types/wait-on": "^5.3.4",
|
|
54
54
|
"jest": "^30.2.0",
|
|
55
55
|
"semver": "^7.8.5",
|
|
56
|
-
"ts-jest": "^29.4.
|
|
56
|
+
"ts-jest": "^29.4.15",
|
|
57
57
|
"typescript": "^6.0.3",
|
|
58
58
|
"wait-on": "^9.5.1"
|
|
59
59
|
},
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"clean": "rimraf dist",
|
|
79
79
|
"release": "npx semantic-release"
|
|
80
80
|
},
|
|
81
|
-
"readme": "# create-bestax\n\n[](https://www.npmjs.com/package/create-bestax)\n[](https://www.npmjs.com/package/create-bestax)\n[](https://opensource.org/licenses/MIT)\n[](https://socket.dev/npm/package/create-bestax/overview)\n[](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[](https://www.bestpractices.dev/projects/14361)\n[](https://www.npmjs.com/package/create-bestax#provenance)\n[](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nThe scaffolder for [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — spin up a Vite app pre-wired for the **Bulma v1** React component library in one command. Picks your framework (JS or TypeScript), CSS flavor, and icon library, and can drop in the bestax **AI skills** so an agent like Claude Code knows the library from the first prompt.\n\nPart of the [bestax monorepo](https://github.com/allxsmith/bestax) — see also [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) for the components themselves.\n\n## Requirements\n\n- **Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the\n CLI exits immediately with an explicit upgrade message.\n- npm 10 or newer (ships with Node 22), or yarn/pnpm.\n\n## Usage\n\n### Quick Start\n\n```bash\n# Using npm\nnpm create bestax@latest my-app\n\n# Using npx\nnpx create-bestax@latest my-app\n```\n\n### Interactive Mode\n\nRunning without arguments will prompt you for all options:\n\n```bash\nnpm create bestax@latest\n```\n\nYou'll be asked to:\n\n1. Enter a project name (and, if that directory already holds files, confirm removing
|
|
81
|
+
"readme": "# create-bestax\n\n[](https://www.npmjs.com/package/create-bestax)\n[](https://www.npmjs.com/package/create-bestax)\n[](https://opensource.org/licenses/MIT)\n[](https://socket.dev/npm/package/create-bestax/overview)\n[](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[](https://www.bestpractices.dev/projects/14361)\n[](https://www.npmjs.com/package/create-bestax#provenance)\n[](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nThe scaffolder for [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — spin up a Vite app pre-wired for the **Bulma v1** React component library in one command. Picks your framework (JS or TypeScript), CSS flavor, and icon library, and can drop in the bestax **AI skills** so an agent like Claude Code knows the library from the first prompt.\n\nPart of the [bestax monorepo](https://github.com/allxsmith/bestax) — see also [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) for the components themselves.\n\n## Requirements\n\n- **Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the\n CLI exits immediately with an explicit upgrade message.\n- npm 10 or newer (ships with Node 22), or yarn/pnpm.\n\n## Usage\n\n### Quick Start\n\n```bash\n# Using npm\nnpm create bestax@latest my-app\n\n# Using npx\nnpx create-bestax@latest my-app\n```\n\n### Interactive Mode\n\nRunning without arguments will prompt you for all options:\n\n```bash\nnpm create bestax@latest\n```\n\nYou'll be asked to:\n\n1. Enter a project name or path (and, if that directory already holds files, confirm removing\n them; nothing is removed until you have answered the last question)\n2. Select a framework (Vite or Vite + TypeScript)\n3. Choose a Bulma CSS flavor (the `--bulma` options below)\n4. Select an icon library (Font Awesome, Material Icons, etc.)\n5. Choose whether to install the bestax AI skills into `.claude/skills/`\n\nEvery prompt has a flag equivalent, so the whole flow is scriptable and CI-friendly — see the options below.\n\n### Command Line Options\n\n```bash\nnpm create bestax@latest [project-directory] -- [options]\n```\n\nPut the options after `--`. npm needs it to pass them on at all, and so does bun, whose\n`bun create` rejects short flags such as `-t` and `-y` itself. pnpm and Yarn 1 accept the options\nwith or without it.\n\n**Arguments:**\n\n- `[project-directory]` - Optional project directory: a name such as `my-app`, or a path,\n absolute or relative, such as `../apps/my-app`, whose missing folders are created. Its last\n part is the project name, held to the same rules as a bare name. It becomes the page title as\n typed, and the `package.json` name lower-cased the way `npm init` does and without a leading\n `_`, since npm rejects both. A path ending in `.` or `..` is refused, like a bare `.` or `..`,\n so name the directory you want.\n\n**Options:**\n\n- `-t, --template <template>` - Template to use: `vite` or `vite-ts` \n **Default:** `vite`\n- `-b, --bulma <flavor>` - Bulma CSS flavor:\n - `complete` - Full Bulma CSS with all components and helpers\n - `prefixed` - Prefixed version for compatibility\n - `no-helpers` - Without helper classes\n - `no-helpers-prefixed` - Prefixed without helpers\n - `no-dark-mode` - Without dark mode support \n **Default:** `complete`\n- `-i, --icon <library>` - Icon library:\n - `none` - No icon library\n - `fontawesome` - Font Awesome\n - `mdi` - Material Design Icons\n - `ionicons` - Ionicons\n - `material-icons` - Material Icons\n - `material-symbols` - Material Symbols \n **Default:** `none`\n- `--skills` / `--no-skills` - Install (or skip) the bestax AI skills into\n `.claude/skills/` (with a `CLAUDE.md`) so a Claude Code session picks them up\n automatically. When omitted you're prompted; with `-y` the default is to install.\n- `-y, --yes` - Skip prompts and use defaults or provided options. \n When used, the following defaults are selected unless overridden by flags:\n - Template: `vite`\n - Bulma flavor: `complete`\n - Icon library: `none`\n - AI skills: installed (pass `--no-skills` to opt out)\n\n `-y` never deletes files. If the target directory is not empty, the run stops, lists what is\n there, and changes nothing; add `--overwrite` to empty it first. Earlier versions emptied the\n directory under `-y` alone, so a script that relied on that now needs `--overwrite`.\n\n- `--overwrite` - Remove existing files if the target directory is not empty, without asking.\n Every other flag is checked first, so a typo stops the run before anything is deleted. It\n never empties the current directory or a folder that holds it, however the path is written.\n\n**Example:**\n\n```bash\n# Create a TypeScript project with Font Awesome icons\nnpm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete\n\n# Use all defaults (skip prompts)\nnpm create bestax@latest my-app -- -y\n\n# Scaffold into a path; the package is named my-app\nnpm create bestax@latest ../apps/my-app -- -y\n\n# The same with bun\nbun create bestax@latest my-app -- -y\n```\n\n## Templates\n\nAvailable templates:\n\n- `vite` - Vite + JavaScript\n- `vite-ts` - Vite + TypeScript\n\nEach template includes:\n\n- Pre-configured bestax-bulma integration (React 19)\n- The latest `@allxsmith/bestax-bulma`, which ships Bulma v1 automatically\n- Icon library support (Font Awesome, Material Design, etc.)\n- Sample components demonstrating library usage\n- Development and build scripts\n\n## For AI Tools\n\nScaffolding for an AI agent? Pass `--skills` (or accept the prompt) to drop the bestax\nAgent Skills and a `CLAUDE.md` into the new project so Claude Code, Cursor, or Copilot\nbuild the bestax way from the start. The library also ships LLM-optimized docs:\n\n- [LLMs guide](https://bestax.io/docs/guides/llms) — using bestax with Claude Code, Cursor, Copilot\n- [Agent Skills](https://bestax.io/docs/skills/intro) — teach your agent the bestax conventions\n- [llms.txt](https://bestax.io/llms.txt) (curated index) · [llms-full.txt](https://bestax.io/llms-full.txt) (full docs)\n\n## Development\n\n```bash\nnpm run build # Build the CLI\nnpm run dev # Watch mode\nnpm test # Run tests\nnpm run lint # Lint CLI source code\nnpm run typecheck # Type check CLI source code\n```\n\n**Note on Templates:** Template files in `templates/` are excluded from linting. They should be manually validated by scaffolding a test project and running lint/build there before releasing.\n\n## Hardened by default\n\nA scaffolder runs with write access to your filesystem and picks your starting dependencies, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/create-bestax#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, so a compromised action release can't roll silently into a build of this CLI.\n- **CodeQL, Dependency Review, and Dependabot** run continuously, alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`create-bestax` can send one **anonymous** usage event after a successful\nscaffold — only if you opt in when asked (once, at the end of a scaffold). The\nevent is just the choices you made — template, Bulma flavor, icon library,\nskills, package manager — plus the CLI version, Node major version, and OS\nplatform; never names, IPs, machine IDs, paths, or file contents, and no\nidentifier exists that could link two events together. Opt out any time with\n`--no-telemetry`, `BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## Publishing\n\nThis package uses semantic-release with scope-based rules. Only commits with `feat(create-bestax)` or `fix(create-bestax)` will trigger releases.\n"
|
|
82
82
|
}
|
|
@@ -37,7 +37,7 @@ instead of hand-writing markup.
|
|
|
37
37
|
- Raw `*Base` form exports (`InputBase`, `SelectBase`, `TextAreaBase`, …) are
|
|
38
38
|
escape-hatch variants of the convenience wrappers above them; see the Form docs.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
104 documented components. Generated from the API docs — every exported
|
|
41
41
|
component is guaranteed to appear (the generator fails if one lacks an API page).
|
|
42
42
|
|
|
43
43
|
## Elements
|
|
@@ -104,14 +104,17 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
104
104
|
- [Sidebar](https://bestax.io/docs/api/components/sidebar) — The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
|
|
105
105
|
- [Steps](https://bestax.io/docs/api/components/steps) — The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.
|
|
106
106
|
- [Tabs](https://bestax.io/docs/api/components/tabs) — The `Tabs` component provides flexible and fully-featured Bulma tab navigation for your Bulma React UI.
|
|
107
|
+
- [Timeline](https://bestax.io/docs/api/components/timeline) — The `Timeline` component renders a list of events in the order they happened, each with a marker on a line down the side.
|
|
107
108
|
- [Toast](https://bestax.io/docs/api/components/toast) — The `Toast` component provides brief notification messages with optional action and cancel buttons.
|
|
108
109
|
- [Tooltip](https://bestax.io/docs/api/components/tooltip) — The `Tooltip` component displays helpful information when users hover over or focus on an element.
|
|
110
|
+
- [TreeView](https://bestax.io/docs/api/components/treeview) — The `TreeView` component renders a tree of items you browse and pick from, the way a file explorer works: one tab stop, the arrow keys to move…
|
|
109
111
|
|
|
110
112
|
## Form
|
|
111
113
|
|
|
112
114
|
- [Autocomplete](https://bestax.io/docs/api/form/autocomplete) — The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
113
115
|
- [Checkbox](https://bestax.io/docs/api/form/checkbox) — The `Checkbox` component provides a Bulma-styled checkbox input.
|
|
114
116
|
- [Checkboxes](https://bestax.io/docs/api/form/checkboxes) — The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
|
|
117
|
+
- [Combobox](https://bestax.io/docs/api/form/combobox) — The `Combobox` component is a text field with a list to pick from under it, for a searchable select, a multi-select with tags, grouped items or a server search…
|
|
115
118
|
- [Control](https://bestax.io/docs/api/form/control) — The `Control` component is a Bulma-styled wrapper for form controls (`Input`, `Select`, `TextArea`, etc.), supporting icons (left/right), loading state…
|
|
116
119
|
- [DateInput](https://bestax.io/docs/api/form/datetime/dateinput) — The `DateInput` component is a form input that opens a popover calendar for date selection.
|
|
117
120
|
- [DateRangeInput](https://bestax.io/docs/api/form/datetime/daterangeinput) — The `DateRangeInput` component is a form input for a start and end date, picked from one popover calendar or typed into two segmented inputs.
|
|
@@ -120,6 +123,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
120
123
|
- [File](https://bestax.io/docs/api/form/file) — The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
|
|
121
124
|
- [Input](https://bestax.io/docs/api/form/input) — The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states…
|
|
122
125
|
- [Numberinput](https://bestax.io/docs/api/form/numberinput) — The `Numberinput` component provides a number input with increment/decrement buttons.
|
|
126
|
+
- [PinInput](https://bestax.io/docs/api/form/pininput) — The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each.
|
|
123
127
|
- [Radio](https://bestax.io/docs/api/form/radio) — The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.
|
|
124
128
|
- [Radios](https://bestax.io/docs/api/form/radios) — The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.
|
|
125
129
|
- [Rate](https://bestax.io/docs/api/form/rate) — The `Rate` component provides a star/icon-based rating system.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bestax-form
|
|
3
|
-
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Slider, Numberinput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
3
|
+
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ using context (`useInsideField` / `useInsideControl`) to detect their surroundin
|
|
|
65
65
|
```tsx
|
|
66
66
|
<Field hasAddons>
|
|
67
67
|
<Control isExpanded>
|
|
68
|
-
<InputBase placeholder="Search" />
|
|
68
|
+
<InputBase placeholder="Search" aria-label="Search" />
|
|
69
69
|
</Control>
|
|
70
70
|
<Control>
|
|
71
71
|
<Button color="primary">Go</Button>
|
|
@@ -73,6 +73,10 @@ using context (`useInsideField` / `useInsideControl`) to detect their surroundin
|
|
|
73
73
|
</Field>
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
A `grouped` or `hasAddons` Field names none of its controls, and a placeholder disappears once
|
|
77
|
+
the user types, so give each input in the row an `aria-label`, or a visible label on an outer
|
|
78
|
+
`Field` (`references/patterns.md`, "Grouped controls and addons").
|
|
79
|
+
|
|
76
80
|
## Component inventory
|
|
77
81
|
|
|
78
82
|
All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Field+Control;
|
|
@@ -89,9 +93,11 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
89
93
|
| `Radio` / `Radios` | Single radio / managed single-select group. |
|
|
90
94
|
| `Switch` | Toggle switch (`isRounded`, `isThin`, `isOutlined`, RTL). |
|
|
91
95
|
| `File` | File upload input with label/message. |
|
|
92
|
-
| `Autocomplete` |
|
|
96
|
+
| `Autocomplete` | Free text with suggestions it filters for you, + keyboard nav. |
|
|
97
|
+
| `Combobox` | A value from a list: `data` for a select, tags or groups, or parts. |
|
|
93
98
|
| `Slider` | Range slider; single/dual thumbs, steps, tooltips, vertical. |
|
|
94
99
|
| `Numberinput` | Numeric input with increment/decrement, min/max, step, stepper. |
|
|
100
|
+
| `PinInput` | A one-time code or PIN as a row of boxes, all in one real input. |
|
|
95
101
|
| `Rate` | Star rating; `max`, `precision` (half/quarter), custom icons, `disabled`. |
|
|
96
102
|
| `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
|
|
97
103
|
| `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers; month or year via `granularity`. |
|
|
@@ -100,6 +106,27 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
100
106
|
(`NumberInput` and `TagInput` also exist as deprecated aliases of `Numberinput`/`Taginput` —
|
|
101
107
|
same components; prefer the lowercase-second-word spellings.)
|
|
102
108
|
|
|
109
|
+
**Autocomplete or Combobox?** A field whose text is the value, with suggestions to help type it,
|
|
110
|
+
is `Autocomplete`, which filters its `data` itself. A value picked from a list is `Combobox`. Given
|
|
111
|
+
`data`, it filters by label, keeps the value (`value`/`onChange`) and shows the picks as tags with
|
|
112
|
+
`multiple`, so a searchable select, a multi-select or a grouped list is one element. Build it from
|
|
113
|
+
parts for a server search whose answer must show unfiltered, or a "Create" item: then it keeps no
|
|
114
|
+
value and never sets the field's text, so you filter the items, keep the value and set the text in
|
|
115
|
+
`onSelect`.
|
|
116
|
+
|
|
117
|
+
**A verification code or a PIN is `PinInput`**, not a row of one-character `Input`s with focus
|
|
118
|
+
moved by hand. It holds the code in one real input under a row of boxes, so a screen reader hears
|
|
119
|
+
one field named by its `label`, and paste, autofill from a text message (`autoComplete` is
|
|
120
|
+
`"one-time-code"`) and password managers work as they do on any input. Give it `length` (6 by
|
|
121
|
+
default), `value`/`onChange` with the code as a string, and verify in `onComplete`, which fires
|
|
122
|
+
once each time a change fills the last box. It takes digits unless `type="alphanumeric"`, turns a
|
|
123
|
+
digit from any script (Arabic-Indic, Persian) into its ASCII digit, drops anything else typed or
|
|
124
|
+
pasted, and `isMasked` shows dots for a PIN. A paste replaces the whole code. Its default `pattern`
|
|
125
|
+
asks for the full length, so `required` rejects a partial code, not just an empty one. Say the
|
|
126
|
+
length in the `label` ("Enter the 6-digit code"): the boxes are hidden from a screen reader, and the
|
|
127
|
+
`message` isn't tied to the input, so it isn't read on focus. Separate help text needs
|
|
128
|
+
`aria-describedby` on the PinInput.
|
|
129
|
+
|
|
103
130
|
## Common props
|
|
104
131
|
|
|
105
132
|
Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
|
|
@@ -126,13 +153,15 @@ spellings compile only where they historically existed: `isFullWidth` everywhere
|
|
|
126
153
|
`Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
|
|
127
154
|
|
|
128
155
|
**Labels.** Default to the convenience `label` prop, with `horizontal` for a side-by-side row.
|
|
129
|
-
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `
|
|
130
|
-
`DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
131
|
-
`htmlFor`/`id`, to your `id` when you pass one
|
|
132
|
-
an explicit `labelProps={{ htmlFor }}` wins.
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `PinInput`, `Slider`, `DateInput`,
|
|
157
|
+
`TimeInput`, `DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
158
|
+
and wire `htmlFor`/`id`, to your `id` when you pass one
|
|
159
|
+
(`<Input label="Email" id="email" horizontal />`); an explicit `labelProps={{ htmlFor }}` wins. `Combobox` renders its `Field` and label too, and wires
|
|
160
|
+
them to its field. Given `data`, its `id`, `aria-label` and `aria-labelledby` go to the field;
|
|
161
|
+
built from parts, put them on `Combobox.Input`, since on the `Combobox` they land on its wrapper
|
|
162
|
+
`div`. The group inputs (`Checkboxes`, `Radios`, `Rate`, `DateRangeInput`) name their
|
|
163
|
+
`role="group"`/`"radiogroup"` wrapper through `aria-labelledby` instead. Inside a `Field` a
|
|
164
|
+
convenience input drops its `label`, and an `inline` date/time picker has no visible input to wire.
|
|
136
165
|
|
|
137
166
|
Composed, a labeled `Field` names the one control it holds: a composed `InputBase`/`SelectBase`/
|
|
138
167
|
`TextAreaBase`, a composed `DateInputBase`/`TimeInputBase`/`DateTimeInputBase` that is not
|
|
@@ -148,9 +177,9 @@ unmatched `for` out of server-rendered HTML.
|
|
|
148
177
|
|
|
149
178
|
A `range` `Slider` points the `for` at its low thumb and starts both thumbs' names with the label
|
|
150
179
|
("Price range Minimum value"); its `ariaLabel={[low, high]}` replaces those names outright, so
|
|
151
|
-
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s
|
|
152
|
-
`Taginput`'s
|
|
153
|
-
hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
180
|
+
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s, a
|
|
181
|
+
`Combobox`'s or a `Taginput`'s list point `aria-labelledby` at the label's `id`, which a label
|
|
182
|
+
wired by hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
154
183
|
fallback names ("Minimum value"/"Maximum value", "Suggestions"). A `<label htmlFor>` you put in
|
|
155
184
|
`Field.Label` yourself (`references/patterns.md`) names its control through the `for` alone, so
|
|
156
185
|
label a row holding either with the `Field`'s `label` prop. An `aria-label` or `aria-labelledby`
|
|
@@ -239,9 +268,9 @@ multi-field form plus the advanced inputs.
|
|
|
239
268
|
|
|
240
269
|
## Reuse the shipped components
|
|
241
270
|
|
|
242
|
-
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch,
|
|
243
|
-
|
|
244
|
-
inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
271
|
+
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch, File,
|
|
272
|
+
Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, and the date/time inputs (see
|
|
273
|
+
the inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
245
274
|
reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
246
275
|
`docs/docs/api/form/` first — it's probably already there under a different name.
|
|
247
276
|
|
|
@@ -19,7 +19,7 @@ Container and layout. Compound parts: `Field.Label`, `Field.Body`.
|
|
|
19
19
|
| `labelProps` | label attributes | Props for the `<label>`. An explicit `htmlFor` (even `undefined`) takes over the association; otherwise it is set automatically. For its `id`, see below. |
|
|
20
20
|
| `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
|
|
21
21
|
|
|
22
|
-
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete` or `Taginput`
|
|
22
|
+
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete`'s, a `Combobox`'s or a `Taginput`'s list's `aria-labelledby` points at. A label wired by hand with `htmlFor` gets no generated id, so give it one, `labelProps={{ htmlFor, id }}`, for a range `Slider`, an `Autocomplete`, a `Combobox` or a `Taginput`. No group points at a label wired by hand.
|
|
23
23
|
|
|
24
24
|
## Control — `form/Control.tsx`
|
|
25
25
|
|
|
@@ -70,6 +70,9 @@ Field/Control/message props. `TextArea` adds `rows` and `hasFixedSize`.
|
|
|
70
70
|
|
|
71
71
|
- `Checkbox` / `Radio` — single controls with `color`, `size`, `checked`/`defaultChecked`,
|
|
72
72
|
`onChange`, `disabled`.
|
|
73
|
+
- `Checkbox` also takes `indeterminate`, the mixed state of a "Select all" box over a partly
|
|
74
|
+
checked list. It changes the look and the announcement, not the value: `checked` stays the
|
|
75
|
+
value, so set both from the list.
|
|
73
76
|
- `Checkboxes` — group wrapper managing an **array** value.
|
|
74
77
|
- `Radios` — group wrapper managing a **single** selected value.
|
|
75
78
|
|
|
@@ -84,13 +87,15 @@ File input with `label`, `message`, color/size, and icon support.
|
|
|
84
87
|
|
|
85
88
|
## Advanced inputs
|
|
86
89
|
|
|
87
|
-
| Component | Key props
|
|
88
|
-
| -------------- |
|
|
89
|
-
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`.
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
90
|
+
| Component | Key props |
|
|
91
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`. |
|
|
93
|
+
| `Combobox` | Either way: `multiple`, `keepFirst`, `inline`, `size`, `position` (`'auto'` keeps the list in the viewport), `appendToBody` (in a `Modal`, point a `ConfigProvider`'s `portalContainer` inside the modal), `open`/`defaultOpen`/`onOpenChange`, `onSelect(value)` and the Field props. Given `data` (strings or `{ value, label?, disabled?, group? }`): `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` with `multiple`, shown as tags), `placeholder`, `name`, `empty`, `itemTemplate`, and `disabled`, `required`, `form`, `id`, `aria-label`/`aria-labelledby` for the field; leaving the field empty clears one value. From parts: `.Input` (an `Input`, with `size` and the Field props on the root), `.Menu`, `.List`, `.Item` (`value`, `checked`, `disabled`), `.Group` (`label`), `.Empty`; you filter, keep the value and set the field's text in `onSelect`. |
|
|
94
|
+
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
95
|
+
| `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size`, `isLoading` (set it here, not on a `Control` around it, whose spinner covers the right-hand button). |
|
|
96
|
+
| `PinInput` | `length` (6), `value`/`defaultValue`/`onChange` (the code as a string), `onComplete(code)` once a change fills the last box, `type` (`'number'` or `'alphanumeric'`), `isMasked`, `name`, `color`, `size`, `isRounded`, `disabled` and the Field props. One input holds the code (`autoComplete="one-time-code"`); a paste replaces it. |
|
|
97
|
+
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
98
|
+
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
94
99
|
|
|
95
100
|
## Date / time inputs
|
|
96
101
|
|
|
@@ -131,7 +131,7 @@ Notes:
|
|
|
131
131
|
// Search box: input + button attached.
|
|
132
132
|
<Field hasAddons>
|
|
133
133
|
<Control isExpanded>
|
|
134
|
-
<InputBase placeholder="Search" />
|
|
134
|
+
<InputBase placeholder="Search" aria-label="Search" />
|
|
135
135
|
</Control>
|
|
136
136
|
<Control>
|
|
137
137
|
<Button color="primary">Search</Button>
|
|
@@ -141,14 +141,18 @@ Notes:
|
|
|
141
141
|
// Two controls on one row.
|
|
142
142
|
<Field grouped>
|
|
143
143
|
<Control>
|
|
144
|
-
<InputBase placeholder="First" />
|
|
144
|
+
<InputBase placeholder="First" aria-label="First name" />
|
|
145
145
|
</Control>
|
|
146
146
|
<Control>
|
|
147
|
-
<InputBase placeholder="Last" />
|
|
147
|
+
<InputBase placeholder="Last" aria-label="Last name" />
|
|
148
148
|
</Control>
|
|
149
149
|
</Field>
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
A `grouped` or `hasAddons` Field names none of the controls it holds, and a placeholder
|
|
153
|
+
disappears once the user types, so each input above carries an `aria-label`. For a visible
|
|
154
|
+
label instead, see the last item below.
|
|
155
|
+
|
|
152
156
|
Recipes built on addons, each written out in full in the docs:
|
|
153
157
|
|
|
154
158
|
- **Password reveal** ([docs](https://bestax.io/docs/api/form/input#password-reveal)): the
|
|
@@ -210,6 +214,60 @@ const [city, setCity] = useState('');
|
|
|
210
214
|
openOnFocus
|
|
211
215
|
/>;
|
|
212
216
|
|
|
217
|
+
// Combobox: a value from a list. Given data, it filters by label, keeps the
|
|
218
|
+
// value and shows its label in the field.
|
|
219
|
+
const [country, setCountry] = useState<string | null>(null);
|
|
220
|
+
<Combobox
|
|
221
|
+
label="Country"
|
|
222
|
+
data={['Canada', 'France', 'Japan']}
|
|
223
|
+
value={country}
|
|
224
|
+
onChange={setCountry}
|
|
225
|
+
empty="No countries match"
|
|
226
|
+
/>;
|
|
227
|
+
|
|
228
|
+
// Several values, shown as tags under the field; groups come from `group`.
|
|
229
|
+
const [labels, setLabels] = useState<string[]>([]);
|
|
230
|
+
<Combobox
|
|
231
|
+
label="Labels"
|
|
232
|
+
multiple
|
|
233
|
+
data={[
|
|
234
|
+
{ value: 'bug', label: 'Bug', group: 'Type' },
|
|
235
|
+
{ value: 'docs', label: 'Docs', group: 'Area' },
|
|
236
|
+
]}
|
|
237
|
+
value={labels}
|
|
238
|
+
onChange={setLabels}
|
|
239
|
+
/>;
|
|
240
|
+
|
|
241
|
+
// From parts, when data doesn't fit (a server search, a "Create" item): you
|
|
242
|
+
// filter, keep the value and set the text, and a pick calls onSelect.
|
|
243
|
+
const countries = ['Canada', 'France', 'Japan'];
|
|
244
|
+
const [choice, setChoice] = useState<string | null>(null);
|
|
245
|
+
const [query, setQuery] = useState('');
|
|
246
|
+
const term = query === choice ? '' : query.trim().toLowerCase();
|
|
247
|
+
const matches = countries.filter(c => c.toLowerCase().includes(term));
|
|
248
|
+
<Combobox
|
|
249
|
+
label="Country"
|
|
250
|
+
onSelect={picked => {
|
|
251
|
+
setChoice(picked);
|
|
252
|
+
setQuery(picked);
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<Combobox.Input value={query} onChange={e => setQuery(e.target.value)} />
|
|
256
|
+
<Combobox.Menu>
|
|
257
|
+
{matches.length > 0 ? (
|
|
258
|
+
<Combobox.List>
|
|
259
|
+
{matches.map(c => (
|
|
260
|
+
<Combobox.Item key={c} value={c} checked={c === choice}>
|
|
261
|
+
{c}
|
|
262
|
+
</Combobox.Item>
|
|
263
|
+
))}
|
|
264
|
+
</Combobox.List>
|
|
265
|
+
) : (
|
|
266
|
+
<Combobox.Empty>No countries match</Combobox.Empty>
|
|
267
|
+
)}
|
|
268
|
+
</Combobox.Menu>
|
|
269
|
+
</Combobox>;
|
|
270
|
+
|
|
213
271
|
// Slider (single and dual thumb)
|
|
214
272
|
const [volume, setVolume] = useState(50);
|
|
215
273
|
<Slider value={volume} onChange={setVolume} min={0} max={100} tooltip="auto" />;
|
|
@@ -229,6 +287,20 @@ const [range, setRange] = useState<[number, number]>([20, 80]);
|
|
|
229
287
|
const [qty, setQty] = useState(1);
|
|
230
288
|
<Numberinput value={qty} onChange={setQty} min={1} max={10} step={1} />;
|
|
231
289
|
|
|
290
|
+
// PinInput: a one-time code or PIN, a box per character, all in one input.
|
|
291
|
+
// onComplete fires once each time a change fills the last box.
|
|
292
|
+
const [code, setCode] = useState('');
|
|
293
|
+
const verify = (otp: string) => {
|
|
294
|
+
// Send it to your server, then show the result with color and message.
|
|
295
|
+
};
|
|
296
|
+
<PinInput
|
|
297
|
+
label="Enter the 6-digit code"
|
|
298
|
+
name="otp"
|
|
299
|
+
value={code}
|
|
300
|
+
onChange={setCode}
|
|
301
|
+
onComplete={verify}
|
|
302
|
+
/>;
|
|
303
|
+
|
|
232
304
|
// Rate
|
|
233
305
|
const [stars, setStars] = useState(3);
|
|
234
306
|
<Rate value={stars} onChange={setStars} max={5} />;
|
|
@@ -97,6 +97,31 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
97
97
|
"Clear search" put focus back in the field. While data loads show a `Skeleton` or a `Loader`,
|
|
98
98
|
and a failed load is a `Message` or a `Notification`, never an EmptyState. The archetypes
|
|
99
99
|
reference has the full pattern under Card grid.
|
|
100
|
+
- **An order's history, a ticket's activity, a changelog or an activity feed is `Timeline`**,
|
|
101
|
+
not a hand-built list with a border down the side: `<Timeline titleAs="h3">` with a
|
|
102
|
+
`<Timeline.Item title="Shipped" time="14 October 2026" dateTime="2026-10-14">` per event, any
|
|
103
|
+
detail as its children, and the whole date in `time`. `titleAs` is the level under the
|
|
104
|
+
section's heading, or `"p"` in a busy feed. For something that moves along on its own, like a
|
|
105
|
+
parcel or a deployment, `active={i}` marks progress and `pending="Out for delivery"` ends it on
|
|
106
|
+
what's under way, oldest first. A journey the user moves through, like a wizard or a checkout,
|
|
107
|
+
stays `Steps`.
|
|
108
|
+
- **A file browser, a checkbox tree (permissions, filters) or an outline you browse is
|
|
109
|
+
`TreeView`**, with `<TreeView.Item value="…" label="…" icon="folder">` items nested inside each
|
|
110
|
+
other: `<TreeView aria-label="Files" selectionMode="single">` with `selected`/`onSelectedChange`
|
|
111
|
+
picks one, and `selectionMode="checkbox"` with `checked`/`onCheckedChange` draws boxes with
|
|
112
|
+
mixed parents, where a parent's value checks its whole branch. Map the data with a recursive
|
|
113
|
+
function, and render an item's children whether it's open or not. A folder that loads when it
|
|
114
|
+
opens takes `hasChildren`, `isLoading` while its children are on their way, and
|
|
115
|
+
`loadError="Couldn't load src"` with `onRetry` when the load fails, setting `isLoading` again
|
|
116
|
+
while the retry runs: the tree draws the Loading… and Retry rows and announces them, so don't
|
|
117
|
+
hand-write a placeholder item. Its items aren't
|
|
118
|
+
links, so the app's sidebar and any list of pages stay on `Menu` with `Menu.Item href`.
|
|
119
|
+
- **A sidebar whose groups of links open and close is `Menu.Section`**, one per group, directly
|
|
120
|
+
in `Menu` (never inside a `Menu.Item` or `Menu.List`):
|
|
121
|
+
`<Menu.Section label="Components" defaultOpen>` around that group's `Menu.List`, with
|
|
122
|
+
`defaultOpen` on the group holding the current page (or `open`/`onOpenChange` to hold the state).
|
|
123
|
+
Its heading is a real button with `aria-expanded`, and its links stay links, each a tab stop.
|
|
124
|
+
Not `TreeView`, and not a `Collapse` inside a hand-written `<li>`.
|
|
100
125
|
- Link lists (footer nav, sidebars): a bare `UnorderedList` of `ListItem`s is already
|
|
101
126
|
marker-less and flush — Bulma's reset unstyles `ul` — so no prop or CSS is needed;
|
|
102
127
|
bullets appear only inside `Content`.
|
|
@@ -152,9 +177,10 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
152
177
|
(`Hero color="primary"`, a dark banner), use **filled** buttons — `color="light"` or
|
|
153
178
|
`color="primary" isInverted` — never a thin `isOutlined` secondary: a light outline + light
|
|
154
179
|
label on a dark surface is low-contrast and gets worse under OS dark mode. And when the page's
|
|
155
|
-
design is single-mode (a fixed light or dark look), pin it
|
|
156
|
-
`<
|
|
157
|
-
colors out from under the fixed palette
|
|
180
|
+
design is single-mode (a fixed light or dark look), pin it on the `<html>` in `index.html`,
|
|
181
|
+
`<html lang="en" data-theme="light">`, so a visitor's OS dark mode can't flip Bulma's text
|
|
182
|
+
colors out from under the fixed palette, from the first paint on (details: the
|
|
183
|
+
`bestax-theming` skill's contrast rules).
|
|
158
184
|
|
|
159
185
|
## Three components core Bulma will talk you out of
|
|
160
186
|
|
|
@@ -199,9 +225,11 @@ background comes from the semantic `color` prop, not `bgColor`.
|
|
|
199
225
|
| `display: 'none'` | `visibility="hidden"`, or responsive `display*` props (`displayMobile`, `displayTablet`, …) |
|
|
200
226
|
| `gap: '1rem'` (flex or grid) | `gap="2"`, or one axis: `columnGap`, `rowGap`; gap scale: `1`=0.5rem, `2`=1rem, `4`=2rem, half steps (`"1.5"`) |
|
|
201
227
|
|
|
202
|
-
No helper matches (e.g. `maxWidth`, a one-off gradient)? Add a named class to the project
|
|
203
|
-
stylesheet
|
|
204
|
-
|
|
228
|
+
No helper matches (e.g. `maxWidth`, a one-off gradient)? Add a named class to the project's
|
|
229
|
+
global stylesheet and pass it via `className` — still never inline `style`. In a create-bestax
|
|
230
|
+
app that is `src/index.css`, which its entry file imports after bestax's stylesheet, so the
|
|
231
|
+
class wins over a Bulma rule of the same weight. The starter page's `src/App.css` loads after
|
|
232
|
+
bestax's too, but only while the starter's `App` imports it.
|
|
205
233
|
|
|
206
234
|
## References
|
|
207
235
|
|
|
@@ -164,8 +164,8 @@ style, so bands cost zero custom CSS. The same values work on `Hero` (and its
|
|
|
164
164
|
**Hero CTAs:** on a colored hero use **filled** buttons only — `color="light"` for the primary
|
|
165
165
|
CTA and `color="primary" isInverted` (solid white, primary text) for a secondary. A thin
|
|
166
166
|
`isOutlined` button on a fixed-color surface is low-contrast and degrades further under OS dark
|
|
167
|
-
mode. Single-mode page designs should also pin the scheme
|
|
168
|
-
(`<
|
|
167
|
+
mode. Single-mode page designs should also pin the scheme on the `<html>` in `index.html`
|
|
168
|
+
(`<html lang="en" data-theme="light">`); see the `bestax-theming` skill's contrast rules.
|
|
169
169
|
|
|
170
170
|
---
|
|
171
171
|
|
|
@@ -39,12 +39,16 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
39
39
|
|
|
40
40
|
| Prop | Type | Notes |
|
|
41
41
|
| --------------------------------- | --------------------------------------- | ----------------------------------------- |
|
|
42
|
+
| `as` | `'div'` (default) or a landmark tag | Renders that tag in place of the `div`. |
|
|
42
43
|
| `fluid` | `boolean` | Full width with a small gutter. |
|
|
43
44
|
| `widescreen` / `fullhd` | `boolean` | Only constrain at that breakpoint and up. |
|
|
44
45
|
| `breakpoint` | `'tablet' \| 'desktop' \| 'widescreen'` | Max-width breakpoint. |
|
|
45
46
|
| `isMax` | `boolean` | Use the `is-max-*` width. |
|
|
46
47
|
| `color` / `textColor` / `bgColor` | Bulma color | — |
|
|
47
48
|
|
|
49
|
+
`as` takes `'main' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'nav'`, so
|
|
50
|
+
`<Container as="main">` renders `<main class="container">` with no wrapper.
|
|
51
|
+
|
|
48
52
|
## Section
|
|
49
53
|
|
|
50
54
|
`<Section>` adds vertical page rhythm (padding).
|
|
@@ -63,6 +67,7 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
63
67
|
| `color` | `primary \| link \| info \| success \| warning \| danger \| black \| white \| light \| dark` (CSS-backed). Also accepted but deprecated, no CSS, dev-warn: every other `validColors` member, plus `inherit` and `current` |
|
|
64
68
|
| `size` | `'small' \| 'medium' \| 'large' \| 'fullheight' \| 'fullheight-with-navbar'` |
|
|
65
69
|
| `fullheightWithNavbar` | `boolean` |
|
|
70
|
+
| `as` | `'section'` (default) \| `'div'` \| `'main'` \| `'header'` \| `'footer'` \| `'article'` \| `'aside'` |
|
|
66
71
|
|
|
67
72
|
```tsx
|
|
68
73
|
<Hero color="primary" size="medium">
|
|
@@ -70,6 +75,11 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
70
75
|
</Hero>
|
|
71
76
|
```
|
|
72
77
|
|
|
78
|
+
`Hero.Head` takes `as="header"` or `as="nav"` and `Hero.Foot` `as="footer"` or `as="nav"` (both
|
|
79
|
+
`div` by default), so a page that is all hero is `<Hero as="main">` with no wrapper. A `header` or
|
|
80
|
+
`footer` is the page's banner or footer landmark only outside a `section`, `article`, `aside`,
|
|
81
|
+
`nav` or `main`, so for that render the hero itself `as="div"`.
|
|
82
|
+
|
|
73
83
|
## Footer
|
|
74
84
|
|
|
75
85
|
`<Footer>` is the page footer.
|
|
@@ -230,6 +240,8 @@ flexDirection="column"` + `flexGrow="1"` pattern applies to `Cell` + `Card`.
|
|
|
230
240
|
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
231
241
|
| `fixed` | `'top' \| 'bottom'` |
|
|
232
242
|
| `transparent` | `boolean` |
|
|
243
|
+
| `isSpaced` | `boolean` (Bulma's `is-spaced`, from desktop up) |
|
|
244
|
+
| `hasShadow` | `boolean` (default `false`, unlike `Card`'s; Bulma's `has-shadow`) |
|
|
233
245
|
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'black' \| 'dark' \| 'light' \| 'white'` |
|
|
234
246
|
| `Navbar.Item` | `as?`, `active?`, `href` |
|
|
235
247
|
| `Navbar.Burger` | `active?`, `onClick` |
|
|
@@ -239,7 +251,12 @@ The burger/menu is controlled state — toggle `Navbar.Burger active`/`onClick`
|
|
|
239
251
|
flag to `Navbar.Menu active`. **A `fixed="top"` navbar requires `has-navbar-fixed-top` on `<html>`**
|
|
240
252
|
(Bulma offsets the page from it); the library adds no helper. Set it statically —
|
|
241
253
|
`<html class="has-navbar-fixed-top">` in `index.html` — reserving a `classList.add` effect for
|
|
242
|
-
a navbar that mounts conditionally.
|
|
254
|
+
a navbar that mounts conditionally. **With `isSpaced`, add `has-spaced-navbar-fixed-top` too**
|
|
255
|
+
(`<html class="has-navbar-fixed-top has-spaced-navbar-fixed-top">`): from desktop up the spaced
|
|
256
|
+
navbar is taller than the plain class pads the page for, and there the spaced class pads it for
|
|
257
|
+
the taller navbar, so with the plain class alone the top of the page sits under the navbar. A
|
|
258
|
+
`fixed="bottom"` navbar takes `has-navbar-fixed-bottom`, and `has-spaced-navbar-fixed-bottom`
|
|
259
|
+
beside it with `isSpaced`.
|
|
243
260
|
|
|
244
261
|
**Routing:** in a routed app, don't use `href="#"` — render items as the router's link
|
|
245
262
|
component. `Menu.Item as={Link} to="/x"` and `Navbar.Item as={Link} to="/x"` both compile
|
|
@@ -250,11 +267,13 @@ https://bestax.io/docs/guides/features/routing.
|
|
|
250
267
|
|
|
251
268
|
## Menu
|
|
252
269
|
|
|
253
|
-
`<Menu>` is a vertical sidebar menu. Subcomponents: `Menu.Label`, `Menu.List`, `Menu.Item
|
|
270
|
+
`<Menu>` is a vertical sidebar menu. Subcomponents: `Menu.Label`, `Menu.List`, `Menu.Item`,
|
|
271
|
+
`Menu.Section`.
|
|
254
272
|
|
|
255
|
-
| Prop
|
|
256
|
-
|
|
|
257
|
-
| `Menu.Item`
|
|
273
|
+
| Prop | Type |
|
|
274
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
275
|
+
| `Menu.Item` | `active?` (the current page: `is-active` plus `aria-current="page"`, so don't add your own), `href?`, `as?` (custom link component); nest a `Menu.List` inside an item for submenus |
|
|
276
|
+
| `Menu.Section` | `label` (the heading, a `ReactNode`), `defaultOpen?`, or `open?` with `onOpenChange?(open)` to hold the state yourself, as `Collapse` does; its child is the group's `Menu.List`, and a closed section's links are out of reach |
|
|
258
277
|
|
|
259
278
|
```tsx
|
|
260
279
|
<Menu>
|
|
@@ -268,6 +287,34 @@ https://bestax.io/docs/guides/features/routing.
|
|
|
268
287
|
</Menu>
|
|
269
288
|
```
|
|
270
289
|
|
|
290
|
+
A sidebar whose groups of links open and close (a docs sidebar, an admin app's sections) puts each
|
|
291
|
+
group's `Menu.List` in a `Menu.Section`. Its heading is a real button drawn like `Menu.Label`, with
|
|
292
|
+
`aria-expanded` and a chevron, and its links stay ordinary links, each a tab stop. Open the
|
|
293
|
+
section that holds the current page. A section goes directly in `Menu`, never inside a `Menu.Item`
|
|
294
|
+
(it would render inside the item's link) or a `Menu.List` (it isn't a list item). Don't build this
|
|
295
|
+
from `TreeView` (its items aren't links) or from a `Collapse` in a hand-written `<li>` (its trigger
|
|
296
|
+
is a `div` with `role="button"`).
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
<nav aria-label="Documentation">
|
|
300
|
+
<Menu>
|
|
301
|
+
<Menu.Section label="Getting started">
|
|
302
|
+
<Menu.List>
|
|
303
|
+
<Menu.Item href="/docs/install">Installation</Menu.Item>
|
|
304
|
+
</Menu.List>
|
|
305
|
+
</Menu.Section>
|
|
306
|
+
<Menu.Section label="Components" defaultOpen>
|
|
307
|
+
<Menu.List>
|
|
308
|
+
<Menu.Item href="/docs/button" active>
|
|
309
|
+
Button
|
|
310
|
+
</Menu.Item>
|
|
311
|
+
<Menu.Item href="/docs/card">Card</Menu.Item>
|
|
312
|
+
</Menu.List>
|
|
313
|
+
</Menu.Section>
|
|
314
|
+
</Menu>
|
|
315
|
+
</nav>
|
|
316
|
+
```
|
|
317
|
+
|
|
271
318
|
## Card
|
|
272
319
|
|
|
273
320
|
`<Card>` for catalog/grid items. Use the props form (`header`, `image`, `footer`) or the compound
|