create-bestax 4.0.1 → 4.1.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAEtD,eAAO,MAAM,QAAQ;;;;;;;wCAaQ,MAAM;uCAEP,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;CAM7B,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;CAOV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MAiHF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA6CvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA4CtC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAEtD,eAAO,MAAM,QAAQ;;;;;;;wCAaQ,MAAM;uCAEP,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;CAM7B,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;CAOV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MAoIF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA6CvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA4CtC,CAAC"}
package/dist/constants.js CHANGED
@@ -129,6 +129,7 @@ cases. Before writing \`style\`, translate each declaration with this table:
129
129
  and \`Tabs.Tab\` has built-in \`icon\`/\`disabled\` props — no nested \`Icon\` needed.
130
130
  - Compose existing components before writing custom CSS; theme via \`Theme\` and \`--bulma-*\`
131
131
  variables, never hardcoded colors.
132
+
132
133
  - \`Navbar.Burger\`/\`Navbar.Menu\` are controlled — wire \`active\` via state on both, and pair
133
134
  \`Navbar fixed="top"\` with the \`has-navbar-fixed-top\` class on \`<html>\` (never an inline
134
135
  padding offset).
@@ -144,6 +145,22 @@ cases. Before writing \`style\`, translate each declaration with this table:
144
145
  (\`pnpm-lock.yaml\` → pnpm, \`package-lock.json\` → npm, \`yarn.lock\` → yarn) — a mismatched
145
146
  install fails or forks the lockfile.
146
147
 
148
+ ### Three components Bulma will talk you out of
149
+
150
+ Everything else here gets found because Bulma has no equivalent; these three have a near-miss
151
+ close enough to end the search.
152
+
153
+ - **Confirmation after an action** — \`Toast\`, not \`Notification\`/\`Message\`. Mount
154
+ \`<ToastContainer position="top-right" />\` once at the root, then \`toast.success('Saved')\`.
155
+ - **A confirm or alert** — \`Dialog\`, not \`Modal\`. Mount \`<DialogContainer />\`, then
156
+ \`if (await dialog.confirm({ title, message })) …\`.
157
+ - **A control that reads as text or a link** — \`LinkButton\`
158
+ (\`variant="text" \\| "ghost" \\| "underline"\`), not \`<a href="#">\`, \`<div onClick>\` or
159
+ \`Button color="text"\`.
160
+
161
+ Mounting a container without ever calling \`toast.*\`/\`dialog.*\` does nothing. Both also work
162
+ as controlled components (\`<Toast message … onClose>\`, \`<Dialog isOpen … onConfirm>\`).
163
+
147
164
  ## AI skills
148
165
 
149
166
  \`.claude/skills/\` contains Agent Skills that teach Claude how to build with this library. They load
@@ -162,7 +179,9 @@ Read skill \`references/\` files with absolute paths — the shell's cwd is not
162
179
 
163
180
  \`.claude/launch.json\` declares this app's dev server for Claude Code's browser preview
164
181
  (\`npm run dev\` on port 5173, \`--strictPort\`) — start it from there rather than rediscovering
165
- the command.
182
+ the command. \`--strictPort\` failing because 5173 is busy means an orphaned dev server from an
183
+ earlier session owns the port — kill the listener (\`lsof -tiTCP:5173 -sTCP:LISTEN | xargs kill\`)
184
+ and relaunch; don't move the app to another port.
166
185
 
167
186
  ## Docs
168
187
 
@@ -1 +1 @@
1
- {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAwBtD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CA2BtD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
package/dist/display.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import figures from 'figures';
3
3
  import { MESSAGES } from './constants.js';
4
+ import { detectPackageManager } from './package-manager.js';
4
5
  export function displayHeader() {
5
6
  console.log();
6
7
  console.log(chalk.cyan('━'.repeat(50)));
@@ -14,9 +15,12 @@ export function displaySuccess(targetDir) {
14
15
  console.log();
15
16
  console.log(chalk.bold(MESSAGES.NEXT_STEPS));
16
17
  console.log();
18
+ // Mirror the package manager that invoked the CLI — `<pm> install` and
19
+ // `<pm> run dev` are valid for all four supported PMs.
20
+ const pm = detectPackageManager();
17
21
  console.log(chalk.cyan(` cd ${targetDir}`));
18
- console.log(chalk.cyan(' pnpm install'));
19
- console.log(chalk.cyan(' pnpm dev'));
22
+ console.log(chalk.cyan(` ${pm} install`));
23
+ console.log(chalk.cyan(` ${pm} run dev`));
20
24
  console.log();
21
25
  console.log(chalk.gray(' Your app includes a beautiful logo-centric homepage'));
22
26
  console.log(chalk.gray(' showcasing Bestax, Vite, and React branding.'));
@@ -2,7 +2,7 @@ export declare function checkDirectoryExists(targetPath: string): Promise<boolea
2
2
  export declare function isDirectoryEmpty(targetPath: string): Promise<boolean>;
3
3
  export declare function emptyDirectory(targetPath: string): Promise<void>;
4
4
  export declare function ensureDirectory(targetPath: string): Promise<void>;
5
- export declare function copyDirectory(source: string, destination: string): Promise<void>;
5
+ export declare function copyDirectory(source: string, destination: string, renames?: Record<string, string>): Promise<void>;
6
6
  export declare function readJsonFile<T = unknown>(filePath: string): Promise<T>;
7
7
  export declare function writeJsonFile(filePath: string, data: unknown, spaces?: number): Promise<void>;
8
8
  export declare function updatePackageJson(targetPath: string, projectName: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAQf"}
1
+ {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -17,11 +17,21 @@ export async function emptyDirectory(targetPath) {
17
17
  export async function ensureDirectory(targetPath) {
18
18
  await fs.ensureDir(targetPath);
19
19
  }
20
- export async function copyDirectory(source, destination) {
20
+ export async function copyDirectory(source, destination, renames) {
21
21
  if (!(await checkDirectoryExists(source))) {
22
22
  throw new Error(MESSAGES.TEMPLATE_NOT_FOUND(source));
23
23
  }
24
24
  await fs.copy(source, destination);
25
+ if (renames) {
26
+ for (const [from, to] of Object.entries(renames)) {
27
+ const copiedPath = path.join(destination, from);
28
+ if (fs.existsSync(copiedPath)) {
29
+ await fs.move(copiedPath, path.join(destination, to), {
30
+ overwrite: true,
31
+ });
32
+ }
33
+ }
34
+ }
25
35
  }
26
36
  export async function readJsonFile(filePath) {
27
37
  return fs.readJson(filePath);
@@ -0,0 +1,5 @@
1
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
2
+ /** Detect the package manager that invoked the CLI from npm_config_user_agent
3
+ * (e.g. "pnpm/9.12.0 npm/? node/v22 darwin arm64"). Falls back to npm. */
4
+ export declare function detectPackageManager(userAgent?: string | undefined): PackageManager;
5
+ //# sourceMappingURL=package-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../src/package-manager.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAI7D;2EAC2E;AAC3E,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,MAAM,GAAG,SAA6C,GAChE,cAAc,CAKhB"}
@@ -0,0 +1,9 @@
1
+ const KNOWN = ['npm', 'pnpm', 'yarn', 'bun'];
2
+ /** Detect the package manager that invoked the CLI from npm_config_user_agent
3
+ * (e.g. "pnpm/9.12.0 npm/? node/v22 darwin arm64"). Falls back to npm. */
4
+ export function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
5
+ const name = userAgent?.split('/')[0] ?? '';
6
+ return KNOWN.includes(name)
7
+ ? name
8
+ : 'npm';
9
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA2BA,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAIxB,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,CAAC,EAAE,MAAM;IAI3B,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa3D,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC;IAkBnB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAInC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBV,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC;IAqBV,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmDhB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,YAAY;IAYd,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAgJV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAyIvE"}
1
+ {"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA2BA,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAWxB,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,CAAC,EAAE,MAAM;IAI3B,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa3D,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC;IAkBnB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAInC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBV,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC;IAqBV,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmDhB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,YAAY;IAYd,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAgJV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAyIvE"}
@@ -8,6 +8,12 @@ import { displayHeader, displaySuccess, displayError, displayCancelled, } from '
8
8
  import { validateProjectName } from './validators.js';
9
9
  import { MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, } from './constants.js';
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ // npm strips literal `.gitignore` files from published tarballs, so the
12
+ // templates track `_gitignore` instead and the scaffolder renames it back
13
+ // on copy — otherwise published scaffolds would ship with no .gitignore.
14
+ const TEMPLATE_RENAMES = {
15
+ _gitignore: '.gitignore',
16
+ };
11
17
  export class ProjectCreator {
12
18
  templatesDir;
13
19
  constructor(templatesDir) {
@@ -43,7 +49,7 @@ export class ProjectCreator {
43
49
  async copyTemplate(template, targetPath) {
44
50
  const templatePath = this.getTemplatePath(template);
45
51
  await ensureDirectory(targetPath);
46
- await copyDirectory(templatePath, targetPath);
52
+ await copyDirectory(templatePath, targetPath, TEMPLATE_RENAMES);
47
53
  }
48
54
  async updateIndexHtmlTitle(targetPath, projectName) {
49
55
  const indexHtmlPath = path.join(targetPath, 'index.html');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bestax",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Create a new bestax-bulma project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,11 +50,12 @@ label — use that instead"_ or _"No `ProfileCard` exists; I'll build one compos
50
50
 
51
51
  Build from existing components before writing any CSS: `Box`, `Card`, `Title`, `SubTitle`,
52
52
  `Icon`, `Block`, `Content`, `Tag`, plus the shared Bulma helper props (spacing, color,
53
- typography, flexbox). Compound sub-parts are the exception — `Card.Content`, `Modal.Card`,
54
- `Tabs.Tab`, `Message.Body` take **no Bulma helper props**, just `className` + HTML attributes
53
+ typography, flexbox). Some compound sub-parts are the exception — `Modal.Card`, `Tabs.Tab`,
54
+ `Message.Body` take **no Bulma helper props**, just `className` + HTML attributes
55
55
  plus their own few (`Tabs.Tab` requires `index={i}` and has built-in `disabled` and
56
56
  `icon`/`iconLibrary`/`iconVariant`/`iconSize`/`iconFeatures` — don't nest an `<Icon>` there) —
57
- so put helper props on the parent or on an element inside them, never invent them there. Most "custom components" are a composition function — zero new styles.
57
+ so put helper props on the parent or on an element inside them, never invent them there.
58
+ (`Card.*` sub-parts do take helper props, like `Table.*`/`Menu.*`/`Hero.*`.) Most "custom components" are a composition function — zero new styles.
58
59
  See `examples/stat-card.tsx` for a complete worked example.
59
60
 
60
61
  ## The component spine
@@ -31,9 +31,10 @@ that can also be used on their own:
31
31
 
32
32
  Because the component destructures these into `bulmaHelperClasses`, callers get the full Bulma
33
33
  helper surface for free on every component built this way, and `rest` stays clean for DOM
34
- spreading. (Library compound sub-parts — `Card.Content`, `Modal.Card`, `Tabs.Tab`,
34
+ spreading. (Some library compound sub-parts — `Modal.Card`, `Tabs.Tab`,
35
35
  `Message.Body` — do **not** take helper props: just `className`, HTML attributes, and their own
36
- few, e.g. `Tabs.Tab`'s required `index` and its built-in `icon`/`disabled` props.)
36
+ few, e.g. `Tabs.Tab`'s required `index` and its built-in `icon`/`disabled` props. `Card.*`
37
+ sub-parts do take them.)
37
38
 
38
39
  ## `classNames(...)` and friends — `helpers/classNames.ts`
39
40
 
@@ -24,8 +24,8 @@ instead of hand-writing markup.
24
24
  - **Compound components** expose sub-parts via dot access (e.g. `Card.Header`,
25
25
  `Navbar.Item`, `Tabs.Tab`, `Hero.Body`, `Columns.Column`, `Table.Tr`); see the
26
26
  component's linked page for the full set. Sub-parts do **not** all take helper
27
- props: the `Table.*`, `Menu.*`, and `Hero.*` families do (most `Navbar.*`
28
- too), but `Card.*`, `Modal.*`, `Tabs.*`, and `Message.*` sub-parts take none —
27
+ props: the `Card.*`, `Table.*`, `Menu.*`, and `Hero.*` families do (most
28
+ `Navbar.*` too), but `Modal.*`, `Tabs.*`, and `Message.*` sub-parts take none —
29
29
  just `className`, HTML attributes, and their own few (`Tabs.Tab` requires
30
30
  `index` and has built-in `icon`/`disabled` props). Put helper props on the
31
31
  parent or on an element inside (`Span`, `Paragraph`, …) instead.
@@ -96,32 +96,47 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
96
96
  | `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
