bitboss-ui 3.0.0-beta.21 → 3.0.0-beta.22
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 +55 -3
- package/bin/bitboss-ui-mcp.mjs +419 -9
- package/bin/bitboss-ui.mjs +293 -22
- package/dist/ai/BbAccordion.md +21 -23
- package/dist/ai/BbAlert.md +6 -12
- package/dist/ai/BbAvatar.md +60 -24
- package/dist/ai/BbBadge.md +12 -20
- package/dist/ai/BbBaseButton.md +14 -16
- package/dist/ai/BbBaseCheckboxIcon.md +6 -10
- package/dist/ai/BbBaseRadioIcon.md +6 -10
- package/dist/ai/BbBaseSwitchIcon.md +5 -9
- package/dist/ai/BbBreadcrumbs.md +23 -34
- package/dist/ai/BbButton.md +35 -47
- package/dist/ai/BbCheckbox.md +21 -37
- package/dist/ai/BbCheckboxGroup.md +13 -13
- package/dist/ai/BbCollapsible.md +14 -26
- package/dist/ai/BbColorInput.md +13 -23
- package/dist/ai/BbColorPalette.md +23 -35
- package/dist/ai/BbConfirm.md +17 -32
- package/dist/ai/BbDatePickerInput.md +15 -21
- package/dist/ai/BbDialog.md +159 -62
- package/dist/ai/BbDropdown.md +21 -25
- package/dist/ai/BbDropdownButton.md +13 -30
- package/dist/ai/BbDropzone.md +9 -11
- package/dist/ai/BbIcon.md +44 -33
- package/dist/ai/BbIndicator.md +2 -2
- package/dist/ai/BbNumberInput.md +18 -20
- package/dist/ai/BbOffCanvas.md +42 -42
- package/dist/ai/BbPagination.md +21 -23
- package/dist/ai/BbPopover.md +15 -17
- package/dist/ai/BbProgress.md +40 -51
- package/dist/ai/BbRadio.md +9 -9
- package/dist/ai/BbRadioGroup.md +7 -11
- package/dist/ai/BbRating.md +8 -12
- package/dist/ai/BbSelect.md +32 -36
- package/dist/ai/BbSelectPopover.md +19 -31
- package/dist/ai/BbSlider.md +15 -19
- package/dist/ai/BbSmoothHeight.md +16 -31
- package/dist/ai/BbSpinner.md +118 -74
- package/dist/ai/BbSwitch.md +25 -18
- package/dist/ai/BbSwitchGroup.md +9 -14
- package/dist/ai/BbTable.md +102 -55
- package/dist/ai/BbTabs.md +59 -82
- package/dist/ai/BbTag.md +12 -23
- package/dist/ai/BbTextInput.md +20 -26
- package/dist/ai/BbTextarea.md +28 -34
- package/dist/ai/BbToast.md +14 -34
- package/dist/ai/BbTooltip.md +22 -20
- package/dist/ai/BbTree.md +46 -72
- package/dist/ai/changelog.json +169 -62
- package/dist/ai/components.json +1467 -9
- package/dist/ai/composables/useConfirm.md +5 -2
- package/dist/ai/composables/useToast.md +4 -2
- package/dist/ai/guides/agent-contract.md +7 -4
- package/dist/ai/guides/ai-router.md +37 -22
- package/dist/ai/guides/component-picker.md +23 -18
- package/dist/ai/guides/design-language.md +141 -13
- package/dist/ai/guides/design-tokens.md +29 -4
- package/dist/ai/guides/icons-policy.md +46 -21
- package/dist/ai/guides/installation-and-plugin-setup.md +253 -17
- package/dist/ai/guides/migration/components/bb-rating.md +26 -1
- package/dist/ai/guides/migration/components/bb-tooltip.md +18 -0
- package/dist/ai/guides/migration/v2-to-v3.md +50 -23
- package/dist/ai/index.md +1 -1
- package/dist/ai/recipes/inertia/approvals-inbox.md +3 -3
- package/dist/ai/recipes/inertia/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/inertia/onboarding.md +2 -2
- package/dist/ai/recipes/inertia/ownership-atlas.md +6 -6
- package/dist/ai/recipes/inertia/record-form.md +5 -5
- package/dist/ai/recipes/inertia/records-workspace.md +4 -4
- package/dist/ai/recipes/inertia/upload-center.md +6 -6
- package/dist/ai/recipes/inertia/wizard-form.md +5 -5
- package/dist/ai/recipes/nuxt/approvals-inbox.md +3 -3
- package/dist/ai/recipes/nuxt/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/nuxt/onboarding.md +2 -2
- package/dist/ai/recipes/nuxt/record-form.md +5 -5
- package/dist/ai/recipes/nuxt/records-workspace.md +4 -4
- package/dist/ai/recipes/nuxt/upload-center.md +6 -6
- package/dist/ai/recipes/nuxt/wizard-form.md +5 -5
- package/dist/ai/recipes/vue/approvals-inbox.md +3 -3
- package/dist/ai/recipes/vue/inline-edit-workspace.md +2 -2
- package/dist/ai/recipes/vue/onboarding.md +2 -2
- package/dist/ai/recipes/vue/record-form.md +10 -10
- package/dist/ai/recipes/vue/records-workspace.md +4 -4
- package/dist/ai/recipes/vue/upload-center.md +6 -6
- package/dist/ai/recipes/vue/wizard-form.md +5 -5
- package/dist/ai/source/BbBadge.md +1 -1
- package/dist/ai/source/BbBaseCheckbox.md +1 -1
- package/dist/ai/source/BbBaseDatePicker.md +5 -5
- package/dist/ai/source/BbBaseDatePickerInput.md +2 -2
- package/dist/ai/source/BbBaseRadio.md +1 -1
- package/dist/ai/source/BbBaseRating.md +5 -5
- package/dist/ai/source/BbBaseSelect.md +1 -1
- package/dist/ai/source/BbBaseSlider.md +5 -5
- package/dist/ai/source/BbBaseSwitch.md +1 -1
- package/dist/ai/source/BbBaseTimePickerInput.md +1 -1
- package/dist/ai/source/BbBreadcrumbs.md +20 -1
- package/dist/ai/source/BbButton.md +2 -2
- package/dist/ai/source/BbDialog.md +39 -8
- package/dist/ai/source/BbDropdown.md +1 -1
- package/dist/ai/source/BbDropzone.md +5 -5
- package/dist/ai/source/BbIcon.md +23 -10
- package/dist/ai/source/BbOffCanvas.md +50 -28
- package/dist/ai/source/BbPagination.md +2 -2
- package/dist/ai/source/BbPopover.md +12 -1
- package/dist/ai/source/BbSelectPopover.md +45 -7
- package/dist/ai/source/BbSpinner.md +2 -2
- package/dist/ai/source/BbTable.md +11 -11
- package/dist/ai/source/BbTooltip.md +57 -10
- package/dist/ai/source/ChipsBox.md +1 -1
- package/dist/ai/source/CommaBox.md +1 -1
- package/dist/ai/source/GuidesComponent.md +14 -7
- package/dist/ai/source/OptionsContainer.md +1 -1
- package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseCheckbox/BbBaseCheckbox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue.d.ts +1 -1
- package/dist/components/BbBaseRadio/BbBaseRadio.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/BbBaseSwitch/BbBaseSwitch.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +71 -71
- package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +102 -97
- package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +58 -58
- package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +33 -35
- package/dist/components/BbIcon/iconCache.d.ts +30 -0
- package/dist/components/BbIcon/iconCache.js +21 -3
- package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +57 -56
- package/dist/components/BbPagination/BbPagination.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +138 -138
- package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +43 -43
- package/dist/components/BbSpinner/BbSpinner.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/BbSpinner/types.d.ts +1 -1
- package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/components/BbTable/BbTableDataRow.js +2 -2
- package/dist/components/BbToast/BbToastRegion.vue_vue_type_script_setup_true_lang.js +62 -62
- package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +81 -78
- package/dist/components/BbTooltip/types.d.ts +8 -1
- package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/CommaBox/CommaBox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/composables/useConfirm.d.ts +22 -0
- package/dist/composables/useConfirm.js +43 -40
- package/dist/composables/useSafeAreaInternal.js +1 -1
- package/dist/composables/useToast.d.ts +16 -0
- package/dist/composables/useToast.js +20 -16
- package/dist/composables/useViewportWidth.d.ts +20 -6
- package/dist/composables/useViewportWidth.js +8 -7
- package/dist/icon-scan.d.ts +276 -0
- package/dist/icon-scan.js +485 -0
- package/dist/icons-whitelist.d.ts +5 -4
- package/dist/llms-full.txt +1979 -1527
- package/dist/llms-medium.txt +461 -74
- package/dist/llms.txt +1 -1
- package/dist/nuxt.js +10 -7
- package/dist/plugin.js +1 -0
- package/dist/runtime/nuxt-plugin.js +2 -1
- package/dist/styles.css +1 -1
- package/dist/types/Config.d.ts +18 -0
- package/dist/utils/cssLayers.d.ts +91 -0
- package/dist/utils/cssLayers.js +43 -0
- package/dist/utils/injectLibraryStyles.d.ts +3 -1
- package/dist/utils/injectLibraryStyles.js +24 -23
- package/dist/vite-plugin.d.ts +74 -11
- package/dist/vite.js +292 -253
- package/llms.txt +1 -1
- package/package.json +7 -5
- package/scripts/lib/ensure-manifest.mjs +28 -5
- package/scripts/lib/eslint-plugin.d.ts +35 -0
- package/scripts/lib/eslint-plugin.mjs +299 -3
- package/scripts/lib/hand-roll-hints.mjs +50 -6
- package/scripts/lib/html-attributes.mjs +42 -0
- package/scripts/lib/mcp-config.mjs +38 -34
- package/scripts/lib/public-type-exports.mjs +246 -0
- package/scripts/lib/validate-bb-markup.mjs +205 -12
|
@@ -17,8 +17,11 @@ Per the design language, every `destructive` action must pass through a confirm.
|
|
|
17
17
|
**Requires exactly one `<BbConfirm />` host, mounted at the app root** (`App.vue`
|
|
18
18
|
under Vue, `app.vue` under Nuxt, the `createInertiaApp` render root under
|
|
19
19
|
Inertia) — never one per page or per component. With no host, `await confirm(...)`
|
|
20
|
-
never settles and the caller hangs
|
|
21
|
-
|
|
20
|
+
never settles and the caller hangs; with two, every dialog renders twice. Both
|
|
21
|
+
warn in DEV, including when the only host has unmounted. The no-host warning is
|
|
22
|
+
deferred by a second, because a late-mounting host renders the pending dialog
|
|
23
|
+
and settles the promise. See
|
|
24
|
+
[Agent Contract § Global Host Rule](../guides/agent-contract.md).
|
|
22
25
|
|
|
23
26
|
## Import
|
|
24
27
|
|
|
@@ -16,8 +16,10 @@ per viewport corner and disappear on their own (default **4000 ms**).
|
|
|
16
16
|
under Vue, `app.vue` under Nuxt, the `createInertiaApp` render root under
|
|
17
17
|
Inertia) — never one per page or per component. With no host, messages are
|
|
18
18
|
enqueued but never rendered _and never expire_, because the dismiss timer lives
|
|
19
|
-
inside the host. With two hosts, every message renders once per host.
|
|
20
|
-
|
|
19
|
+
inside the host. With two hosts, every message renders once per host. Both warn
|
|
20
|
+
in DEV, including when the only host has unmounted. The no-host warning is
|
|
21
|
+
deferred by a second, because a late-mounting host renders the queued message.
|
|
22
|
+
See [Agent Contract § Global Host Rule](../guides/agent-contract.md).
|
|
21
23
|
|
|
22
24
|
Rule of thumb: `useToast` for _what just happened_ ("Saved", "Upload complete"),
|
|
23
25
|
`BbAlert` for the _current state of the page_ (a form error summary, a banner).
|
|
@@ -12,6 +12,8 @@ Use this contract before generating any UI code with `bitboss-ui`.
|
|
|
12
12
|
1. Prefer `Bb*` components in consumer-facing code.
|
|
13
13
|
2. Treat low-level primitives (`Base*`, `BbBase*`, `Common*`, listbox internals) as reference-only — **except `BbBaseButton`**, which is a first-class consumer component: the unstyled navigable primitive for any clickable surface (cards, rows, custom links) that isn't a variant-first `BbButton`.
|
|
14
14
|
3. Use the other reference-only components only when the user explicitly asks for internal composition details.
|
|
15
|
+
4. **Not every `Bb*` export is a component.** `BbTableColumn`, `BbDropdownItem`, `BbTabsItem` and `BbBreadcrumbsItem` are TYPES: they describe the objects you pass to a prop (`columns`, `items`), and they never render. Import one with `import type { … } from 'bitboss-ui'`; writing `<BbTableColumn>` is always wrong. The `types` array in `components.json` lists every one with the prop it belongs to, and `npx bitboss-ui check` says so by name if you slip.
|
|
16
|
+
5. **Two dozen of those types are the ones to reach for.** Each carries a `usage` tier: `api` means it appears in a component's own props or events, so it is what you annotate the value you build — `get_component` returns the list for the component you asked about. `wrapper` (`BbButtonProps` / `BbButtonEvents` / `BbButtonSlots` and siblings) is only for wrapping one of our components in one of yours, and `advanced` is everything left over — contexts, directive values, config shapes, the `…VariantType` unions. The tier is decided by that one rule and nothing else, so do not sort by what a name sounds like: `AlertVariantRegistry` reads like a registry but is `api`, because `BbAlert`'s `variant` prop is typed by it. Only `api` entries carry `usedBy`; if a type has one, it is `api`. Types the library does NOT export are that way on purpose: a size or a trigger is a string literal you write directly (`size="lg"`), so there is no name to import and none is missing.
|
|
15
17
|
|
|
16
18
|
## Setup-First Rule
|
|
17
19
|
|
|
@@ -113,7 +115,7 @@ For components accepting `items`:
|
|
|
113
115
|
|
|
114
116
|
## Icons Rule
|
|
115
117
|
|
|
116
|
-
External-provider-first: prefer iconify provider icons — default set `lucide:*` — sourced from `@iconify-json/*` packages the consumer installs separately (`npm i -D @iconify-json/lucide`). Use `local:*` SVGs only for brand-specific art placed in the configured `iconDir`. Only reference icons from installed providers or existing local files — **never invent icon names**. Full workflow: [Icons Policy](./icons-policy.md).
|
|
118
|
+
External-provider-first: prefer iconify provider icons — default set `lucide:*` — sourced from `@iconify-json/*` packages the consumer installs separately (`npm i -D @iconify-json/lucide`). Use `local:*` SVGs only for brand-specific art placed in the configured `iconDir`. Only reference icons from installed providers or existing local files — **never invent icon names**. Provider icons ship only when written as a `prefix:name` literal somewhere in the project (any file type, PHP included) or listed in the plugin's `iconWhitelist`; the same rule applies in dev, and a name that does not resolve renders a placeholder and logs an error instead of throwing. Full workflow: [Icons Policy](./icons-policy.md).
|
|
117
119
|
|
|
118
120
|
## Variants Rule
|
|
119
121
|
|
|
@@ -154,8 +156,9 @@ These overlays also return focus to their trigger element on close, by documente
|
|
|
154
156
|
|
|
155
157
|
**Mount exactly one `<BbToast />` and one `<BbConfirm />` for the whole application, at the app root** — `App.vue` under Vue, `app.vue` under Nuxt, the `createInertiaApp` render root under Inertia. Never one per page, per route, per layout branch or per component; a page that calls `toast()` or `confirm()` mounts nothing.
|
|
156
158
|
|
|
157
|
-
- **Zero hosts** — `toast(...)` enqueues
|
|
158
|
-
- **Two hosts** — every toast and every dialog renders once per host: duplicated DOM, duplicated `role="status"` live regions, duplicated focus traps.
|
|
159
|
+
- **Zero hosts** — `toast(...)` enqueues and the message never expires (the dismiss timer lives inside the host); `await confirm(...)` never settles, so the caller hangs. Neither throws. Both warn once per process in DEV.
|
|
160
|
+
- **Two hosts** — every toast and every dialog renders once per host: duplicated DOM, duplicated `role="status"` live regions, duplicated focus traps. Both warn in DEV, naming the host count.
|
|
161
|
+
- **A host that mounts and then unmounts also warns** (since 2026-09-09). This is the failure mode of mounting a host anywhere but the app root: one in a layout works right up until a route renders outside that layout, and the promise then hangs forever. The warning is **deferred by one second**, not immediate, because a late-mounting host renders the pending dialog and settles the promise normally — so a host that unmounts and remounts across a route change is not a failure and stays quiet. What the warning means is "one second later, still nothing to render this".
|
|
159
162
|
|
|
160
163
|
Standalone examples in this repo (`src/components/*/examples/*.vue`, stories, dev showcases) mount a host inline **because each one is a self-contained root with no application around it**. That is a property of the example harness, not a pattern to copy — in a real app the host is already mounted at the root, and the page must not mount a second one.
|
|
161
164
|
|
|
@@ -168,7 +171,7 @@ Boolean props default to `false` so the bare `<Comp foo>` shorthand turns the fe
|
|
|
168
171
|
|
|
169
172
|
These are the only intentional default-`true` booleans; anywhere else, `true` default is a bug (`check-api` flags it).
|
|
170
173
|
|
|
171
|
-
**Arrows are tooltip-only in v3.** `hideArrow` / `showArrow` / `arrowPadding` were removed from `BbPopover`, `BbSelectPopover`, `BbColorPalette`, and `BbDropdown` (leftover bindings land silently in `$attrs`).
|
|
174
|
+
**Arrows are tooltip-only in v3.** `hideArrow` / `showArrow` / `arrowPadding` were removed from `BbPopover`, `BbSelectPopover`, `BbColorPalette`, and `BbDropdown` (leftover bindings land silently in `$attrs`). `BbTooltip` keeps **both** — `hideArrow` (default `false`, so the arrow shows) and `arrowPadding` (default `10`). See [bb-popover.md](./migration/components/bb-popover.md).
|
|
172
175
|
|
|
173
176
|
## Testing Against bitboss-ui
|
|
174
177
|
|
|
@@ -37,7 +37,8 @@ pinned to the prerelease channel rather than following it.
|
|
|
37
37
|
`locale` explicitly if the app is not Italian** — the plugin defaults to
|
|
38
38
|
`'it'` and there is no build-time warning for leaving it unset.
|
|
39
39
|
3. **Choose intent:**
|
|
40
|
-
- Whole page / flow → hop to **Recipes** below (one platform
|
|
40
|
+
- Whole page / flow → hop to **Recipes** below (one platform folder: the
|
|
41
|
+
layout scaffold plus your page recipe).
|
|
41
42
|
- Single control → [Component picker](./component-picker.md), then open
|
|
42
43
|
**one** `../<ComponentName>.md` contract (Usage & Guidelines first).
|
|
43
44
|
4. **Never invent props.** Confirm names in that contract or in
|
|
@@ -51,16 +52,27 @@ Folders: `../recipes/vue/`, `../recipes/nuxt/`, `../recipes/inertia/`.
|
|
|
51
52
|
|
|
52
53
|
Same page shapes on each platform; only the data/mutation layer differs.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
55
|
+
**`layout-scaffold.md` is a prerequisite, not an option.** It is the three
|
|
56
|
+
app-owned chrome files — the peek-stack context, the `PageShell` frame and the
|
|
57
|
+
`FloatingBottomPanel` selection dock — that every other recipe's
|
|
58
|
+
`@/components/layout/*` imports resolve to. Open it **with** your page recipe;
|
|
59
|
+
the pair counts as one hop.
|
|
60
|
+
|
|
61
|
+
| Intent | Recipe file (kebab) |
|
|
62
|
+
| ----------------------------------------- | ----------------------------------------- |
|
|
63
|
+
| App chrome every recipe imports (**1st**) | `layout-scaffold.md` |
|
|
64
|
+
| Filterable listing + bulk/peek | `records-workspace.md` |
|
|
65
|
+
| Create/edit form | `record-form.md` |
|
|
66
|
+
| Inline-edit table / backlog | `inline-edit-workspace.md` |
|
|
67
|
+
| Hierarchy drill-down (nested tables) | `ownership-atlas.md` (Inertia only today) |
|
|
68
|
+
| Approvals / triage inbox | `approvals-inbox.md` |
|
|
69
|
+
| Uploads + editable queue | `upload-center.md` |
|
|
70
|
+
| Multi-step wizard | `wizard-form.md` |
|
|
71
|
+
| Onboarding checklist | `onboarding.md` |
|
|
72
|
+
| ⌘K / Ctrl+K global command palette | `command-palette.md` |
|
|
73
|
+
|
|
74
|
+
That table is the complete set — every file in each platform folder appears in
|
|
75
|
+
it, so a directory listing (or the MCP `list_recipes`) can add nothing.
|
|
64
76
|
|
|
65
77
|
No recipe for detail-with-tabs, settings, dashboards, or profile pages —
|
|
66
78
|
compose those from the closest listing/form recipe plus the guides
|
|
@@ -70,17 +82,20 @@ compose those from the closest listing/form recipe plus the guides
|
|
|
70
82
|
|
|
71
83
|
## Playbooks (only when the pattern applies)
|
|
72
84
|
|
|
73
|
-
| Need
|
|
74
|
-
|
|
|
75
|
-
| Tied selects / stale model cleanup
|
|
76
|
-
| Async `items` providers / prefill
|
|
77
|
-
| `item-text` / `item-value` / disabled
|
|
78
|
-
| Table cell edit ownership
|
|
79
|
-
| Table row → detail (identity cells)
|
|
80
|
-
| Peek that drills into related records
|
|
81
|
-
| Icons (`lucide:*`, never invent names)
|
|
82
|
-
| Tokens / styling around components
|
|
83
|
-
|
|
|
85
|
+
| Need | Guide |
|
|
86
|
+
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| Tied selects / stale model cleanup | [coherence-playbook](./coherence-playbook.md) |
|
|
88
|
+
| Async `items` providers / prefill | [fetch-items-playbook](./fetch-items-playbook.md) |
|
|
89
|
+
| `item-text` / `item-value` / disabled | [options-items-playbook](./options-items-playbook.md) |
|
|
90
|
+
| Table cell edit ownership | [inline-edit-playbook](./inline-edit-playbook.md) |
|
|
91
|
+
| Table row → detail (identity cells) | [agent-contract](./agent-contract.md) § Inline Links |
|
|
92
|
+
| Peek that drills into related records | [agent-contract](./agent-contract.md) § Consult-Stack |
|
|
93
|
+
| Icons (`lucide:*`, never invent names) | [icons-policy](./icons-policy.md) |
|
|
94
|
+
| Tokens / styling around components | [design-language](./design-language.md) |
|
|
95
|
+
| Theming a whole app / retuning a component | [design-tokens](./design-tokens.md) — the two tiers, and why most restyling is a component token, not a `--bb-*` one |
|
|
96
|
+
| A Tailwind utility on a `Bb*` component does nothing, or half-applies | [installation](./installation-and-plugin-setup.md) § Cascade layers — v4 is layered, we are not by default. It is per PROPERTY: a utility loses only where we declare the same one, and a shorthand like `p-4` can half-apply. `cssLayers: TAILWIND_V4_LAYERS` in the build plugin |
|
|
97
|
+
| Upgrading from bitboss-ui 2.x | [migration/v2-to-v3](./migration/v2-to-v3.md) |
|
|
98
|
+
| Bumping between two v3 prereleases | `npx bitboss-ui changelog --since <your version>` — what BREAKS in the bump; then `check --manifest` for the files it breaks |
|
|
84
99
|
|
|
85
100
|
---
|
|
86
101
|
|
|
@@ -231,24 +231,29 @@ a composable you want is not in this corpus's `composables/` folder — it is no
|
|
|
231
231
|
here.** Check this list before inventing a name; if the need is genuinely absent,
|
|
232
232
|
compose it or write it yourself.
|
|
233
233
|
|
|
234
|
-
| Assumed component or composable | Reality
|
|
235
|
-
| -------------------------------------------------------------- |
|
|
236
|
-
| `BbColumnManager` — show/hide/reorder columns | Does not exist as a component. Order is `v-model:order` on `BbTable` (an array of keys any panel can write; `reorderable` adds the built-in header drag + keyboard handle); visibility is `hidden: true` on the column definition (the column stays declared and keeps its slot in the order; header slots get a `hideColumn()` that fires `hide:column`). Compose the panel from `BbPopover` + `BbCheckbox`.
|
|
237
|
-
| `BbKanban` / drag-and-drop board | Does not exist. There is no reusable DnD primitive in the library (`BbTable`'s header drag is internal to the table) — compose the board from `BbTable` (a table/list view of the same data) or hand-roll the drag-and-drop interaction yourself.
|
|
238
|
-
| `BbModal` | It is [BbDialog](../BbDialog.md).
|
|
239
|
-
| `BbDrawer` / `BbSheet` | It is [BbOffCanvas](../BbOffCanvas.md).
|
|
240
|
-
| `BbCombobox` / `BbAutocomplete` | It is [BbSelect](../BbSelect.md) / [BbSelectPopover](../BbSelectPopover.md) — search is built in, see `disable-writing`.
|
|
241
|
-
| `BbToggle` / `BbToggleGroup` | It is [BbButton](../BbButton.md) with `v-model` (+ `true-value` / `false-value`), which owns `aria-pressed` and the active class.
|
|
242
|
-
| `TagChip` / colored, read-only tag chip | It is [BbBadge](../BbBadge.md) — [BbTag](../BbTag.md) is a tags-_input_ only, not a display component. Use `BbBadge` with a registered `soft-*` variant for a fixed palette, or a one-off `--bg`/`--color` override per row for arbitrary/data-driven colors (BbBadge.md § One-off colors) — not a registered variant per distinct color.
|
|
243
|
-
| `BbTab` (singular) | The consumer API is [BbTabs](../BbTabs.md) / `BbTabsRoot`. `BbTab` appears only as the **v2 "before" side** of the migration diffs.
|
|
244
|
-
| `BbCard` | Does not exist — a card is a `<div>` with tokens. See [design-language](./design-language.md).
|
|
245
|
-
| `BbList` / `BbListItem` / `BbDescriptionList` | Do not exist — a list row is an `<li>` with tokens, and [BbBaseButton](../BbBaseButton.md) makes the whole row navigable. See the note under Data Display.
|
|
246
|
-
| `useLiveFeed` / any polling or cross-tab feed helper | Does not exist, and neither does a cross-tab helper — `useBroadcastChannelInstance` was removed (cross-tab messaging is not a UI concern). Use `useBroadcastChannel` from `@vueuse/core`; the polling and the merge are yours.
|
|
247
|
-
| `BbStepper` / `BbWizard` | Does not exist as a component; the wizard-form recipe composes one.
|
|
248
|
-
| `BbSkeleton` (standalone) | Only `BbTable` ships skeletons, via the column `skeleton` field.
|
|
249
|
-
| `BbCommand` / command palette | Does not exist. Compose `BbDialog` + `BbSelectPopover
|
|
250
|
-
| `BbCalendar` (month / scheduling view) | Does not exist. [BbDatePicker](../BbDatePicker.md) is a picker, not a calendar surface.
|
|
251
|
-
| `
|
|
234
|
+
| Assumed component or composable | Reality |
|
|
235
|
+
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
236
|
+
| `BbColumnManager` — show/hide/reorder columns | Does not exist as a component. Order is `v-model:order` on `BbTable` (an array of keys any panel can write; `reorderable` adds the built-in header drag + keyboard handle); visibility is `hidden: true` on the column definition (the column stays declared and keeps its slot in the order; header slots get a `hideColumn()` that fires `hide:column`). Compose the panel from `BbPopover` + `BbCheckbox`. |
|
|
237
|
+
| `BbKanban` / drag-and-drop board | Does not exist. There is no reusable DnD primitive in the library (`BbTable`'s header drag is internal to the table) — compose the board from `BbTable` (a table/list view of the same data) or hand-roll the drag-and-drop interaction yourself. |
|
|
238
|
+
| `BbModal` | It is [BbDialog](../BbDialog.md). |
|
|
239
|
+
| `BbDrawer` / `BbSheet` | It is [BbOffCanvas](../BbOffCanvas.md). |
|
|
240
|
+
| `BbCombobox` / `BbAutocomplete` | It is [BbSelect](../BbSelect.md) / [BbSelectPopover](../BbSelectPopover.md) — search is built in, see `disable-writing`. |
|
|
241
|
+
| `BbToggle` / `BbToggleGroup` | It is [BbButton](../BbButton.md) with `v-model` (+ `true-value` / `false-value`), which owns `aria-pressed` and the active class. |
|
|
242
|
+
| `TagChip` / colored, read-only tag chip | It is [BbBadge](../BbBadge.md) — [BbTag](../BbTag.md) is a tags-_input_ only, not a display component. Use `BbBadge` with a registered `soft-*` variant for a fixed palette, or a one-off `--bg`/`--color` override per row for arbitrary/data-driven colors (BbBadge.md § One-off colors) — not a registered variant per distinct color. |
|
|
243
|
+
| `BbTab` (singular) | The consumer API is [BbTabs](../BbTabs.md) / `BbTabsRoot`. `BbTab` appears only as the **v2 "before" side** of the migration diffs. |
|
|
244
|
+
| `BbCard` | Does not exist — a card is a `<div>` with tokens. See [design-language](./design-language.md). |
|
|
245
|
+
| `BbList` / `BbListItem` / `BbDescriptionList` | Do not exist — a list row is an `<li>` with tokens, and [BbBaseButton](../BbBaseButton.md) makes the whole row navigable. See the note under Data Display. |
|
|
246
|
+
| `useLiveFeed` / any polling or cross-tab feed helper | Does not exist, and neither does a cross-tab helper — `useBroadcastChannelInstance` was removed (cross-tab messaging is not a UI concern). Use `useBroadcastChannel` from `@vueuse/core`; the polling and the merge are yours. |
|
|
247
|
+
| `BbStepper` / `BbWizard` | Does not exist as a component; the [wizard-form recipe](../recipes/inertia/wizard-form.md) (also under `vue/`, `nuxt/`) composes one. |
|
|
248
|
+
| `BbSkeleton` (standalone) | Only `BbTable` ships skeletons, via the column `skeleton` field. |
|
|
249
|
+
| `BbCommand` / command palette | Does not exist. Compose `BbDialog` + `BbSelectPopover` — do not hand-roll the search: the [command-palette recipe](../recipes/inertia/command-palette.md) (also under `vue/`, `nuxt/`) already wires debouncing, race protection, grouping and keyboard nav. |
|
|
250
|
+
| `BbCalendar` (month / scheduling view) | Does not exist. [BbDatePicker](../BbDatePicker.md) is a picker, not a calendar surface. |
|
|
251
|
+
| `BbDataTable` / `BbDataGrid` / data grid | It is [BbTable](../BbTable.md) — sorting, selection, column order/resize, virtual scrolling and inline edit all live there. |
|
|
252
|
+
| `filter` / `filter-method` prop on BbTable, column filters | No `filter` prop, and its absence is not a missing feature. A filter is an input to the FETCH: put your filter state in `dependencies` and the provider refetches (debounced, value-hashed, races discarded). Per-column filter UI goes in `#header:<key>:append`, which keeps the built-in sort button. A whole filtered listing is the records-workspace recipe. Client-side over a plain array, use a computed: `:items="filteredItems"`. See [BbTable](../BbTable.md) § Filtering. |
|
|
253
|
+
| `BbSnackbar` / `BbNotification` / flash message | It is [BbToast](../BbToast.md), raised through the `useToast` composable. |
|
|
254
|
+
| `BbRichTextEditor` / `BbEditor` / WYSIWYG | Does not exist, and no rich-text or markdown editor ships here. [BbTextarea](../BbTextarea.md) is plain text only; bring your own editor. |
|
|
255
|
+
| `BbEmptyState` / `BbPlaceholder` / no-results panel | Does not exist as a component. [BbTable](../BbTable.md) has a `#no-data` slot; elsewhere an empty state is your own markup with tokens. |
|
|
256
|
+
| `BbChart` / `BbGraph`, `BbCarousel`, `BbTimeline`, `BbDivider` | Do not exist. No charting, carousel or timeline primitive ships here. |
|
|
252
257
|
|
|
253
258
|
App chrome — an app shell, sidebar, page header, selection dock — is
|
|
254
259
|
deliberately **not** a component: it is yours to own. The per-platform
|
|
@@ -14,8 +14,13 @@ components or choose component variants.
|
|
|
14
14
|
1. **Never hardcode colors** in custom CSS around components. Use the `--bb-*`
|
|
15
15
|
custom properties below so your surfaces follow theme and dark mode.
|
|
16
16
|
2. **Never override component internals** (`.bb-*__*` element classes) from
|
|
17
|
-
consumer code
|
|
18
|
-
|
|
17
|
+
consumer code, and never target the reference-only families at all
|
|
18
|
+
(`.bb-base-*`, `.bb-common-*`, listbox internals) — they are the CSS twin of
|
|
19
|
+
the `BbBase*` / `Common*` import boundary and they get restructured without
|
|
20
|
+
a rename table. Restyle via tokens, variants, or documented props. A
|
|
21
|
+
component's **root** class (`.bb-table`, `.bb-badge`) is a legitimate target
|
|
22
|
+
for setting that component's own tokens — see "How to style around the
|
|
23
|
+
library" below.
|
|
19
24
|
3. **One primary action per view region.** Everything else is `secondary`,
|
|
20
25
|
`outline`, or `ghost`.
|
|
21
26
|
4. **`destructive` is only for irreversible actions**, and destructive flows
|
|
@@ -25,23 +30,67 @@ components or choose component variants.
|
|
|
25
30
|
|
|
26
31
|
## How to style around the library
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
Four lanes, in order of preference. Do not skip to a later lane because an
|
|
34
|
+
earlier one has no obvious knob — check the lane first.
|
|
29
35
|
|
|
30
36
|
1. **Component look → props.** Pick a variant, `size`, `compact`. Never write
|
|
31
37
|
CSS to make a component look different.
|
|
32
38
|
2. **Page layout & spacing → Tailwind utilities** (or your project's
|
|
33
39
|
equivalent) on your own wrapper elements — `flex items-end gap-3`,
|
|
34
40
|
`grid gap-6`, `max-w-3xl`. Components size themselves; you only place them.
|
|
35
|
-
3. **
|
|
41
|
+
3. **Retuning one component → its own component tokens.** Every component
|
|
42
|
+
declares unprefixed `--*` tokens: cell height, padding, badge fill, tab
|
|
43
|
+
strip height. **Set the token on the element that declares it** — that is
|
|
44
|
+
usually the component's root class, and an inline style on the component
|
|
45
|
+
then works too:
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
.bb-table {
|
|
49
|
+
--cell-h: 32px;
|
|
50
|
+
--padding-x: 8px;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```vue
|
|
55
|
+
<BbTree style="--indent: 14px" />
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
⚠ **silent** — a component made of several elements declares some tokens on
|
|
59
|
+
an inner one, and a value you set further out is _masked_, not inherited:
|
|
60
|
+
the inner element's own declaration wins. `BbTabs` keeps `--list-h` on
|
|
61
|
+
`.bb-tabs-list`, so `<BbTabs style="--list-h: 40px">` does nothing and
|
|
62
|
+
`.bb-tabs-list { --list-h: 40px }` is the working form. Check which selector
|
|
63
|
+
declares the token before you set it.
|
|
64
|
+
|
|
65
|
+
4. **Genuinely custom visuals → `--bb-*` tokens.** The few CSS rules that
|
|
36
66
|
remain (a custom panel, a status dot) use theme tokens so they follow
|
|
37
67
|
theming and dark mode automatically.
|
|
38
68
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
**Finding a component's tokens.** They are not in the `--bb-*` inventory below
|
|
70
|
+
— that tier is the brand knobs only, and it is deliberately small. Component
|
|
71
|
+
tokens live in the component's own CSS, on whichever element consumes them.
|
|
72
|
+
Read `dist/ai/source/<Name>.md`, which carries that CSS verbatim, and grep it
|
|
73
|
+
for the declaration rather than assuming the root. Some components also call
|
|
74
|
+
theirs out in the contract, as [BbTree](../BbTree.md) does for the
|
|
75
|
+
`--indent`/`--gap` pair you anchor connector lines to.
|
|
76
|
+
|
|
77
|
+
**A missing `--bb-*` token is not a gap in the library.** If you want a shorter
|
|
78
|
+
table row and there is no `--bb-table-row-h`, that is lane 3 telling you to set
|
|
79
|
+
`--cell-h` on `.bb-table`, not an invitation to write element-class overrides.
|
|
80
|
+
The full rules, including how per-size lookup tables interact with specificity,
|
|
81
|
+
are in [design-tokens.md](./design-tokens.md) § "Overriding one from a consumer
|
|
82
|
+
app".
|
|
83
|
+
|
|
84
|
+
**A utility class on a component that does nothing is a layer problem, not a
|
|
85
|
+
token problem.** Tailwind v4 puts its utilities in `@layer utilities`, and the
|
|
86
|
+
cascade ranks any unlayered rule above any layered one regardless of
|
|
87
|
+
specificity — so `<BbButton class="px-6">` loses to the library's own padding
|
|
88
|
+
until the library is layered too. That is a build-plugin setting, not CSS:
|
|
89
|
+
`cssLayers: TAILWIND_V4_LAYERS` (exported from `bitboss-ui/vite`) places the
|
|
90
|
+
library after Preflight and before the utilities, and the utility wins. Opt-in,
|
|
91
|
+
and the whole story is in
|
|
92
|
+
[installation-and-plugin-setup](./installation-and-plugin-setup.md) § Cascade
|
|
93
|
+
layers.
|
|
45
94
|
|
|
46
95
|
## Token Vocabulary
|
|
47
96
|
|
|
@@ -51,6 +100,13 @@ that change and everything derived follows. The full architecture (tiers,
|
|
|
51
100
|
naming rules, the emphasis ladder) lives in
|
|
52
101
|
[design-tokens.md](./design-tokens.md); this is the consumer summary.
|
|
53
102
|
|
|
103
|
+
**This list is closed.** There is no Tailwind-style numeric ramp — no
|
|
104
|
+
`--bb-primary-800`, no `--bb-gray-100`. Emphasis comes from the derived tokens
|
|
105
|
+
below (`--bb-muted`, `--bb-text-muted`, `--bb-surface-hover`) and from
|
|
106
|
+
`color-mix()` against a knob, not from a shade number. A `--bb-*` name that is
|
|
107
|
+
not in these tables resolves to nothing and fails silently, so a rule built on
|
|
108
|
+
one renders with no value at all rather than erroring.
|
|
109
|
+
|
|
54
110
|
### Surfaces & structure
|
|
55
111
|
|
|
56
112
|
| Token | Meaning |
|
|
@@ -106,9 +162,11 @@ so themes with loud brand colors keep gray chrome.
|
|
|
106
162
|
|
|
107
163
|
Input-family tokens also exist (`--bb-input-h`, `--bb-input-px`,
|
|
108
164
|
`--bb-label-fs`…) — see design-tokens.md. Component-specific tokens are
|
|
109
|
-
**unprefixed** and defined on
|
|
110
|
-
`--
|
|
111
|
-
coherently instead of styling individual
|
|
165
|
+
**unprefixed** and defined on the element that consumes them, usually the
|
|
166
|
+
component's root class (e.g. BbTree's `--indent`). Override them per instance
|
|
167
|
+
or at a scope to retune a whole area coherently instead of styling individual
|
|
168
|
+
components, and set them on the declaring element — see lane 3 above for the
|
|
169
|
+
masking trap.
|
|
112
170
|
|
|
113
171
|
## Dark Mode
|
|
114
172
|
|
|
@@ -155,6 +213,76 @@ recipe in [design-tokens.md](./design-tokens.md).
|
|
|
155
213
|
Never invent a variant name that is not built-in or registered in the
|
|
156
214
|
project's plugin config — check `vite.config.*` / `nuxt.config.*` first.
|
|
157
215
|
|
|
216
|
+
### Re-skinning a variant the library already ships
|
|
217
|
+
|
|
218
|
+
Registering a variant covers a name we do **not** ship. Changing how an
|
|
219
|
+
existing one looks is a different job, and the first question is not "which
|
|
220
|
+
CSS" but **how far the change should reach**:
|
|
221
|
+
|
|
222
|
+
1. **The colour itself is wrong for this brand** (primary is blue, not black) →
|
|
223
|
+
override the **theme knobs**, not any component. `--bb-primary` and its
|
|
224
|
+
family feed every surface, and every derived value and dark-mode variant
|
|
225
|
+
follows. This is the theme builder's path and the only one that stays
|
|
226
|
+
coherent.
|
|
227
|
+
2. **One area should look different** (a toolbar, a marketing header) →
|
|
228
|
+
re-point the same knobs **on a scope**. Custom properties inherit, so this
|
|
229
|
+
reaches every component in the subtree, whatever shape its variant block
|
|
230
|
+
has:
|
|
231
|
+
|
|
232
|
+
```css
|
|
233
|
+
.toolbar {
|
|
234
|
+
--bb-primary: #2563eb;
|
|
235
|
+
--bb-primary-hover: #1d4ed8;
|
|
236
|
+
--bb-primary-fg: #fff;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Prefer this to per-component rules. It cannot desynchronise states, and it
|
|
241
|
+
survives dark mode.
|
|
242
|
+
|
|
243
|
+
3. **One component's take on one variant is wrong** (badges too loud, buttons
|
|
244
|
+
fine) → you have to read that component's variant block first, because
|
|
245
|
+
**they are not built the same way**:
|
|
246
|
+
|
|
247
|
+
| Family | Its variant block sets | So a re-skin is… |
|
|
248
|
+
| ---------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
249
|
+
| `BbBadge`, `BbTooltip` | component tokens (`--bg`, `--color` / `--fg`, `--border-color`, `--ring`) | re-point those tokens |
|
|
250
|
+
| `BbButton` | CSS properties directly (`background-color`, `border-color`, `color`) | **no token to re-point** — scope the knobs (2), or restate _every_ state |
|
|
251
|
+
| `BbAlert` | both (`--main-color` / `--muted-color`, plus `background-color`) | read the block; half of it is tokens |
|
|
252
|
+
| `BbToast` | only `--icon-color` — the variant tints the icon, nothing else | there is no fill to re-skin |
|
|
253
|
+
|
|
254
|
+
Read the block in `dist/ai/source/<Name>.md` before writing the rule. Assuming
|
|
255
|
+
the badge recipe works on a button is the single most common way this goes
|
|
256
|
+
wrong: `.bb-button--primary { --bg: … }` is silently inert, because the button
|
|
257
|
+
never reads a `--bg`.
|
|
258
|
+
|
|
259
|
+
**Three traps, all of which fail quietly.**
|
|
260
|
+
|
|
261
|
+
**1. A variant is not one declaration.** `primary` on a button sets the rest
|
|
262
|
+
state, `:hover`, `[aria-pressed]`, the `:focus-visible` ring, _and_ an adjacent
|
|
263
|
+
group border. Override `background-color` alone and the old brand survives in
|
|
264
|
+
hover and focus — visible only on interaction, which is why this one reaches
|
|
265
|
+
production. Overriding the knobs (1 or 2) changes every state at once; that is
|
|
266
|
+
the argument for doing it there.
|
|
267
|
+
|
|
268
|
+
**2. Variant blocks are two classes deep.** They are nested under the root, so
|
|
269
|
+
they compile to `.bb-button.bb-button--primary` — more specific than the
|
|
270
|
+
single-class rule you are about to write:
|
|
271
|
+
|
|
272
|
+
```css
|
|
273
|
+
.bb-button--primary {
|
|
274
|
+
background-color: red;
|
|
275
|
+
} /* 0,1,0 — loses, renders nothing */
|
|
276
|
+
.bb-button.bb-button--primary {
|
|
277
|
+
background-color: red;
|
|
278
|
+
} /* 0,2,0 — wins */
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**3. Override the knob, not the derived value.** Values built with
|
|
282
|
+
`color-mix()` off a knob re-derive per theme. Replacing the derived value
|
|
283
|
+
hardcodes it for both schemes, and the bug surfaces as "dark mode looks wrong"
|
|
284
|
+
months later.
|
|
285
|
+
|
|
158
286
|
## Using color
|
|
159
287
|
|
|
160
288
|
**Reach for color more than the monochrome default suggests — but make it mean
|
|
@@ -295,12 +295,34 @@ Rules:
|
|
|
295
295
|
SelectPopover to read) keep a distinctive name per rule 3.
|
|
296
296
|
|
|
297
297
|
Component tokens are still real affordances: a consumer can retune one
|
|
298
|
-
instance (`<
|
|
298
|
+
instance (`<BbTree style="--indent: 14px">`) or a whole page scope. They are
|
|
299
299
|
just not global namespace.
|
|
300
300
|
|
|
301
301
|
### Overriding one from a consumer app
|
|
302
302
|
|
|
303
|
-
|
|
303
|
+
Four rules, in order of how often they are broken.
|
|
304
|
+
|
|
305
|
+
**0. Set it on the element that declares it.** Sealing (rule 2 above) is what
|
|
306
|
+
makes an inline style on the component root work — and it is also what makes
|
|
307
|
+
the same style _silently do nothing_ when the token lives on an inner element.
|
|
308
|
+
The inner declaration is not overridden by an inherited value; it replaces it.
|
|
309
|
+
`BbTabs` declares `--list-h` on `.bb-tabs-list`, not on `.bb-tabs`:
|
|
310
|
+
|
|
311
|
+
```css
|
|
312
|
+
/* works — the list is the element that declares it */
|
|
313
|
+
.bb-tabs-list {
|
|
314
|
+
--list-h: 40px;
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
```vue
|
|
319
|
+
<!-- does nothing — .bb-tabs-list re-declares --list-h and masks this -->
|
|
320
|
+
<BbTabs style="--list-h: 40px" />
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Grep the component's CSS in `dist/ai/source/<Name>.md` for the declaration
|
|
324
|
+
before choosing a selector. A masked token produces no warning and no visual
|
|
325
|
+
change.
|
|
304
326
|
|
|
305
327
|
**1. Override only what you actually render.** Several components expose a
|
|
306
328
|
per-size lookup table (`--icon-size-xs … --icon-size-2xl`, `--h-xs … --h-2xl`)
|
|
@@ -334,7 +356,10 @@ silently does nothing on every `size="sm"` one — the confusing half-failure th
|
|
|
334
356
|
sends people to the shotgun in rule 1. The per-size entry has no competing
|
|
335
357
|
declaration, so it always wins. (A scope selector of equal-or-greater
|
|
336
358
|
specificity also works, but you have to keep winning that race; overriding the
|
|
337
|
-
entry does not.
|
|
359
|
+
entry does not. If your app is on Tailwind v4 or otherwise layered, the race
|
|
360
|
+
has a structural answer: `cssLayers` in the build plugin puts the library in a
|
|
361
|
+
layer below your utilities, and a utility then wins without any specificity at
|
|
362
|
+
all — see the install guide § Cascade layers.)
|
|
338
363
|
|
|
339
364
|
**3. Scope it to the component, never `:root`.** Short names are deliberately
|
|
340
365
|
reused across components — `--icon-size` is read by `BbButton`, `BbBadge` and
|
|
@@ -656,7 +681,7 @@ like it would replace the seal rule structurally, but it breaks the system:
|
|
|
656
681
|
|
|
657
682
|
- `inherits: false` stops the root→descendant flow _inside_ a component
|
|
658
683
|
(declare on `.bb-base-switch`, consume on `__icon` — that is inheritance),
|
|
659
|
-
which is also what makes `style="--
|
|
684
|
+
which is also what makes `style="--indent: 14px"` overrides work. CSS
|
|
660
685
|
cannot express "inherit within my subtree but not into nested components".
|
|
661
686
|
- A registered property always has a value, so `var(--max-h, none)`
|
|
662
687
|
fallbacks never fire again — our sealing pattern dies.
|
|
@@ -16,12 +16,16 @@ Mandatory for AI-generated code.
|
|
|
16
16
|
2. **`local:*` is for brand-specific art only** — logos, custom marks, one-off
|
|
17
17
|
illustrations. They are SVG files in the configured `iconDir`.
|
|
18
18
|
3. **Never invent icon names.** Use only names that exist in an installed
|
|
19
|
-
`@iconify-json/*` set or as files in `iconDir`. A
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
`@iconify-json/*` set or as files in `iconDir`. A name that does not
|
|
20
|
+
resolve renders the built-in placeholder (a question mark in a circle,
|
|
21
|
+
tagged `data-bb-icon-missing="<name>"`) and logs one `console.error` with
|
|
22
|
+
the fix — in dev also as a Vite error overlay. It never throws.
|
|
23
|
+
4. **Write icon names as literals, anywhere in the project.** The plugin
|
|
24
|
+
scans the whole project (PHP included) for `provider:name` literals and
|
|
25
|
+
bundles only those; `local:` ships everything. A name the scanner cannot
|
|
26
|
+
see as a literal — composed at runtime (`` `lucide:${kind}` ``), stored in a
|
|
27
|
+
database, chosen in an admin panel — goes in the plugin's `iconWhitelist`.
|
|
28
|
+
This holds in dev exactly as in production: dev registers the same set.
|
|
25
29
|
5. **Never hand-place `BbIcon` where a component already accepts an icon** via
|
|
26
30
|
a prop, item field, or dedicated slot (catalog below).
|
|
27
31
|
|
|
@@ -35,19 +39,38 @@ The `bitbossUi` Vite plugin (`iconDir` option) — or the Nuxt module (config ke
|
|
|
35
39
|
auto-discovered. Installing `@iconify-json/lucide` makes every valid
|
|
36
40
|
`lucide:<name>` available; removing it makes them all errors. Nothing to
|
|
37
41
|
configure.
|
|
38
|
-
- **
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
- **What ships — `local:` everything, providers only what is scanned or
|
|
43
|
+
whitelisted.** Every SVG in `iconDir` is bundled. For provider sets the
|
|
44
|
+
plugin reads the **whole project** as bytes — every file under the Vite
|
|
45
|
+
root, PHP, Blade, JSON, SQL seeders and Markdown included — looking for
|
|
46
|
+
`<prefix>:<name>` where `<prefix>` is an installed set; what resolves in the
|
|
47
|
+
set's `icons.json` (icons and aliases) becomes a lazy chunk. Skipped:
|
|
48
|
+
whatever `.gitignore` ignores (every level), `node_modules`, `vendor`,
|
|
49
|
+
`storage`, the build `outDir`, every hidden path (`.something`), binary
|
|
50
|
+
files, and paths listed in `iconsScanner.exclude`. Extra locations
|
|
51
|
+
(`iconsScanner.include`) are additive. No language is parsed, so a Laravel
|
|
52
|
+
enum returning `'tabler:user-share'` counts as a literal.
|
|
53
|
+
- **Dev is prod.** The same scan runs on dev start (once, cached by content
|
|
54
|
+
hash under `node_modules/.cache/bitboss-ui/`) and again per file on every
|
|
55
|
+
save, so the registry in dev is exactly the set the build emits. Typing a
|
|
56
|
+
new literal into a template renders it without a restart. An icon that is
|
|
57
|
+
neither scanned nor whitelisted therefore shows the placeholder and the
|
|
58
|
+
error on first render in dev — not after deploy.
|
|
59
|
+
- **Names the scanner cannot see** — composed at runtime, persisted in a
|
|
60
|
+
database, picked by an admin — go in `iconWhitelist`: exact names
|
|
61
|
+
(`'tabler:crown'`) or globs (`'tabler:calendar-*'`). An entry that resolves
|
|
62
|
+
in no installed set fails the build with the same message the overlay shows.
|
|
63
|
+
- **A missing name is loud, in both environments.** `BbIcon` renders the
|
|
64
|
+
built-in placeholder glyph with `data-bb-icon-missing="<requested name>"`
|
|
65
|
+
and logs one `console.error` per name per page load; dev adds the Vite
|
|
66
|
+
error overlay. A typo inside an installed set reads "does not exist in the
|
|
67
|
+
lucide icon set — did you mean `lucide:…`?"; a real icon nobody wrote as a
|
|
68
|
+
literal reads "exists in the lucide icon set but is not bundled … add it to
|
|
69
|
+
`iconWhitelist`"; a set that is not installed reads "no installed icon set
|
|
70
|
+
has the prefix `mdi` (installed sets: lucide) … run:
|
|
71
|
+
`npm install -D @iconify-json/mdi`". Read the message: a typo wants a
|
|
72
|
+
different name, an unscanned name wants the whitelist, a missing set wants
|
|
73
|
+
an install, never a guess.
|
|
51
74
|
- **Local icons**: `iconDir` is scanned recursively; each `.svg` becomes
|
|
52
75
|
`local:<basename-without-extension>`. A bare, unprefixed name normalizes to
|
|
53
76
|
`local:`. Adding/removing files is picked up live in dev, and the plugin
|
|
@@ -256,8 +279,10 @@ markup) yourself:
|
|
|
256
279
|
(slot content or `text`; `aria-label` only to override).
|
|
257
280
|
- Inventing or guessing icon names; assuming a set is installed without
|
|
258
281
|
checking `package.json`.
|
|
259
|
-
- Runtime-composed icon names (template strings / concatenation)
|
|
260
|
-
the
|
|
282
|
+
- Runtime-composed icon names (template strings / concatenation) without a
|
|
283
|
+
matching `iconWhitelist` entry — invisible to the scanner, so they render the
|
|
284
|
+
placeholder (in dev too). Prefer a lookup of full literals; whitelist what
|
|
285
|
+
must stay dynamic.
|
|
261
286
|
- Setting `color`/`fill` on the icon element instead of the parent.
|
|
262
287
|
- Falling back to random provider sets when a needed `local:` asset is missing —
|
|
263
288
|
ask for the SVG (or approval to substitute) instead.
|