bitboss-ui 3.0.0-beta.0 → 3.0.0-beta.2
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/bin/bitboss-ui.mjs +133 -12
- package/dist/ai/changelog.json +1 -1
- package/dist/ai/components.json +2 -2
- package/dist/ai/guides/ai-router.md +2 -2
- package/dist/ai/guides/design-tokens.md +46 -6
- package/dist/ai/guides/installation-and-plugin-setup.md +71 -5
- package/dist/ai/guides/migration/components/bb-alert.md +37 -0
- package/dist/ai/guides/migration/components/bb-avatar.md +47 -8
- package/dist/ai/guides/migration/components/bb-badge.md +23 -1
- package/dist/ai/guides/migration/components/bb-button.md +64 -0
- package/dist/ai/guides/migration/components/bb-checkbox-group.md +55 -1
- package/dist/ai/guides/migration/components/bb-date-picker-input.md +9 -2
- package/dist/ai/guides/migration/components/bb-dialog.md +121 -11
- package/dist/ai/guides/migration/components/bb-icon.md +42 -0
- package/dist/ai/guides/migration/components/bb-offcanvas.md +35 -1
- package/dist/ai/guides/migration/components/bb-rating.md +52 -1
- package/dist/ai/guides/migration/components/bb-select.md +48 -0
- package/dist/ai/guides/migration/components/bb-table.md +156 -10
- package/dist/ai/guides/migration/components/bb-tabs.md +79 -1
- package/dist/ai/guides/migration/components/bb-text-input.md +23 -1
- package/dist/ai/guides/migration/components/bb-toast.md +44 -10
- package/dist/ai/guides/migration/components/use-confirm.md +48 -13
- package/dist/ai/guides/migration/v2-to-v3.md +626 -108
- package/dist/ai/index.md +9 -9
- package/dist/ai/source/BbDialog.md +0 -3
- package/dist/ai/source/BbDropdown.md +24 -1
- package/dist/ai/source/BbDropdownGroup.md +24 -1
- package/dist/index.d.ts +2 -1
- package/dist/llms-full.txt +1814 -367
- package/dist/llms-medium.txt +82 -16
- package/dist/llms.txt +11 -11
- package/dist/styles.css +1 -1
- package/llms.txt +12 -12
- package/package.json +2 -1
- package/scripts/lib/validate-bb-markup.mjs +105 -17
package/bin/bitboss-ui.mjs
CHANGED
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
* components.json manifest (unknown props, removed props, bad
|
|
18
18
|
* v-models, unknown `<template #slot>` names, `href`/`to`/
|
|
19
19
|
* `method` on a component that doesn't declare them). Exits 1
|
|
20
|
-
* on findings; supports --json output.
|
|
20
|
+
* on findings; supports --json output. Every finding carries the
|
|
21
|
+
* source line. `--allow-component <Name>` (or package.json's
|
|
22
|
+
* `bitboss-ui.allowComponents`) exempts an app-owned component
|
|
23
|
+
* whose name happens to start with `Bb`.
|
|
24
|
+
*
|
|
25
|
+
* SCOPE: `check` knows the INSTALLED v3 API and nothing about v2.
|
|
26
|
+
* "was removed from X" is a recorded v2→v3 break; "is not in X's
|
|
27
|
+
* API" only says the prop is not a v3 prop of that component —
|
|
28
|
+
* it may never have been one anywhere. `printFindingLegend` says
|
|
29
|
+
* so in the output, because two migrating fleets each spent real
|
|
30
|
+
* time discovering it the hard way.
|
|
21
31
|
* mcp Start a stdio MCP server exposing the dist/ai knowledge base
|
|
22
32
|
* (search_components, get_component, list_recipes, get_recipe,
|
|
23
33
|
* validate) to MCP-capable agent harnesses.
|
|
@@ -445,12 +455,84 @@ function summarizeFileCounts(files) {
|
|
|
445
455
|
return exts.map((ext) => `${counts.get(ext)} ${ext}`).join(' + ');
|
|
446
456
|
}
|
|
447
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Component names the PROJECT owns despite the `Bb` prefix, read from disk so
|
|
460
|
+
* CI, the editor and a local run agree without repeating a flag.
|
|
461
|
+
*
|
|
462
|
+
* Two sources, merged: `bitboss-ui.check.json`'s `allowComponents`, and
|
|
463
|
+
* package.json's `bitboss-ui.allowComponents`. The package.json key is the one
|
|
464
|
+
* to reach for — it sits next to the `eslint.config.js` that already carries
|
|
465
|
+
* the same list for `no-unknown-attributes`, so the two gates stay in sync.
|
|
466
|
+
* Unreadable/malformed files are ignored: a broken config must not turn into a
|
|
467
|
+
* check failure about markup.
|
|
468
|
+
* @returns {string[]}
|
|
469
|
+
*/
|
|
470
|
+
function configuredAllowComponents(projectRoot) {
|
|
471
|
+
const out = [];
|
|
472
|
+
const readList = (path, pick) => {
|
|
473
|
+
try {
|
|
474
|
+
if (!existsSync(path)) return;
|
|
475
|
+
const value = pick(JSON.parse(readFileSync(path, 'utf-8')));
|
|
476
|
+
if (Array.isArray(value))
|
|
477
|
+
for (const name of value) if (typeof name === 'string') out.push(name);
|
|
478
|
+
} catch {
|
|
479
|
+
/* malformed config — ignore, never fail the check on it */
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
readList(
|
|
483
|
+
join(projectRoot, 'bitboss-ui.check.json'),
|
|
484
|
+
(cfg) => cfg?.allowComponents
|
|
485
|
+
);
|
|
486
|
+
readList(
|
|
487
|
+
join(projectRoot, 'package.json'),
|
|
488
|
+
(pkg) => pkg?.['bitboss-ui']?.allowComponents
|
|
489
|
+
);
|
|
490
|
+
return out;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* CHK-SINCE-V2 — the two unknown-prop message shapes mean OPPOSITE things and
|
|
495
|
+
* nothing in the output said so. One migrating fleet filed two non-findings
|
|
496
|
+
* against the library, and the other told the categories apart only by
|
|
497
|
+
* installing v2 alongside and diffing by hand. Printed once per failing run,
|
|
498
|
+
* and only for the kinds actually present.
|
|
499
|
+
*
|
|
500
|
+
* `check` is honest about its limits here: it validates against the INSTALLED
|
|
501
|
+
* v3 manifest and holds no record of v2, so "is not in X's API" is a statement
|
|
502
|
+
* about v3 alone. Deciding whether such a prop ever worked is the reader's job,
|
|
503
|
+
* and the greps in the migration guide are how it is done.
|
|
504
|
+
*/
|
|
505
|
+
function printFindingLegend(findings) {
|
|
506
|
+
const kinds = new Set(findings.map((f) => f.kind));
|
|
507
|
+
const rows = [];
|
|
508
|
+
if (kinds.has('removed-prop'))
|
|
509
|
+
rows.push(
|
|
510
|
+
' "was removed from X" a v2→v3 break. X really had this prop; fix the call site.'
|
|
511
|
+
);
|
|
512
|
+
if (kinds.has('unknown-prop'))
|
|
513
|
+
rows.push(
|
|
514
|
+
' "is not in X\'s API" X does not declare this prop in v3 — and `check` cannot\n' +
|
|
515
|
+
' say whether it ever did. It may be a v3 removal that\n' +
|
|
516
|
+
' predates the removal register, or a name that was never\n' +
|
|
517
|
+
' a prop at all and has always fallen through to the DOM\n' +
|
|
518
|
+
' as an inert attribute. Check the v2 source before\n' +
|
|
519
|
+
' "restoring" anything: the second case needs deleting,\n' +
|
|
520
|
+
' not porting.'
|
|
521
|
+
);
|
|
522
|
+
if (rows.length === 0) return;
|
|
523
|
+
console.error('\nReading these findings:');
|
|
524
|
+
for (const row of rows) console.error(row);
|
|
525
|
+
console.error(
|
|
526
|
+
' `check` reads the installed v3 manifest only; the package carries no v2 API.'
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
448
530
|
/**
|
|
449
531
|
* `npx bitboss-ui check [glob…] [--json]` — validate `Bb*` markup (props,
|
|
450
532
|
* v-models, and `<template #slot>` names) in `.vue` and `.md` files against
|
|
451
533
|
* the installed components.json manifest.
|
|
452
534
|
*/
|
|
453
|
-
function checkCommand(globs, jsonMode, allowEmpty
|
|
535
|
+
function checkCommand(globs, { json: jsonMode, allowEmpty, allowComponents }) {
|
|
454
536
|
const projectRoot = process.cwd();
|
|
455
537
|
|
|
456
538
|
const manifestPath = join(PACKAGE_ROOT, 'dist', 'ai', 'components.json');
|
|
@@ -481,6 +563,15 @@ function checkCommand(globs, jsonMode, allowEmpty = false) {
|
|
|
481
563
|
: new Set();
|
|
482
564
|
const files = resolveCheckFiles(projectRoot, globs);
|
|
483
565
|
|
|
566
|
+
// CHK-ALLOWCOMPONENTS: `Bb` is a naming convention, not proof of ownership.
|
|
567
|
+
// An app component called `BbRichEditor` is not invalid library markup, and
|
|
568
|
+
// before this the only way to silence it was to rename the component.
|
|
569
|
+
const allowedComponents = [
|
|
570
|
+
...configuredAllowComponents(projectRoot),
|
|
571
|
+
...allowComponents,
|
|
572
|
+
];
|
|
573
|
+
const validateOptions = { allowComponents: allowedComponents };
|
|
574
|
+
|
|
484
575
|
const findings = [];
|
|
485
576
|
/*
|
|
486
577
|
* Advisory only — see scripts/lib/hand-roll-hints.mjs. Hints never touch the
|
|
@@ -493,8 +584,8 @@ function checkCommand(globs, jsonMode, allowEmpty = false) {
|
|
|
493
584
|
const relPath = relative(projectRoot, file);
|
|
494
585
|
const content = readFileSync(file, 'utf-8');
|
|
495
586
|
const { findings: fileFindings } = file.endsWith('.md')
|
|
496
|
-
? validateMarkdown(content, manifest)
|
|
497
|
-
: validateVueSnippet(content, manifest);
|
|
587
|
+
? validateMarkdown(content, manifest, validateOptions)
|
|
588
|
+
: validateVueSnippet(content, manifest, validateOptions);
|
|
498
589
|
for (const finding of fileFindings) {
|
|
499
590
|
findings.push({ file: relPath, ...finding });
|
|
500
591
|
}
|
|
@@ -573,14 +664,19 @@ function checkCommand(globs, jsonMode, allowEmpty = false) {
|
|
|
573
664
|
finding.fenceInfo != null
|
|
574
665
|
? `fence#${finding.fenceIndex}${finding.fenceInfo ? ` (${finding.fenceInfo})` : ''} `
|
|
575
666
|
: '';
|
|
667
|
+
// CHK-LINE: three identical `full-screen` findings under one filename
|
|
668
|
+
// were indistinguishable, so every automated fix had to re-parse the
|
|
669
|
+
// file to place them. The line comes straight off the template AST.
|
|
670
|
+
const at = finding.line != null ? `line ${finding.line}: ` : '';
|
|
576
671
|
console.error(
|
|
577
|
-
` ✗ ${where ? `[${where.trim()}] ` : ''}${finding.message}`
|
|
672
|
+
` ✗ ${at}${where ? `[${where.trim()}] ` : ''}${finding.message}`
|
|
578
673
|
);
|
|
579
674
|
if (finding.hint) console.error(` hint: ${finding.hint}`);
|
|
580
675
|
}
|
|
581
676
|
console.error(
|
|
582
677
|
'\nFix the markup (or the component API) so it stays copy-safe for agents.'
|
|
583
678
|
);
|
|
679
|
+
printFindingLegend(findings);
|
|
584
680
|
printHints();
|
|
585
681
|
process.exit(1);
|
|
586
682
|
}
|
|
@@ -600,7 +696,8 @@ Commands:
|
|
|
600
696
|
server for Claude Code (.mcp.json), Cursor (.cursor/mcp.json),
|
|
601
697
|
VS Code/Copilot (.vscode/mcp.json), and Windsurf (~/.codeium
|
|
602
698
|
global) — merges, never overwrites other servers.
|
|
603
|
-
check [glob…] [--json] [--allow-empty]
|
|
699
|
+
check [glob…] [--json] [--allow-empty] [--allow-component <Name>]
|
|
700
|
+
[--no-hints]
|
|
604
701
|
Validate \`Bb*\` markup in .vue/.md files against the
|
|
605
702
|
installed components.json manifest (unknown/removed props,
|
|
606
703
|
bad v-models, unknown \`<template #slot>\` names,
|
|
@@ -612,7 +709,27 @@ Commands:
|
|
|
612
709
|
Exits 1 on findings, and on an explicit glob that matched
|
|
613
710
|
nothing (a typo'd CI path validates zero files silently) —
|
|
614
711
|
pass \`--allow-empty\` when an empty match is expected.
|
|
615
|
-
|
|
712
|
+
|
|
713
|
+
\`--allow-component <Name>\` (repeatable) exempts a
|
|
714
|
+
component your APP owns whose name starts with \`Bb\` —
|
|
715
|
+
the prefix is a convention, not proof of provenance.
|
|
716
|
+
Persist the list instead of repeating the flag:
|
|
717
|
+
package.json { "bitboss-ui": { "allowComponents": ["BbRichEditor"] } }
|
|
718
|
+
or \`bitboss-ui.check.json\` \`{ "allowComponents": [...] }\`.
|
|
719
|
+
Same option name as the ESLint rules' \`allowComponents\`,
|
|
720
|
+
so both gates configure alike.
|
|
721
|
+
|
|
722
|
+
\`--json\` prints \`{ findings: [...], hints: [...], files }\`.
|
|
723
|
+
Each finding: \`{ file, line, component, attr, kind,
|
|
724
|
+
message, hint?, to? }\` (plus \`fenceIndex\`/\`fenceInfo\`
|
|
725
|
+
inside a markdown fence; \`line\` is absolute in the file).
|
|
726
|
+
\`kind\` is one of: \`removed-prop\` (a v2→v3 break — fix
|
|
727
|
+
it), \`unknown-prop\` (not a v3 prop of that component;
|
|
728
|
+
may never have been one — see the legend the text output
|
|
729
|
+
prints), \`unknown-model\`, \`unknown-slot\`,
|
|
730
|
+
\`no-default-slot\`, \`inert-nav-attr\`,
|
|
731
|
+
\`missing-partner-prop\`, \`partner-prop\`,
|
|
732
|
+
\`validated-only-prop\`.
|
|
616
733
|
mcp Start a stdio MCP server exposing the dist/ai knowledge
|
|
617
734
|
base (search_components, get_component, list_recipes,
|
|
618
735
|
get_recipe, validate) to MCP-capable agent harnesses.
|
|
@@ -658,14 +775,18 @@ switch (command) {
|
|
|
658
775
|
{
|
|
659
776
|
json: { type: 'boolean' },
|
|
660
777
|
'allow-empty': { type: 'boolean' },
|
|
778
|
+
'allow-component': { type: 'string', multiple: true },
|
|
779
|
+
// Accepted and ignored here — read straight off argv inside
|
|
780
|
+
// checkCommand, but parseArgs is strict, so it must be declared.
|
|
781
|
+
'no-hints': { type: 'boolean' },
|
|
661
782
|
},
|
|
662
783
|
{ allowPositionals: true }
|
|
663
784
|
);
|
|
664
|
-
checkCommand(
|
|
665
|
-
|
|
666
|
-
values
|
|
667
|
-
values['allow-
|
|
668
|
-
);
|
|
785
|
+
checkCommand(positionals, {
|
|
786
|
+
json: values.json ?? false,
|
|
787
|
+
allowEmpty: values['allow-empty'] ?? false,
|
|
788
|
+
allowComponents: values['allow-component'] ?? [],
|
|
789
|
+
});
|
|
669
790
|
break;
|
|
670
791
|
}
|
|
671
792
|
case 'mcp': {
|
package/dist/ai/changelog.json
CHANGED
package/dist/ai/components.json
CHANGED
|
@@ -15,9 +15,9 @@ Package export entry: `bitboss-ui/ai` → `dist/ai/index.md`.
|
|
|
15
15
|
Subpaths: `bitboss-ui/ai/components.json`, `bitboss-ui/ai/guides/…`, etc.
|
|
16
16
|
|
|
17
17
|
**Not installed, or can only fetch URLs?** The same files are served from the
|
|
18
|
-
published package at `https://cdn.jsdelivr.net/npm/bitboss-ui@
|
|
18
|
+
published package at `https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/ai/…`.
|
|
19
19
|
If you can make only one request, fetch
|
|
20
|
-
[`…@
|
|
20
|
+
[`…@beta/dist/llms-medium.txt`](https://cdn.jsdelivr.net/npm/bitboss-ui@beta/dist/llms-medium.txt)
|
|
21
21
|
— this router plus the agent contract, setup, component picker, design language
|
|
22
22
|
and the full component catalogue, in ~110 KB.
|
|
23
23
|
|
|
@@ -424,12 +424,40 @@ scope (`.brand { --bb-primary: … }`) does not re-derive them — re-apply the
|
|
|
424
424
|
`light`/`dark` class on that scope, or override the derived tokens directly.
|
|
425
425
|
Root-level theming (what the theme builder emits) is unaffected.
|
|
426
426
|
|
|
427
|
-
###
|
|
427
|
+
### Registered knobs (`@property`)
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
429
|
+
Nine tokens are registered in `variables.css`: the eight color knobs with
|
|
430
|
+
`syntax: '<color>'`, and `--bb-ring-opacity` with `syntax: '<percentage>'`.
|
|
431
|
+
All are `inherits: true`.
|
|
432
|
+
|
|
433
|
+
**Registration types them, and a typed property rejects.** A declaration that
|
|
434
|
+
does not parse as the declared syntax is invalid at computed-value time — the
|
|
435
|
+
value is discarded, and because these inherit, an override at `:root` (no
|
|
436
|
+
parent to inherit from) lands on the registered `initial-value`. That is the
|
|
437
|
+
fail-soft half: an unparseable `--bb-panel` falls back instead of detonating
|
|
438
|
+
every derivation that mixes it. It is also the trap. Write
|
|
439
|
+
|
|
440
|
+
```css
|
|
441
|
+
:root {
|
|
442
|
+
--bb-ring-opacity: 0.15;
|
|
443
|
+
} /* wrong: not a <percentage> */
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
and nothing errors — not the build, not the console. The computed value is
|
|
447
|
+
just `25%`, and every ring paints 1.67× stronger than the `15%` you asked for
|
|
448
|
+
(opacity is the ring's strength; its thickness is `--bb-ring-size`).
|
|
449
|
+
Percentages need the `%`; colors must be a `<color>` (`transparent` and
|
|
450
|
+
`currentColor` qualify, `none` and a mistyped hex do not). Nothing in the
|
|
451
|
+
toolchain catches it either: `bitboss-ui check` reads `.vue`/`.md`, never
|
|
452
|
+
stylesheets.
|
|
453
|
+
|
|
454
|
+
Fail-soft is only soft when the initial value is close to the real one. Every
|
|
455
|
+
color knob is registered at its light default, so a rejected color override
|
|
456
|
+
renders the library's own light theme. `--bb-ring-opacity` is registered at
|
|
457
|
+
`25%`, which matches neither shipped scope (light `15%`, dark `40%`), so a
|
|
458
|
+
rejected value there is the one that shows.
|
|
459
|
+
|
|
460
|
+
Typed knobs are also **animatable**. Add `.theme-animated`
|
|
433
461
|
(main.css) to the element that carries the scheme class or theme overrides:
|
|
434
462
|
|
|
435
463
|
```html
|
|
@@ -570,7 +598,7 @@ escape the host dialog's `overflow: hidden` and centering transform.
|
|
|
570
598
|
| `color-mix(in srgb, …)` | `color-mix(in oklab, …)` everywhere |
|
|
571
599
|
| dialog/offcanvas `--radius-multiplier` ×1.8, literal `6px`/`8px`/`0.375rem` radii | `--bb-radius-surface` / `--bb-radius-sm` |
|
|
572
600
|
| `--bb-contrasting` | `--bb-primary-fg` |
|
|
573
|
-
|
|
|
601
|
+
| hard-coded `cubic-bezier()` literals (v2 exposed no easing token) | `--bb-ease` |
|
|
574
602
|
| `--bb-panel-disabled`, `--bb-input-bg-secondary` | `--bb-muted` |
|
|
575
603
|
| `--bb-muted-color`, `--bb-hint`, `--bb-placeholder`, `--bb-icon-color`, `--bb-prefix-color` | `--bb-text-muted` |
|
|
576
604
|
| `--bb-input-color`, `--bb-input-bg` | `--bb-text`, `--bb-panel` |
|
|
@@ -587,6 +615,18 @@ escape the host dialog's `overflow: hidden` and centering transform.
|
|
|
587
615
|
| `--bb-table-{id}-track-{key}` (parent→nested width bridge) | `--table-{id}-track-{key}` (distinctive cross-component name, rule 3) |
|
|
588
616
|
| `--bb-select-option-*`, `--bb-primary-base`, all `--x-light`/`--x-dark` shadows | deleted (dead) |
|
|
589
617
|
|
|
618
|
+
**This table maps names, not values.** A token that is absent from it survived
|
|
619
|
+
the rename — which is not the same as surviving unchanged. Six kept their v2
|
|
620
|
+
name and their global scope and moved default anyway (input height, input
|
|
621
|
+
vertical padding, prefix column width, label weight, ring size, leading); the
|
|
622
|
+
before/after numbers are tabled in the v2→v3 migration guide, §3. The rule
|
|
623
|
+
generalizes past this migration: a `--bb-*` name is a slot in the derivation
|
|
624
|
+
graph, not a promise about the number in it. An override you carried forward
|
|
625
|
+
keeps working and keeps your old look; an override you deleted because "the
|
|
626
|
+
name still exists" hands you whatever `variables.css` declares today. Check
|
|
627
|
+
each survivor against `variables.css` and delete only the ones that were
|
|
628
|
+
restating a default.
|
|
629
|
+
|
|
590
630
|
## Enforcement
|
|
591
631
|
|
|
592
632
|
`npm run check:tokens` (see `scripts/check-tokens.mjs`) fails the build when:
|
|
@@ -13,16 +13,21 @@ Source of truth for integrating `bitboss-ui`. Two pieces are always required:
|
|
|
13
13
|
## 1) Install
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
# v3 is a prerelease —
|
|
16
|
+
# v3 is a prerelease — a dist-tag is REQUIRED.
|
|
17
17
|
# A bare `npm install bitboss-ui` still resolves to v2, which has no build
|
|
18
18
|
# plugin and no `dist/ai` knowledge base, so everything below silently fails.
|
|
19
|
-
npm install bitboss-ui@
|
|
19
|
+
npm install bitboss-ui@beta
|
|
20
20
|
# recommended default icon set (any @iconify-json/* works)
|
|
21
21
|
npm install -D @iconify-json/lucide
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Confirm you got v3 before continuing — `npm ls bitboss-ui` must report a
|
|
25
|
-
`3.0.0
|
|
25
|
+
`3.0.0-*` version. If it reports `2.x`, nothing in this guide applies.
|
|
26
|
+
|
|
27
|
+
**`@beta` is the current v3 channel.** `@alpha` still resolves, but it is
|
|
28
|
+
frozen at the last alpha and does not receive fixes — it is not simply an older
|
|
29
|
+
copy of the same line. `@latest` is the **v2** line and always will be until v3
|
|
30
|
+
goes stable, so never reach for it here.
|
|
26
31
|
|
|
27
32
|
Peer dependency: `vue ^3.5.12` — the only required one. `@inertiajs/vue3` is an **optional** peer, needed only in Inertia apps.
|
|
28
33
|
|
|
@@ -219,6 +224,48 @@ import 'bitboss-ui/styles.css';
|
|
|
219
224
|
import 'bitboss-ui/reset.css';
|
|
220
225
|
```
|
|
221
226
|
|
|
227
|
+
**Exception — an app that needs its own CSS to load _before_ the library.**
|
|
228
|
+
Auto-injection is library-first and not configurable: the runtime plugin
|
|
229
|
+
inserts its `<style>` **before the first** `style`/`link[rel="stylesheet"]`
|
|
230
|
+
already in `<head>` (`src/utils/injectLibraryStyles.ts`), so the guaranteed
|
|
231
|
+
order above is exactly what you get and every app stylesheet — your Tailwind
|
|
232
|
+
entry included — lands after the library. There is no option to inject later.
|
|
233
|
+
|
|
234
|
+
That is the order most apps want. It is the wrong order if you need library
|
|
235
|
+
rules to beat Tailwind's Preflight **by source order**, which is the case on
|
|
236
|
+
**Tailwind v3**: `@tailwind base` compiles to ordinary unlayered CSS, so under
|
|
237
|
+
auto-injection Preflight sits after the library sheet and its element resets
|
|
238
|
+
(`button`, `input`, `h1`, …) win every equal-specificity tie. `injectStyles:
|
|
239
|
+
false` + `resetCss: false` (the default) disables injection entirely and hands
|
|
240
|
+
you the order:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
// vite.config.ts / the `bitboss` key in nuxt.config.ts
|
|
244
|
+
bitbossUi({ iconDir: '…', injectStyles: false, resetCss: false });
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
// app entry — order is yours now
|
|
249
|
+
import './app.css'; // your Tailwind entry
|
|
250
|
+
import 'bitboss-ui/styles.css'; // after Tailwind, so library rules win the ties
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Leave `resetCss` at `false` here — Preflight already reset everything.
|
|
254
|
+
Nothing else about the plugin changes; `injectStyles` controls only whether the
|
|
255
|
+
library injects its own `<style>`.
|
|
256
|
+
|
|
257
|
+
**Tailwind v4 apps do not need this** — source order does not decide their ties
|
|
258
|
+
at all. `@import 'tailwindcss'` puts Preflight in a real `@layer base` and
|
|
259
|
+
utilities in `@layer utilities`, and layered rules lose to unlayered rules of
|
|
260
|
+
equal specificity **regardless of source order**. `dist/styles.css` declares no
|
|
261
|
+
`@layer`, so the library wins those ties wherever it is injected: leave
|
|
262
|
+
auto-injection on. The same mechanic bites the other way, and silently — a
|
|
263
|
+
utility cannot re-size a Bb component either. `<BbButton class="h-12 px-6">`
|
|
264
|
+
loses to the library's own `.bb-button { height: var(--h); padding-left:
|
|
265
|
+
var(--px); padding-right: var(--px) }` (equal specificity, unlayered), with no
|
|
266
|
+
warning from anything. Size Bb components through their props and tokens, never
|
|
267
|
+
utilities. (Same trap as the scoped-`<style>` case above, one level up.)
|
|
268
|
+
|
|
222
269
|
## 6) Icons
|
|
223
270
|
|
|
224
271
|
- `iconDir` (required) is scanned recursively for `.svg` files; each file becomes `local:<basename>`.
|
|
@@ -260,6 +307,15 @@ When `true`, the **dev server** adds `./node_modules/.bitboss-ui/local-icons.jso
|
|
|
260
307
|
|
|
261
308
|
When `true`, the **dev server** registers the bitboss-ui MCP server in each project-scoped agent-harness config on boot — `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), and `.vscode/mcp.json` (VS Code / Copilot) — so agents can query the knowledge base with no manual setup. The write is merge-safe (leaves other servers alone) and idempotent (only logs when it actually changes a file). Windsurf is intentionally **not** written here: its config is global (`~/.codeium`), and silently editing a home-dir file that affects every project on the machine from a dev-server boot would be surprising — run `npx bitboss-ui ai-init --mcp` to register Windsurf too. This flag is the zero-command equivalent of that command's project-scoped writes. The MCP server's own runtime — `@modelcontextprotocol/sdk` and `zod` — ships as **optional peer dependencies** (~12 MB, more than every runtime dependency of the library combined, and loaded by nothing else), so install them with `npm i -D @modelcontextprotocol/sdk zod` before the harness first launches the server; both this flag and `ai-init --mcp` warn when they are missing.
|
|
262
309
|
|
|
310
|
+
**MCP is a convenience, not a requirement, and skipping it costs no
|
|
311
|
+
knowledge.** The server is a reader over files the install already put on disk
|
|
312
|
+
— every tool `readFileSync`s out of `node_modules/bitboss-ui/dist/ai/` (plus
|
|
313
|
+
`dist/styles.css` for the token lookup). An agent that opens that directory
|
|
314
|
+
itself has the identical knowledge base, `npx bitboss-ui check` runs without the
|
|
315
|
+
peers, and `ai-init` (no `--mcp`) still writes the harness pointers. If you are
|
|
316
|
+
migrating under a "no new dependencies" constraint, leave `mcp` at `false`,
|
|
317
|
+
skip `--mcp`, and point your agent at `dist/ai/guides/ai-router.md`.
|
|
318
|
+
|
|
263
319
|
### `inertiaLinkName?: string` (default: `'Link'`)
|
|
264
320
|
|
|
265
321
|
Global component name used for Inertia links. **Optional** — when nothing is
|
|
@@ -438,7 +494,17 @@ than inventing hues.
|
|
|
438
494
|
|
|
439
495
|
The package ships its agent docs in `node_modules/bitboss-ui/dist/ai/`. **Start at** `guides/ai-router.md`, then the catalogue `index.md`. Run `npx bitboss-ui ai-init` in the consumer project to inject harness-agnostic pointers (AGENTS.md, Cursor rules, Copilot instructions, Claude skill, Windsurf rules) so coding agents discover them without a vendor lock-in. After generating Bb\* markup, run `npx bitboss-ui check` (supports `--json`) so unknown props fail before review.
|
|
440
496
|
|
|
441
|
-
|
|
497
|
+
**Three tiers ship under `dist/ai/`**, and the third one is easy to miss:
|
|
498
|
+
`guides/` + `recipes/` are the authored prose; `<Name>.md` at the root is the
|
|
499
|
+
generated typed contract for each component (props, slots, events, examples);
|
|
500
|
+
`source/<Name>.md` is that same component's **complete implementation** —
|
|
501
|
+
`.vue` template, `types.ts` and `index.css` in one file, one per component
|
|
502
|
+
including the internal ones the package does not export. Read the source tier
|
|
503
|
+
when the contract cannot answer the question — the usual case is rebuilding
|
|
504
|
+
chrome around a control the library does not ship, since the class names such a
|
|
505
|
+
template emits are already styled by the public `bitboss-ui/styles.css`.
|
|
506
|
+
|
|
507
|
+
Add `--mcp` to also register a live MCP server (`npx bitboss-ui@<installed version> mcp`, pinned so npx can never fetch a different version from the registry) the harness launches on demand — it merges the entry (never overwriting other servers) into `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (VS Code / Copilot), and `~/.codeium/windsurf/mcp_config.json` (Windsurf). **Windsurf's config is global** — it has no per-project scope, so registering it affects every project on the machine; that is why only the explicit `ai-init --mcp` command writes it, while the plugin's `mcp: true` dev-server flag (§7) auto-registers the project-scoped harnesses only. Install the server's optional peers first — `npm i -D @modelcontextprotocol/sdk zod` — they are not dependencies of the library (~12 MB used only by the MCP server); the command warns if they are absent. **`--mcp` is optional and skipping it loses you nothing but the transport** — the server only reads the `dist/ai/` files listed above, so run plain `ai-init` and let the agent read that directory if you cannot add the peers (see `mcp?: boolean` in §7).
|
|
442
508
|
|
|
443
509
|
For a human-in-the-loop (and agent) safety net, add the `bitboss-ui/eslint-plugin` flat-config plugin (`...bitbossUi.configs.recommended`) so unknown/removed `Bb*` props surface as ESLint errors in the editor and CI — the same manifest checks as `bitboss-ui check`, reusing your `eslint-plugin-vue` parser. Real HTML attributes fall through untouched; escape hatches are the standard `<!-- eslint-disable-next-line bitboss-ui/no-unknown-attributes -->` and the rule's `allowAttributes` / `allowComponents` options. **`eslint --fix` also auto-migrates deprecated v2 props** (renames, boolean-polarity inversions like `allowWriting`→`disableWriting`, value remaps like BbToast `placement`→`position`, and deletion of inert removed props); structural migrations that need a slot/directive/CSS are reported but left by hand. **This library is TS-first (recipes ship `<script setup lang="ts">`, some with `generic="T"`), so wire `@typescript-eslint/parser` for both `.ts` and `.vue` files** — without it `vue-eslint-parser` throws a `Parsing error` on those files, which means no rule (including `no-unknown-attributes`) runs on them at all. See the README "ESLint plugin" section for the full config snippet (both parser blocks + the `npm i -D @typescript-eslint/parser` line) and the fix table. `recommended` also enables `bitboss-ui/no-active-class-on-root-link`: `active-class` on a link-capable `Bb*` component (`BbButton`/`BbBaseButton`/`BbBadgeButton`) matches by path PREFIX, so a link whose target is the literal root (`href="/"` / `to="/"`) reads as "active" on every URL — the rule flags that combination and points you at `exact-active-class` instead.
|
|
444
510
|
|
|
@@ -458,7 +524,7 @@ It also enables `bitboss-ui/require-partner-prop`: an opt-in prop set without it
|
|
|
458
524
|
- [ ] build plugin configured: `bitboss-ui/vite` plugin or `bitboss-ui/nuxt` module (`bitboss` config key);
|
|
459
525
|
- [ ] `iconDir` points at an existing SVG folder;
|
|
460
526
|
- [ ] runtime plugin installed: `app.use(bitbossUiPlugin)` (automatic in Nuxt);
|
|
461
|
-
- [ ] style strategy chosen: auto injection (default) or `injectStyles: false` + `import 'bitboss-ui/styles.css'
|
|
527
|
+
- [ ] style strategy chosen: auto injection (default) or `injectStyles: false` + `import 'bitboss-ui/styles.css'` — auto injection is always library-first, so a Tailwind **v3** app that needs Preflight to load before the library must take the second option (§5);
|
|
462
528
|
- [ ] if a reset is required: `resetCss: true` or `import 'bitboss-ui/reset.css'`;
|
|
463
529
|
- [ ] Inertia apps: `@inertiajs/vue3` installed (no link registration needed; set `inertiaLinkName` only if you register your own link wrapper).
|
|
464
530
|
- [ ] building the [Layout Scaffold](../recipes/vue/layout-scaffold.md)? `npm i @vueuse/core` — `usePageShellContext.ts` imports `injectLocal`/`provideLocal` from it. This library depends on it too, so it can resolve via hoisting without being declared — don't rely on that; declare it in your own `package.json` or a strict installer (pnpm, Yarn PnP) will fail to resolve it.
|
|
@@ -35,3 +35,40 @@ prop now actually carries `text` (v2 mistakenly passed `title`).
|
|
|
35
35
|
A hand-applied `class="bb-alert--warning"` also becomes `variant="warning"` —
|
|
36
36
|
see [main guide §7](../v2-to-v3.md). Custom `theme` names: register via the
|
|
37
37
|
plugin's `alertVariants` option and keep your CSS.
|
|
38
|
+
|
|
39
|
+
## ⚠ `theme` painted nothing in v2 — and now two identical call sites diverge
|
|
40
|
+
|
|
41
|
+
v2's stylesheet shipped **zero** `.bb-alert--*` rules. The component emitted
|
|
42
|
+
`bb-alert--${theme}` faithfully, but unless your own CSS defined the rule, every
|
|
43
|
+
value rendered the same default surface. `theme="success"`, `theme="blue"` and
|
|
44
|
+
`theme="warning"` were the same alert.
|
|
45
|
+
|
|
46
|
+
In v3 the string is live, and what happens next depends on nothing but whether
|
|
47
|
+
the name collides with a built-in (`outline`, `primary`, `destructive`,
|
|
48
|
+
`warning`):
|
|
49
|
+
|
|
50
|
+
```diff
|
|
51
|
+
<!-- these two rendered identically in v2 -->
|
|
52
|
+
- <BbAlert theme="success" title="Saved" />
|
|
53
|
+
+ <BbAlert variant="success" title="Saved" />
|
|
54
|
+
<!-- `success` is not an alert built-in → compile error until you register it. You will notice. -->
|
|
55
|
+
|
|
56
|
+
- <BbAlert theme="warning" title="Quota almost reached" />
|
|
57
|
+
+ <BbAlert variant="warning" title="Quota almost reached" />
|
|
58
|
+
<!-- `warning` IS a built-in → compiles, and silently repaints amber (#fffbea on #fee685). You will not. -->
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The rename diff above is a **visual change** for `warning`, not a no-op. Before
|
|
62
|
+
converting any `theme=` value, grep your own stylesheets for
|
|
63
|
+
`.bb-alert--<value>`:
|
|
64
|
+
|
|
65
|
+
- **Nothing styled it** → the value was decoration. Drop the prop and take the
|
|
66
|
+
v3 default, or register the name via `alertVariants` and ship no CSS for it,
|
|
67
|
+
which holds v2's appearance exactly.
|
|
68
|
+
- **Your CSS styled it** → register the name and keep your rule; `warning` is
|
|
69
|
+
the one case where your rule now lands on top of a library skin, so check
|
|
70
|
+
that you override every property it sets (background, border, `--main-color`,
|
|
71
|
+
`--muted-color`) and not just the ones your v2 rule needed.
|
|
72
|
+
|
|
73
|
+
The same trap applies to `BbTooltip` and `toast()` — v2 styled none of their
|
|
74
|
+
theme classes either.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: 'Migration v2→v3: BbAvatar'
|
|
3
|
-
summary: The color prop is removed — the
|
|
3
|
+
summary: The color prop is removed — retheme the fallback through the --bg-color / --text-color locals.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# BbAvatar — v2 → v3
|
|
7
7
|
|
|
8
8
|
## Changes
|
|
9
9
|
|
|
10
|
-
| v2 | v3 | Kind
|
|
11
|
-
| ---------------- | --------------------------------------------------------------------------------------------- |
|
|
12
|
-
| `color?: string` | removed |
|
|
13
|
-
| `timeout` | unchanged (SSR image grace period — **not** renamed; only `BbTooltip.timeout` became `delay`) | —
|
|
10
|
+
| v2 | v3 | Kind |
|
|
11
|
+
| ---------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `color?: string` | removed | the fallback surface paints from two locals on `.bb-avatar` — `--bg-color` / `--text-color` (below) |
|
|
13
|
+
| `timeout` | unchanged (SSR image grace period — **not** renamed; only `BbTooltip.timeout` became `delay`) | — |
|
|
14
14
|
|
|
15
15
|
## Edits
|
|
16
16
|
|
|
@@ -19,6 +19,45 @@ summary: The color prop is removed — the initials fallback is always primary.
|
|
|
19
19
|
+ <BbAvatar :src="user.photo" :alt="user.name" />
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
## Where the color went
|
|
23
|
+
|
|
24
|
+
`.bb-avatar` declares three locals and reads them for the fallback (initials,
|
|
25
|
+
slotted content, or the built-in person glyph — an image covers all of them):
|
|
26
|
+
|
|
27
|
+
| local | default | paints |
|
|
28
|
+
| ---------------------- | ------------------------------------------ | --------------------------- |
|
|
29
|
+
| `--bg-color` | `var(--bb-primary)` | the avatar background |
|
|
30
|
+
| `--text-color` | `var(--bb-primary-fg)` | initials / slotted fallback |
|
|
31
|
+
| `--default-icon-color` | 80% mix of `--bb-primary-fg` over the fill | the built-in person glyph |
|
|
32
|
+
|
|
33
|
+
Set them on a scope, not on the component:
|
|
34
|
+
|
|
35
|
+
```css
|
|
36
|
+
.team-avatar .bb-avatar {
|
|
37
|
+
--bg-color: #7c3aed;
|
|
38
|
+
--text-color: #fff;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
⚠ Set **both**. `--bg-color` alone leaves the initials at `--bb-primary-fg`,
|
|
43
|
+
which is the contrast pair for the _primary_ color, not for yours — the symptom
|
|
44
|
+
is white-on-pale or black-on-dark initials.
|
|
45
|
+
|
|
46
|
+
Use this for a color that genuinely varies per instance (per-team, per-tenant).
|
|
47
|
+
If the v2 `color` was your brand color repeated at every call site, retheme
|
|
48
|
+
`--bb-primary` / `--bb-primary-fg` once instead and write no per-avatar rules.
|
|
49
|
+
That is the order the BbAvatar usage guide prescribes: retheme the tokens
|
|
50
|
+
first, reach for the locals only when the value is per-instance.
|
|
51
|
+
|
|
52
|
+
## `.bb-avatar--square` was never a library modifier
|
|
53
|
+
|
|
54
|
+
If your app has a `.bb-avatar--square` rule, it is **your** CSS. v2 shipped no
|
|
55
|
+
such modifier — its entire avatar stylesheet was four rules, all hardcoding
|
|
56
|
+
`border-radius: 50%`, and the string `square` appears nowhere in the v2
|
|
57
|
+
package. v3 bakes in the same circle.
|
|
58
|
+
|
|
59
|
+
So nothing was removed and there is no v3 modifier to rename to. Your rule
|
|
60
|
+
keeps working: it has the same specificity as the library's `.bb-avatar` rule
|
|
61
|
+
and the library sheet is injected above your app CSS, so yours wins the tie.
|
|
62
|
+
Rename the class to an app-owned block (`.p-avatar--square`) anyway, so the
|
|
63
|
+
next reader does not take it for library API and go hunting for a `shape` prop.
|
|
@@ -23,7 +23,7 @@ re-express it with the new badge's `variant` API.
|
|
|
23
23
|
| ------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
|
24
24
|
| `content?: string \| number \| null` | `text?: string \| number` — plus new `max?: number` (renders `${max}+`) |
|
|
25
25
|
| `color?: string` (free-form) | `variant?: IndicatorVariantType` (`default, success, info, warning, destructive`; default `default`) |
|
|
26
|
-
| `dot`, `left`, `bottom` | unchanged
|
|
26
|
+
| `dot`, `left`, `bottom` | unchanged — including precedence: `dot` renders a contentless bubble and **suppresses** the label |
|
|
27
27
|
| `floating` | removed — the indicator always positions against its slotted anchor |
|
|
28
28
|
| `#content` slot | dropped — use `text` |
|
|
29
29
|
|
|
@@ -38,6 +38,28 @@ re-express it with the new badge's `variant` API.
|
|
|
38
38
|
If you styled `.bb-badge` in CSS for this use, the block is now
|
|
39
39
|
`.bb-indicator` (`--dot`, `--left`, `--bottom`, `--<variant>` modifiers).
|
|
40
40
|
|
|
41
|
+
### `dot` beats the label — in both versions
|
|
42
|
+
|
|
43
|
+
`dot` and the label are not additive and never were. v2 rendered the content
|
|
44
|
+
span only when `dot` was false; v3 does the same with `text`. Passing both has
|
|
45
|
+
always produced a bare dot.
|
|
46
|
+
|
|
47
|
+
The trap is on the way out, not the way in. A v2 call site that toggled the dot
|
|
48
|
+
off a count —
|
|
49
|
+
|
|
50
|
+
```diff
|
|
51
|
+
- <BbBadge :content="unread" :dot="unread !== null" />
|
|
52
|
+
+ <BbIndicator :text="unread" :dot="unread !== null" />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
— shipped a **dot**, never the number, and the port above keeps it that way.
|
|
56
|
+
Drop `dot` and port `content` to `:text="unread"` alone and the number appears
|
|
57
|
+
where it never did before. Carry `dot` across unchanged unless you mean to
|
|
58
|
+
change the design.
|
|
59
|
+
|
|
60
|
+
Nothing renders at all when `dot` is false and `text` is `null`/`undefined`, so
|
|
61
|
+
`:text="count || undefined"` is the way to hide a zero.
|
|
62
|
+
|
|
41
63
|
## v2 BbChip → v3 BbBadge
|
|
42
64
|
|
|
43
65
|
Same role (dismissible token), two functional deltas: `clearable` is now
|