97
97
  | `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers. |
98
98
 
99
+ (`NumberInput` and `TagInput` also exist as deprecated aliases of `Numberinput`/`Taginput` —
100
+ same components; prefer the lowercase-second-word spellings.)
101
+
99
102
  ## Common props
100
103
 
101
104
  Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
102
105
 
103
- | Prop | Type | Purpose |
104
- | -------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------- |
105
- | `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state — use `'danger'` for errors, `'success'` for valid. |
106
- | `size` | `'small' \| 'medium' \| 'large'` | Input size. |
107
- | `value` / `onChange` | controlled value + handler | Standard React controlled inputs. |
108
- | `defaultValue` | uncontrolled initial value | When not controlling state. |
109
- | `disabled`, `readOnly` | `boolean` | Native states (`readOnly` on `*Base`). |
110
- | `label` | `ReactNode` | Field label (convenience components). |
111
- | `message` | `ReactNode` | Help / validation text rendered as `<p class="help">`. |
112
- | `messageColor` | a Bulma color | Colors the help text (`'danger'` for errors). |
113
- | `iconLeftName` / `iconRightName` | `string` | Icon shortcuts; pair with `hasIconsLeft/Right`. |
114
- | `isLoading` | `boolean` | Loading indicator on the Control. |
106
+ | Prop | Type | Purpose |
107
+ | -------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------- |
108
+ | `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state — use `'danger'` for errors, `'success'` for valid. |
109
+ | `size` | `'small' \| 'medium' \| 'large'` | Input size. |
110
+ | `value` / `onChange` | controlled value + handler | Standard React controlled inputs. |
111
+ | `defaultValue` | uncontrolled initial value | When not controlling state. |
112
+ | `disabled`, `readOnly` | `boolean` | Native states (`readOnly` on `*Base`). |
113
+ | `label` | `ReactNode` | Field label (convenience components; auto-associated via `htmlFor`). |
114
+ | `message` | `ReactNode` | Help / validation text rendered as `<p class="help">`. |
115
+ | `messageColor` | a Bulma color | Colors the help text (`'danger'` for errors). |
116
+ | `iconLeftName` / `iconRightName` | `string` | Icon shortcuts; pair with `hasIconsLeft/Right`. |
117
+ | `isLoading` | `boolean` | Loading indicator on the Control. |
115
118
 
116
119
  Plus the full Bulma **helper props** (`m`, `p`, `textColor`, `display`, …) on every component
117
120
  via `useBulmaClasses`.
118
121
 
119
- ⚠️ Full-width casing is inconsistent across the library: `Select`, `File`, and `Table` take
120
- `isFullwidth` (lowercase w); `Button` alone takes `isFullWidth`; `Tabs` takes bare `fullwidth`.
121
-
122
- ⚠️ The `label` prop renders the `<label>` but does **not** wire `htmlFor`/`id` — assistive tech
123
- gets no association. Pass `id` on the input plus `labelProps={{ htmlFor: sameId }}` (every
124
- convenience input and `Field` accept `labelProps`).
122
+ Full-width is `isFullwidth` on every component that supports it (`Button`, `LinkButton`,
123
+ `Select`, `File`, `Table`, `Tabs`, `Sidebar`) always write `isFullwidth`. The deprecated
124
+ spellings compile only where they historically existed: `isFullWidth` everywhere except
125
+ `Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
126
+
127
+ The `label` prop on the single-control convenience inputs (`Input`, `Select`, `TextArea`,
128
+ `File`, `Numberinput`, `Slider`, `DateInput`, `TimeInput`, `DateTimeInput`, `Autocomplete`,
129
+ `Taginput`) wires `htmlFor`/`id` automatically — your `id` is used when provided, a
130
+ generated one otherwise, and an explicit `labelProps={{ htmlFor }}` wins. The wiring only
131
+ happens when the component renders its own `Field` (nested inside one, the `label` prop is
132
+ dropped); the date/time pickers skip it in `inline` mode and `Taginput` skips it at
133
+ `maxTags` (no visible input to label). The group inputs (`Checkboxes`, `Radios`, `Rate`)
134
+ associate their `label` too, but group-style: the wrapper gets `role="group"`/`"radiogroup"`
135
+ and `aria-labelledby` pointing at the label. Composing `Field` + bases yourself also
136
+ associates: `Field`'s own `label` wires to a single composed `InputBase`/`SelectBase`/
137
+ `TextAreaBase` (skipped for `grouped`/`hasAddons`). Pass `labelProps={{ htmlFor }}` plus a
138
+ matching `id` only when you want a stable id, or `labelProps={{ htmlFor: undefined }}` to
139
+ opt out — e.g. when the labeled `Field` wraps something that is not one of those bases.
125
140
 
126
141
  ## Convenience vs composed
127
142
 
@@ -168,8 +183,6 @@ function SignupForm() {
168
183
  message={error}
169
184
  messageColor={error ? 'danger' : undefined}
170
185
  iconLeftName="envelope"
171
- id="signup-email"
172
- labelProps={{ htmlFor: 'signup-email' }}
173
186
  />
174
187
  <Button color="primary" type="submit" mt="3">
175
188
  Sign up
@@ -199,6 +212,23 @@ inventory above). **Compose these; don't hand-roll raw `<input class="input">` m
199
212
  reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
200
213
  `docs/docs/api/form/` first — it's probably already there under a different name.
201
214
 
215
+ ## What happens after submit
216
+
217
+ A form is not finished at the last field. The two components that carry the result are easy to
218
+ miss because Bulma has something that looks close:
219
+
220
+ - **Confirmation** — `Toast`, not `Notification`/`Message`. Mount
221
+ `<ToastContainer position="top-right" />` once at the app root, then call
222
+ `toast.success('Demo booked')` from the submit handler (`.danger` for a failed submit). It
223
+ self-dismisses; a `Notification` is a static element you place and tear down yourself.
224
+ - **"Are you sure?"** — `Dialog`, not `Modal`. Mount `<DialogContainer />` at the root, then
225
+ `if (await dialog.confirm({ title: 'Delete this key?', message: '…', type: 'danger' })) …`.
226
+ It resolves to a boolean, so a destructive action stays one `if` rather than a state machine.
227
+ `Modal` is an empty shell — with it you rebuild the title, message and button row by hand.
228
+
229
+ Both also work as plain controlled components (`<Toast message … onClose>`,
230
+ `<Dialog isOpen … onConfirm onCancel>`) when the state should live in your component.
231
+
202
232
  ## Visually inspect it in a browser
203
233
 
204
234
  Forms have layout, spacing, and _stateful_ behavior that types and unit tests don't cover.
@@ -213,12 +243,17 @@ for the expected classes/states, and say plainly that the visual pass is still o
213
243
  ## Checklist
214
244
 
215
245
  - [ ] Built from the shipped form components (no hand-rolled inputs / reinvented controls).
216
- - [ ] Every label is programmatically associated: `label` prop + `id` on the input +
217
- `labelProps={{ htmlFor }}`, or a `<label htmlFor>` when composing.
246
+ - [ ] Every label is programmatically associated the convenience `label` prop, the group
247
+ inputs, and `Field` + single-base composition all do this automatically; pass
248
+ `labelProps={{ htmlFor }}` plus a matching `id` only for a stable id, and label a
249
+ multi-control `Field`'s controls individually (`aria-label`, `aria-labelledby`, or
250
+ a `<label htmlFor>` matching each control's `id`).
218
251
  - [ ] Controlled inputs have both `value` and `onChange` (or use `defaultValue` uncontrolled).
219
252
  - [ ] Error state shows via `color="danger"` + `message` + `messageColor="danger"`.
220
253
  - [ ] Grouped/addon layouts use explicit `Field` + `Control` composition.
221
254
  - [ ] No assumption of a built-in validation/form library — state is owned by the app.
255
+ - [ ] Submit feedback is a `Toast` and any "are you sure?" is a `Dialog` — not a hand-placed
256
+ `Notification` or a `Modal` you filled in yourself.
222
257
  - [ ] **Rendered and visually inspected in a browser** — layout and the error/validation states
223
258
  look right, not just green tests. No browser available? The `renderToString` fallback above
224
259
  counts only if you grepped the emitted classes/states **and** said the visual pass is owed.
@@ -8,16 +8,16 @@ via `useBulmaClasses`.
8
8
 
9
9
  Container and layout. Compound parts: `Field.Label`, `Field.Body`.
10
10
 
11
- | Prop | Type | Notes |
12
- | ----------------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
13
- | `horizontal` | `boolean` | Label + control side by side. Auto-wraps children in `Field.Body`. |
14
- | `grouped` | `boolean \| 'centered' \| 'right' \| 'multiline'` | Controls in a row. |
15
- | `hasAddons` | `boolean \| 'centered' \| 'right'` | Attached controls. |
16
- | `narrow` | `boolean` | Constrain to content width (inside horizontal bodies). |
17
- | `label` | `ReactNode` | Convenience label. |
18
- | `labelSize` | `'small' \| 'normal' \| 'medium' \| 'large'` | Label size. |
19
- | `labelProps` | label attributes | Props for the `<label>` where `htmlFor` goes. |
20
- | `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
11
+ | Prop | Type | Notes |
12
+ | ----------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | `horizontal` | `boolean` | Label + control side by side. Auto-wraps children in `Field.Body`. |
14
+ | `grouped` | `boolean \| 'centered' \| 'right' \| 'multiline'` | Controls in a row. |
15
+ | `hasAddons` | `boolean \| 'centered' \| 'right'` | Attached controls. |
16
+ | `narrow` | `boolean` | Constrain to content width (inside horizontal bodies). |
17
+ | `label` | `ReactNode` | Convenience label; auto-associates with a single composed `InputBase`/`SelectBase`/`TextAreaBase` (skipped for `grouped`/`hasAddons`). |
18
+ | `labelSize` | `'small' \| 'normal' \| 'medium' \| 'large'` | Label size. |
19
+ | `labelProps` | label attributes | Props for the `<label>`. An explicit `htmlFor` (even `undefined`) takes over the association; otherwise it is set automatically. |
20
+ | `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
21
21
 
22
22
  ## Control — `form/Control.tsx`
23
23
 
@@ -59,7 +59,7 @@ Wraps a single input; adds icons and loading.
59
59
  ## Select / SelectBase, TextArea / TextAreaBase
60
60
 
61
61
  Same convenience/raw split as Input. `Select` supports `isLoading` (on the control), `color`,
62
- `size`, `isRounded`, `isFullwidth` (lowercase w — Button's is `isFullWidth`), `multiple` +
62
+ `size`, `isRounded`, `isFullwidth`, `multiple` +
63
63
  `multipleSize`, plus the Field/Control/message props. `TextArea` adds `rows` and
64
64
  `hasFixedSize`.
65
65
 
@@ -70,11 +70,12 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
70
70
  `textWeight`, `textSize` directly (their `color` prop colors the cell; for muted cell text
71
71
  wrap content in `Span textColor="grey"`). Set the app-wide icon library once with
72
72
  `<ConfigProvider iconLibrary="…">` at the root rather than `library` on every `<Icon>`.
73
- - **Decorative CSS is budgeted: two compact rules, ≤10 lines per app — comments count:
73
+ - **Decorative CSS is budgeted: three compact rules, ≤13 lines per app — comments count:
74
74
  at most one short inline note, never a file-header comment block — every value derived
75
- from `--bulma-*`.** A marketing page gets at most one hero wash + one alternating section
76
- band, applied via `className` — no resets (Bulma ships one; body/list margins are already
77
- zero) and no grid textures, masks, or multi-layer backdrops; the components carry the design:
75
+ from `--bulma-*`.** A marketing page gets at most one hero wash, one alternating section
76
+ band, and one featured-card ring, applied via `className` — no resets (Bulma ships one;
77
+ body/list margins are already zero) and no grid textures, masks, or multi-layer backdrops;
78
+ the components carry the design:
78
79
 
79
80
  ```css
80
81
  .hero-wash {
@@ -87,14 +88,19 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
87
88
  .section-alt {
88
89
  background: var(--bulma-scheme-main-bis); /* next band: -ter */
89
90
  }
91
+ .featured-ring {
92
+ --bulma-shadow: 0 0 0 2px var(--bulma-primary);
93
+ }
90
94
  ```
91
95
 
92
- A highlighted/"featured" `Card`/`Box` needs **no third rule**: wrap that one element in
93
- `<Theme bulmaVars={{ '--bulma-shadow': '0 0 0 2px var(--bulma-primary)' }}>`. Override the
94
- **upstream token**, not the component's own var: `.card`/`.box` re-declare
95
- `--bulma-card-shadow`/`--bulma-box-shadow` on their own selector, so setting those from an
96
- ancestor never wins (same for `--bulma-box-radius`; `--bulma-card-radius` is a literal with
97
- no ancestor route at all). The subtree stays theme- and dark-mode-aware.
96
+ The ring works by overriding the **upstream token**, not the component's own var: `.card`
97
+ and `.box` re-declare `--bulma-card-shadow`/`--bulma-box-shadow` from `--bulma-shadow` on
98
+ their own selector, so setting _those_ from an ancestor never wins (same for
99
+ `--bulma-box-radius`; `--bulma-card-radius` is a literal with no ancestor route at all).
100
+ It has to be a CSS rule rather than `<Theme bulmaVars={{ '--bulma-shadow': }}>`, because
101
+ `bulmaVars` is a closed typed record and `--bulma-shadow` is not one of its keys — that
102
+ exact call **does not compile**, and it was the single most repeated invention across this
103
+ library's cold-start evals. Either way the subtree stays theme- and dark-mode-aware.
98
104
 
99
105
  - **CTAs on a colored hero must stay legible in both schemes.** On a fixed-color surface
100
106
  (`Hero color="primary"`, a dark banner), use **filled** buttons — `color="light"` or
@@ -104,6 +110,26 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
104
110
  `<Theme isRoot colorMode="light">` — so a visitor's OS dark mode can't flip Bulma's text
105
111
  colors out from under the fixed palette (details: the `bestax-theming` skill's contrast rules).
106
112
 
113
+ ## Three components core Bulma will talk you out of
114
+
115
+ Most of this library's additions get found on their own, because nothing in Bulma does the
116
+ job. These three do not: each has a Bulma near-miss close enough to stop the search. Across
117
+ 44 cold-start builds, `Dialog` was used **zero** times and `LinkButton` in two thirds — and
118
+ every miss shipped the "not this" column instead.
119
+
120
+ | You need | Use | Not this |
121
+ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
122
+ | A brief confirmation after an action, self-dismissing | `Toast` — mount `<ToastContainer position="top-right" />` once at the app root, then `toast.success('Saved')` (also `.danger`/`.warning`/`.info`) from anywhere | `Notification`/`Message` — static page elements you have to place, position and dismiss yourself |
123
+ | A confirm or alert the user must answer before anything | `Dialog` — mount `<DialogContainer />` at the root, then `if (await dialog.confirm({ title, message })) …` | `Modal` — an empty shell; the title, message, button row and confirm/cancel wiring are all yours to rebuild |
124
+ | A control that reads as text or a link but _does_ something | `LinkButton` (`variant="text" \| "ghost" \| "underline"`, optional `color`) | `<a href="#">`/`<div onClick>` (no keyboard or screen-reader support) or `Button color="text"` (still button-shaped) |
125
+
126
+ Mounting a container without ever calling `toast.*`/`dialog.*` is not usage — the container is
127
+ the mount point, the imperative call is the thing that shows something. Both also work as
128
+ ordinary controlled components when you would rather hold the state yourself —
129
+ `<Toast message … duration onClose>`, `<Dialog isOpen title message type onConfirm onCancel>` —
130
+ but in an app with more than one call site the root container plus the imperative helper is
131
+ less wiring, not more.
132
+
107
133
  ## Inline style → helper prop mapping
108
134
 
109
135
  Look up the declaration you were about to inline. The spacing, typography, and flex helpers
@@ -157,11 +183,14 @@ inline `style`.
157
183
  - [ ] Wire `active` state to **both** `Navbar.Burger` and `Navbar.Menu` (they are controlled).
158
184
  - [ ] For a fixed navbar, add `has-navbar-fixed-top` to `<html>`.
159
185
  - [ ] Do not use `Tile` — it is not shipped.
186
+ - [ ] Action feedback goes through `Toast`, a confirmation through `Dialog`, a text-styled
187
+ action through `LinkButton` — not `Notification`, `Modal` or a bare `<a>`.
160
188
  - [ ] Style with helper props, not inline `style` — translate via the mapping table; values
161
189
  with no helper get a named class in the stylesheet, never `style={{}}`. No raw Bulma
162
190
  `className`s either (`Span`/`Paragraph` wrap bare text; `Th`/`Td` take `textAlign`/`textWeight`).
163
- - [ ] Decorative CSS ≤10 lines total incl. comments — no file-header comment (hero wash + section band), `--bulma-*`-derived;
164
- no resets Bulma ships one. A featured-card ring is a scoped `<Theme bulmaVars>`, not CSS.
191
+ - [ ] Decorative CSS ≤13 lines total incl. comments — no file-header comment (hero wash +
192
+ section band + featured-card ring), `--bulma-*`-derived; no resets Bulma ships one.
193
+ The ring sets `--bulma-shadow` in a CSS rule; `Theme bulmaVars` has no such key.
165
194
  - [ ] Set the icon library once via `<ConfigProvider iconLibrary="…">` at the root.
166
195
  - [ ] Site built? ~800 KB raw / ~82 KB gzip CSS is the expected default-flavor size — to shrink
167
196
  it, run the `bestax-optimize` skill (measure first).
@@ -44,7 +44,7 @@ export default function LoginPage() {
44
44
  iconLeftName="lock"
45
45
  placeholder="••••••••"
46
46
  />
47
- <Button color="primary" isFullWidth mt="4">
47
+ <Button color="primary" isFullwidth mt="4">
48
48
  Sign in
49
49
  </Button>
50
50
  </Box>
@@ -58,11 +58,11 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
58
58
 
59
59
  `<Hero>` is a full-width banner. Subcomponents: `Hero.Head`, `Hero.Body`, `Hero.Foot`.
60
60
 
61
- | Prop | Type |
62
- | ---------------------- | ---------------------------------------------------------------------------- |
63
- | `color` | Bulma color |
64
- | `size` | `'small' \| 'medium' \| 'large' \| 'fullheight' \| 'fullheight-with-navbar'` |
65
- | `fullheightWithNavbar` | `boolean` |
61
+ | Prop | Type |
62
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | `color` | `primary \| link \| info \| success \| warning \| danger \| black \| white \| light \| dark` (CSS-backed). Also accepted but deprecated, no CSS, dev-warn: `black-bis`, `black-ter`, the greys, `inherit`, `current` |
64
+ | `size` | `'small' \| 'medium' \| 'large' \| 'fullheight' \| 'fullheight-with-navbar'` |
65
+ | `fullheightWithNavbar` | `boolean` |
66
66
 
67
67
  ```tsx
68
68
  <Hero color="primary" size="medium">
@@ -118,6 +118,18 @@ The responsive grid. `Columns` is the row; `Column` is a cell (also reachable as
118
118
  ```ts
119
119
  type BulmaColumnSize =
120
120
  | number // 1–12
121
+ | '1'
122
+ | '2'
123
+ | '3'
124
+ | '4'
125
+ | '5'
126
+ | '6'
127
+ | '7'
128
+ | '8'
129
+ | '9'
130
+ | '10'
131
+ | '11'
132
+ | '12' // string form, equivalent
121
133
  | 'full'
122
134
  | 'half'
123
135
  | 'one-third'
@@ -130,8 +142,8 @@ type BulmaColumnSize =
130
142
  | 'four-fifths';
131
143
  ```
132
144
 
133
- Numeric sizes are **numbers** — `sizeDesktop={7}`, never `"7"`; only the fraction names are
134
- strings. (`gap` is the exception that accepts number **or** string.)
145
+ Numeric sizes accept a number **or** its string form — `sizeDesktop={7}` and
146
+ `sizeDesktop="7"` are equivalent, and the fraction names are strings.
135
147
 
136
148
  > Columns **stack on mobile** by default and go side-by-side at the tablet breakpoint and up.
137
149
  > Use the per-breakpoint `size*` props to control how many cells share a row at each width.
@@ -157,7 +169,9 @@ the column's auto height). Make the `Column` a flex container and let the card g
157
169
  ```
158
170
 
159
171
  `display`, `flexDirection`, and `flexGrow` are helper props every component accepts;
160
- `flexGrow` takes a string (`"1"`).
172
+ `flexGrow` takes a string (`"1"`). `Card.*` sub-parts accept helper props too, so pinning a
173
+ footer inside a card is `<Card display="flex" flexDirection="column">` +
174
+ `<Card.Content flexGrow="1">` — no `Box` swap needed.
161
175
 
162
176
  ## Grid / Cell
163
177
 
@@ -32,7 +32,7 @@ this automatically for literal values; dynamic values get TODOs.
32
32
  ## Boolean modifier convention
33
33
 
34
34
  RBC uses bare booleans (`loading`, `outlined`, `rounded`, `fullwidth`); bestax prefixes
35
- with `is`/`has` (`isLoading`, `isOutlined`, `isRounded`, `isFullWidth`). Per-component
35
+ with `is`/`has` (`isLoading`, `isOutlined`, `isRounded`, `isFullwidth`). Per-component
36
36
  tables live in [component-map.md](component-map.md).
37
37
 
38
38
  ## Responsive breakpoint objects → flat per-viewport props
@@ -100,6 +100,27 @@ and numeric shades `--bulma-<c>-00` … `--bulma-<c>-95`.
100
100
  | `--bulma-size-small` / `-normal` / `-medium` / `-large` | 0.75 / 1 / 1.25 / 1.5rem | via `bulmaVars` |
101
101
  | `--bulma-weight-light/normal/medium/semibold/bold/extrabold` | 300 / 400 / 500 / 600 / 700 / 800 | via `bulmaVars` |
102
102
 
103
+ ## Shadow — the one token `bulmaVars` cannot set
104
+
105
+ `--bulma-shadow` is a real Bulma variable (`.box` and `.card` both derive
106
+ `--bulma-box-shadow`/`--bulma-card-shadow` from it), but it is **not a key of `bulmaVars`** —
107
+ that record is a closed union built from an explicit list, and shadow was left out. Writing
108
+ `<Theme bulmaVars={{ '--bulma-shadow': … }}>` is a compile error, and it is the single most
109
+ repeated invention in this library's cold-start evals: every run on every guidance channel
110
+ tried it.
111
+
112
+ Set it in CSS instead, then apply the class to the one element that needs it:
113
+
114
+ ```css
115
+ .featured-ring {
116
+ --bulma-shadow: 0 0 0 2px var(--bulma-primary);
117
+ }
118
+ ```
119
+
120
+ Do **not** reach for `--bulma-box-shadow`/`--bulma-card-shadow` from an ancestor instead —
121
+ those are re-declared on `.box`/`.card` themselves, so an inherited value always loses (the
122
+ same rule as the extras section below).
123
+
103
124
  ## Extras component variables
104
125
 
105
126
  Every "Beyond Bulma" extra registers its own `--bulma-<component>-*` variables. They are
@@ -10,9 +10,14 @@ This is the self-contained inventory of the color/size/variant props that matter
10
10
  values are component-specific (see the table). Example: `<Button color="primary">` → `is-primary`.
11
11
  ⚠️ Some unions are **typed wider than the CSS Bulma ships** — the class is emitted but no rule
12
12
  matches. No component ships `is-grey*`/`is-*-bis`/`is-*-ter` rules at all: those `validColors`
13
- members typecheck on `Progress`/`Notification`/`Hero` but style nothing (the `has-text-*`/
14
- `has-background-*` **helpers** do cover all 17). Before relying on an unusual value, grep the
15
- shipped CSS: `node_modules/@allxsmith/bestax-bulma/dist/bestax.css` for e.g. `.progress.is-grey`.
13
+ members still typecheck on `Progress`/`Notification`/`Hero` but style nothing. They are
14
+ **deprecated**: passing one logs a console warning in development, and they will be removed
15
+ from those unions in the next major (the `has-text-*`/`has-background-*` **helpers** do cover
16
+ all 17). On the `Hero` root, `inherit` and `current` are equally CSS-less and deprecated
17
+ (its `Head`/`Body`/`Foot` sub-components keep them, as text helpers). `Pagination` and
18
+ `Tabs` ship no color CSS for **any** value — their entire `color`
19
+ prop is deprecated. Before relying on an unusual value, grep the shipped CSS:
20
+ `node_modules/@allxsmith/bestax-bulma/dist/bestax.css` for e.g. `.progress.is-grey`.
16
21
  2. **Helper color props** (on most components, applied as utility classes):
17
22
  - `color` / `textColor` → `has-text-<color>` (text color)
18
23
  - `backgroundColor` / `bgColor` → `has-background-<color>` (background)
@@ -21,7 +26,8 @@ This is the self-contained inventory of the color/size/variant props that matter
21
26
  Components with a real `is-<color>` modifier (`Button`, `Hero`) drop the `color` helper and
22
27
  re-expose it as **`textColor`** / **`bgColor`**. `Box`/`Card`/`Section` ship no `is-<color>`
23
28
  rule — their `color` _is_ the text helper (`has-text-<color>`; narrowed to the 6 on
24
- `Box`/`Card`), so `color` and `textColor` are the same lever there. `Tag` and `Td`/`Th` have
29
+ `Box`/`Card`), so `color` and `textColor` are the same lever there (prefer `textColor`; it
30
+ takes precedence when both are set). `Tag` and `Td`/`Th` have
25
31
  **no text-color prop** — wrap content in `<Span textColor="…">`. `Input` has none either and
26
32
  the wrapper trick can't work (it renders a native `<input>`; a child can't color its value):
27
33
  recolor via the upstream `--bulma-text-strong-l`, since Bulma re-declares `--bulma-input-*` on
@@ -40,43 +46,43 @@ Shades (`colorShade` / `backgroundColorShade`): `00, 05, 10, … 95, invert, lig
40
46
 
41
47
  ## Component `color` / `size` props (verbatim unions)
42
48
 
43
- | Component | `color` accepts | `size` accepts | Notes |
44
- | ------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
45
- | `Button` | `primary \| link \| info \| success \| warning \| danger \| white \| light \| dark \| black \| text \| ghost` | `small \| normal \| medium \| large` | adds `text`, `ghost`; also `isLight`, `isOutlined`, `isInverted`, `isRounded` |
46
- | `Notification` | the 17 `validColors` (greys typecheck, no CSS — see ⚠️) | — | also `isLight` |
47
- | `Tag` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `normal \| medium \| large` | also `isRounded`, `isDelete`, `isHoverable` — **no `isLight`** |
48
- | `Box` | `primary \| link \| info \| success \| warning \| danger` | — | `color` renders `has-text-<color>` (no `.box.is-*` ships — tint via `bgColor`); also `hasShadow` |
49
- | `Message` | `primary \| link \| info \| success \| warning \| danger` | — | the 6 only |
50
- | `Input` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded`, `isStatic` |
51
- | `Avatar` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `16x16 \| 24x24 \| 32x32 \| 48x48 \| 64x64 \| 96x96 \| 128x128 \| number` | initials/icon background (auto-derived from `name` when unset); also `shape` |
52
- | `Badge` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | pill background; default `danger` |
53
- | `Title` | — (no `color`; use `textColor`) | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | also `isSpaced` |
54
- | `SubTitle` | — (no `color`; use `textColor`) | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | — |
55
- | `Autocomplete` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
56
- | `Checkbox` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | the 6 only |
57
- | `DateInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded` |
58
- | `DateTimeInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded` |
59
- | `File` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isBoxed`, `isFullwidth` |
60
- | `Hero` | the 17 `validColors` (greys typecheck, no CSS — see ⚠️) | `small \| medium \| large \| fullheight \| fullheight-with-navbar` | section background |
61
- | `LinkButton` | `primary \| link \| info \| success \| warning \| danger \| white \| light \| dark \| black` | — | button-styled link; emits `link-button-<color>` — **no `isLight`/`isOutlined`/`isInverted`** |
62
- | `Loading` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | spinner color; default light grey |
63
- | `Navbar` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
64
- | `Numberinput` | `primary \| link \| info \| success \| warning \| danger \| light \| dark` | `small \| medium \| large` | also `inputColor` (the 6) for the inner input |
65
- | `Pagination` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | |
66
- | `Panel` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
67
- | `Progress` | the 17 `validColors` (greys typecheck, no CSS — see ⚠️) | `small \| medium \| large` | — |
68
- | `Radio` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | the 6 only |
69
- | `Rate` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
70
- | `Select` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded` |
71
- | `Slider` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded`, `isCircle` |
72
- | `Steps` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
73
- | `Switch` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | also `isRounded`, `isThin`, `isOutlined` |
74
- | `Tabs` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | |
75
- | `Taginput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `tagColor` (the 6 + `dark \| light`) for the tags |
76
- | `TextArea` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded`, `isStatic` |
77
- | `TimeInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | — |
78
- | `Tooltip` | `primary \| link \| info \| success \| warning \| danger \| dark \| light` | `small \| medium \| large` | — |
79
- | `Tr` / `Td` / `Th` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | cell background; cells take `textAlign`/`textWeight`/`textSize` directly |
49
+ | Component | `color` accepts | `size` accepts | Notes |
50
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
51
+ | `Button` | `primary \| link \| info \| success \| warning \| danger \| white \| light \| dark \| black \| text \| ghost` | `small \| normal \| medium \| large` | adds `text`, `ghost`; also `isLight`, `isOutlined`, `isInverted`, `isRounded` |
52
+ | `Notification` | the 17 `validColors` (`black-bis`, `black-ter`, and greys deprecated: no CSS, dev-warn, removed next major — see ⚠️) | — | also `isLight` |
53
+ | `Tag` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `normal \| medium \| large` | also `isLight`, `isRounded`, `isDelete`, `isHoverable` |
54
+ | `Box` | `primary \| link \| info \| success \| warning \| danger` | — | `color` renders `has-text-<color>`, same as `textColor` (which wins when both are set; no `.box.is-*` ships — tint via `bgColor`); also `hasShadow` |
55
+ | `Message` | `primary \| link \| info \| success \| warning \| danger` | — | the 6 only |
56
+ | `Input` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded`, `isStatic` |
57
+ | `Avatar` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `16x16 \| 24x24 \| 32x32 \| 48x48 \| 64x64 \| 96x96 \| 128x128 \| number` | initials/icon background (auto-derived from `name` when unset); also `shape` |
58
+ | `Badge` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | pill background; default `danger` |
59
+ | `Title` | — (no `color`; use `textColor`) | `1`–`6` (string or number) | also `isSpaced` |
60
+ | `SubTitle` | — (no `color`; use `textColor`) | `1`–`6` (string or number) | — |
61
+ | `Autocomplete` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
62
+ | `Checkbox` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | the 6 only |
63
+ | `DateInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded` |
64
+ | `DateTimeInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded` |
65
+ | `File` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isBoxed`, `isFullwidth` |
66
+ | `Hero` | the 17 `validColors` + `inherit`/`current` (`black-bis`, `black-ter`, greys, `inherit`, `current` deprecated: no CSS, dev-warn, removed next major — see ⚠️) | `small \| medium \| large \| fullheight \| fullheight-with-navbar` | section background |
67
+ | `LinkButton` | `primary \| link \| info \| success \| warning \| danger \| white \| light \| dark \| black` | — | button-styled link; emits `link-button-<color>` — **no `isLight`/`isOutlined`/`isInverted`** |
68
+ | `Loading` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | spinner color; default light grey |
69
+ | `Navbar` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
70
+ | `Numberinput` | `primary \| link \| info \| success \| warning \| danger \| light \| dark` | `small \| medium \| large` | also `inputColor` (the 6) for the inner input |
71
+ | `Pagination` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | **entire `color` prop deprecated** (no CSS ships; dev-warn; removal next major) |
72
+ | `Panel` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | — |
73
+ | `Progress` | the 17 `validColors` (`black-bis`, `black-ter`, and greys deprecated: no CSS, dev-warn, removed next major — see ⚠️) | `small \| medium \| large` | — |
74
+ | `Radio` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | the 6 only |
75
+ | `Rate` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
76
+ | `Select` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded` |
77
+ | `Slider` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `isRounded`, `isCircle` |
78
+ | `Steps` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | the 6 only |
79
+ | `Switch` | `primary \| link \| info \| success \| warning \| danger` | `small \| normal \| medium \| large` | also `isRounded`, `isThin`, `isOutlined` |
80
+ | `Tabs` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | **entire `color` prop deprecated** (no CSS ships; dev-warn; removal next major) |
81
+ | `Taginput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | also `tagColor` (the 6 + `dark \| light`) for the tags |
82
+ | `TextArea` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | `small \| medium \| large` | also `isRounded`, `isStatic` |
83
+ | `TimeInput` | `primary \| link \| info \| success \| warning \| danger` | `small \| medium \| large` | — |
84
+ | `Tooltip` | `primary \| link \| info \| success \| warning \| danger \| dark \| light` | `small \| medium \| large` | — |
85
+ | `Tr` / `Td` / `Th` | `primary \| link \| info \| success \| warning \| danger \| black \| dark \| light \| white` | — | cell background; cells take `textAlign`/`textWeight`/`textSize` directly |
80
86
 
81
87
  The 6 brand colors (`primary, link, info, success, warning, danger`) are the ones a custom theme
82
88
  recolors via the HSL trios (see `css-variables.md`). The greyscale and `white`/`light`/`dark`
@@ -0,0 +1,25 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+ *.tsbuildinfo
15
+
16
+ # Editor directories and files
17
+ .vscode/*
18
+ !.vscode/extensions.json
19
+ .idea
20
+ .DS_Store
21
+ *.suo
22
+ *.ntvs*
23
+ *.njsproj
24
+ *.sln
25
+ *.sw?
@@ -0,0 +1,25 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+ *.tsbuildinfo
15
+
16
+ # Editor directories and files
17
+ .vscode/*
18
+ !.vscode/extensions.json
19
+ .idea
20
+ .DS_Store
21
+ *.suo
22
+ *.ntvs*
23
+ *.njsproj
24
+ *.sln
25
+ *.sw?
@@ -5,6 +5,7 @@
5
5
  "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
6
  "module": "ESNext",
7
7
  "skipLibCheck": true,
8
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo",
8
9
 
9
10
  /* Bundler mode */
10
11
  "moduleResolution": "bundler",