create-bestax 2.2.0 β 3.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.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +27 -6
- package/dist/project-creator.d.ts +2 -0
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +27 -3
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +9 -0
- package/package.json +4 -3
- package/templates/skills/bestax-custom-component/SKILL.md +389 -0
- package/templates/skills/bestax-custom-component/references/api.md +77 -0
- package/templates/skills/bestax-custom-component/references/patterns.md +133 -0
- package/templates/skills/bestax-form/SKILL.md +209 -0
- package/templates/skills/bestax-form/references/api.md +102 -0
- package/templates/skills/bestax-form/references/patterns.md +210 -0
- package/templates/skills/bestax-layout-scaffold/SKILL.md +66 -0
- package/templates/skills/bestax-layout-scaffold/examples/app-shell.tsx +80 -0
- package/templates/skills/bestax-layout-scaffold/examples/card-grid.tsx +98 -0
- package/templates/skills/bestax-layout-scaffold/examples/centered.tsx +56 -0
- package/templates/skills/bestax-layout-scaffold/examples/landing.tsx +77 -0
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +183 -0
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +181 -0
- package/templates/skills/bestax-theming/SKILL.md +73 -0
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +38 -0
- package/templates/skills/bestax-theming/examples/theme-config.tsx +58 -0
- package/templates/skills/bestax-theming/references/css-variables.md +130 -0
- package/templates/skills/bestax-theming/references/themeable-components.md +74 -0
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,CA0BnC;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED,wBAAgB,MAAM,IAAI,IAAI,CAG7B"}
|
package/dist/cli.js
CHANGED
|
@@ -57,6 +57,8 @@ export function createCLI() {
|
|
|
57
57
|
.option('-t, --template <template>', 'template to use (vite, vite-ts)')
|
|
58
58
|
.option('-b, --bulma <flavor>', 'Bulma CSS flavor (complete, prefixed, no-helpers, no-helpers-prefixed, no-dark-mode)')
|
|
59
59
|
.option('-i, --icon <library>', 'icon library (none, fontawesome, mdi, ionicons, material-icons, material-symbols)')
|
|
60
|
+
.option('--skills', 'install the bestax AI skills into .claude/skills')
|
|
61
|
+
.option('--no-skills', 'do not install the bestax AI skills')
|
|
60
62
|
.option('-y, --yes', 'skip prompts and use defaults or provided options')
|
|
61
63
|
.action((projectDir, options) => {
|
|
62
64
|
projectCreator.create(projectDir, options);
|
package/dist/constants.d.ts
CHANGED
|
@@ -20,13 +20,16 @@ export declare const MESSAGES: {
|
|
|
20
20
|
readonly PROJECT_CREATED: "β Done! Project created successfully.";
|
|
21
21
|
readonly NEXT_STEPS: "Next steps:";
|
|
22
22
|
readonly HAPPY_CODING: "Happy coding! π";
|
|
23
|
+
readonly SKILLS_ADDED: "β Installed bestax AI skills into .claude/skills/ (+ CLAUDE.md)";
|
|
23
24
|
};
|
|
24
25
|
export declare const PROMPTS: {
|
|
25
26
|
readonly PROJECT_NAME: "Project name:";
|
|
26
27
|
readonly SELECT_FRAMEWORK: "Select a framework:";
|
|
27
28
|
readonly SELECT_ICON_LIBRARY: "Would you like to add an icon library?";
|
|
28
29
|
readonly SELECT_BULMA_FLAVOR: "Which Bulma CSS flavor would you like to use?";
|
|
30
|
+
readonly INSTALL_SKILLS: "Install the bestax AI skills (.claude/skills) for Claude Code and other agents?";
|
|
29
31
|
};
|
|
32
|
+
export declare const CLAUDE_MD: (projectName: string) => string;
|
|
30
33
|
export interface IconLibrary {
|
|
31
34
|
name: string;
|
|
32
35
|
display: string;
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -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;;;;;wCAMQ,MAAM;uCAEP,MAAM;sCACP,MAAM;4CAEA,MAAM
|
|
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;;;;;wCAMQ,MAAM;uCAEP,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;CAM7B,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;CAOV,CAAC;AAIX,eAAO,MAAM,SAAS,GAAI,aAAa,MAAM,KAAG,MAgB/C,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;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EAyCvC,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,EA0CtC,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -18,13 +18,34 @@ export const MESSAGES = {
|
|
|
18
18
|
PROJECT_CREATED: 'β Done! Project created successfully.',
|
|
19
19
|
NEXT_STEPS: 'Next steps:',
|
|
20
20
|
HAPPY_CODING: 'Happy coding! π',
|
|
21
|
+
SKILLS_ADDED: 'β Installed bestax AI skills into .claude/skills/ (+ CLAUDE.md)',
|
|
21
22
|
};
|
|
22
23
|
export const PROMPTS = {
|
|
23
24
|
PROJECT_NAME: 'Project name:',
|
|
24
25
|
SELECT_FRAMEWORK: 'Select a framework:',
|
|
25
26
|
SELECT_ICON_LIBRARY: 'Would you like to add an icon library?',
|
|
26
27
|
SELECT_BULMA_FLAVOR: 'Which Bulma CSS flavor would you like to use?',
|
|
28
|
+
INSTALL_SKILLS: 'Install the bestax AI skills (.claude/skills) for Claude Code and other agents?',
|
|
27
29
|
};
|
|
30
|
+
// Minimal CLAUDE.md scaffolded alongside the skills so an AI agent knows the
|
|
31
|
+
// stack and where the skills live.
|
|
32
|
+
export const CLAUDE_MD = (projectName) => `# ${projectName}
|
|
33
|
+
|
|
34
|
+
This app is built with [\`@allxsmith/bestax-bulma\`](https://bestax.io) β React components for
|
|
35
|
+
Bulma 1.x.
|
|
36
|
+
|
|
37
|
+
## AI skills
|
|
38
|
+
|
|
39
|
+
\`.claude/skills/\` contains Agent Skills that teach Claude how to build with this library. They load
|
|
40
|
+
automatically when the task matches:
|
|
41
|
+
|
|
42
|
+
- **bestax-custom-component** β build a new custom component the bestax way.
|
|
43
|
+
- **bestax-form** β build forms with the bestax form components (no form library).
|
|
44
|
+
- **bestax-theming** β colors, branding, and dark mode via the \`Theme\` component (\`colorMode\`).
|
|
45
|
+
- **bestax-layout-scaffold** β scaffold full pages (app shell, landing, centered, card grid).
|
|
46
|
+
|
|
47
|
+
Prefer the library's components and these skills over hand-written Bulma markup or custom CSS.
|
|
48
|
+
`;
|
|
28
49
|
export const ICON_LIBRARIES = [
|
|
29
50
|
{
|
|
30
51
|
name: 'none',
|
|
@@ -78,9 +99,9 @@ export const BULMA_FLAVORS = [
|
|
|
78
99
|
{
|
|
79
100
|
name: 'prefixed',
|
|
80
101
|
display: 'Prefixed',
|
|
81
|
-
description: 'All classes prefixed with "
|
|
102
|
+
description: 'All classes prefixed with "bestax-" to avoid conflicts',
|
|
82
103
|
color: chalk.blue,
|
|
83
|
-
importStatement: "import '
|
|
104
|
+
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-prefixed.css';",
|
|
84
105
|
needsPrefix: true,
|
|
85
106
|
},
|
|
86
107
|
{
|
|
@@ -88,14 +109,14 @@ export const BULMA_FLAVORS = [
|
|
|
88
109
|
display: 'No Helpers',
|
|
89
110
|
description: 'Core components only, no utility classes',
|
|
90
111
|
color: chalk.yellow,
|
|
91
|
-
importStatement: "import 'bulma/
|
|
112
|
+
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-helpers.css';",
|
|
92
113
|
},
|
|
93
114
|
{
|
|
94
115
|
name: 'no-helpers-prefixed',
|
|
95
116
|
display: 'No Helpers, Prefixed',
|
|
96
|
-
description: 'Core components only with "
|
|
117
|
+
description: 'Core components only with "bestax-" prefix',
|
|
97
118
|
color: chalk.magenta,
|
|
98
|
-
importStatement: "import 'bulma/
|
|
119
|
+
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-helpers-prefixed.css';",
|
|
99
120
|
needsPrefix: true,
|
|
100
121
|
},
|
|
101
122
|
{
|
|
@@ -103,6 +124,6 @@ export const BULMA_FLAVORS = [
|
|
|
103
124
|
display: 'No Dark Mode',
|
|
104
125
|
description: 'Light mode only, smaller bundle size',
|
|
105
126
|
color: chalk.cyan,
|
|
106
|
-
importStatement: "import 'bulma/
|
|
127
|
+
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-dark-mode.css';",
|
|
107
128
|
},
|
|
108
129
|
];
|
|
@@ -9,6 +9,7 @@ export interface CLIOptions {
|
|
|
9
9
|
template?: string;
|
|
10
10
|
bulma?: string;
|
|
11
11
|
icon?: string;
|
|
12
|
+
skills?: boolean;
|
|
12
13
|
yes?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export declare class ProjectCreator {
|
|
@@ -18,6 +19,7 @@ export declare class ProjectCreator {
|
|
|
18
19
|
checkExistingDirectory(targetPath: string, targetDir: string): Promise<boolean>;
|
|
19
20
|
getTemplatePath(template: string): string;
|
|
20
21
|
copyTemplate(template: string, targetPath: string): Promise<void>;
|
|
22
|
+
setupSkills(targetPath: string, projectName: string): Promise<void>;
|
|
21
23
|
setupBulmaFlavor(targetPath: string, bulmaFlavor: string, template: string): Promise<void>;
|
|
22
24
|
private getIconName;
|
|
23
25
|
private getIconProps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AAoCA,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,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBnE,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;IA2IV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAwEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAqIvE"}
|
package/dist/project-creator.js
CHANGED
|
@@ -3,10 +3,10 @@ import { fileURLToPath } from 'url';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
5
|
import { checkDirectoryExists, isDirectoryEmpty, emptyDirectory, ensureDirectory, copyDirectory, updatePackageJson, } from './file-system.js';
|
|
6
|
-
import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptIconLibrary, promptBulmaFlavor, } from './prompts.js';
|
|
6
|
+
import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptIconLibrary, promptBulmaFlavor, promptInstallSkills, } from './prompts.js';
|
|
7
7
|
import { displayHeader, displaySuccess, displayError, displayCancelled, } from './display.js';
|
|
8
8
|
import { validateProjectName } from './validators.js';
|
|
9
|
-
import { MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS } from './constants.js';
|
|
9
|
+
import { MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, } from './constants.js';
|
|
10
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
export class ProjectCreator {
|
|
12
12
|
templatesDir;
|
|
@@ -45,6 +45,16 @@ export class ProjectCreator {
|
|
|
45
45
|
await ensureDirectory(targetPath);
|
|
46
46
|
await copyDirectory(templatePath, targetPath);
|
|
47
47
|
}
|
|
48
|
+
async setupSkills(targetPath, projectName) {
|
|
49
|
+
const skillsSrc = path.join(this.templatesDir, 'skills');
|
|
50
|
+
if (!(await checkDirectoryExists(skillsSrc))) {
|
|
51
|
+
console.log(chalk.yellow(' Skipping skills β bundle not found in this build.'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await fs.copy(skillsSrc, path.join(targetPath, '.claude', 'skills'));
|
|
55
|
+
await fs.writeFile(path.join(targetPath, 'CLAUDE.md'), CLAUDE_MD(projectName));
|
|
56
|
+
console.log(chalk.green(MESSAGES.SKILLS_ADDED));
|
|
57
|
+
}
|
|
48
58
|
async setupBulmaFlavor(targetPath, bulmaFlavor, template) {
|
|
49
59
|
const flavor = BULMA_FLAVORS.find(f => f.name === bulmaFlavor);
|
|
50
60
|
if (!flavor) {
|
|
@@ -251,7 +261,7 @@ export class ProjectCreator {
|
|
|
251
261
|
// Build ConfigProvider props
|
|
252
262
|
const configProps = [];
|
|
253
263
|
if (needsPrefix) {
|
|
254
|
-
configProps.push('classPrefix="
|
|
264
|
+
configProps.push('classPrefix="bestax-"');
|
|
255
265
|
}
|
|
256
266
|
if (needsIconLibrary) {
|
|
257
267
|
// Map the icon library name to the correct value for ConfigProvider
|
|
@@ -364,6 +374,17 @@ export class ProjectCreator {
|
|
|
364
374
|
process.exit(1);
|
|
365
375
|
}
|
|
366
376
|
}
|
|
377
|
+
// Install AI skills? (use option -> --yes default -> prompt)
|
|
378
|
+
let installSkills;
|
|
379
|
+
if (options?.skills !== undefined) {
|
|
380
|
+
installSkills = options.skills;
|
|
381
|
+
}
|
|
382
|
+
else if (options?.yes) {
|
|
383
|
+
installSkills = true;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
installSkills = await promptInstallSkills();
|
|
387
|
+
}
|
|
367
388
|
// Create project
|
|
368
389
|
console.log();
|
|
369
390
|
console.log(chalk.green(MESSAGES.CREATING_PROJECT(targetPath)));
|
|
@@ -373,6 +394,9 @@ export class ProjectCreator {
|
|
|
373
394
|
await this.setupBulmaFlavor(targetPath, bulmaFlavor, template);
|
|
374
395
|
await this.setupIconLibrary(targetPath, iconLibrary, template);
|
|
375
396
|
await this.setupConfigProvider(targetPath, bulmaFlavor, iconLibrary, template);
|
|
397
|
+
if (installSkills) {
|
|
398
|
+
await this.setupSkills(targetPath, projectName);
|
|
399
|
+
}
|
|
376
400
|
displaySuccess(targetDir);
|
|
377
401
|
}
|
|
378
402
|
catch (error) {
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare function promptProjectName(): Promise<string | null>;
|
|
2
2
|
export declare function promptOverwriteDirectory(targetDir: string): Promise<boolean>;
|
|
3
|
+
export declare function promptInstallSkills(): Promise<boolean>;
|
|
3
4
|
export declare function promptTemplate(): Promise<string | null>;
|
|
4
5
|
export declare function promptIconLibrary(): Promise<string | null>;
|
|
5
6
|
export declare function promptBulmaFlavor(): Promise<string | null>;
|
package/dist/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAWA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUhE;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY7D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAahE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAehE"}
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAWA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUhE;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS5D;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY7D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAahE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAehE"}
|
package/dist/prompts.js
CHANGED
|
@@ -20,6 +20,15 @@ export async function promptOverwriteDirectory(targetDir) {
|
|
|
20
20
|
});
|
|
21
21
|
return response.overwrite === true;
|
|
22
22
|
}
|
|
23
|
+
export async function promptInstallSkills() {
|
|
24
|
+
const response = await prompts({
|
|
25
|
+
type: 'confirm',
|
|
26
|
+
name: 'skills',
|
|
27
|
+
message: PROMPT_MESSAGES.INSTALL_SKILLS,
|
|
28
|
+
initial: true,
|
|
29
|
+
});
|
|
30
|
+
return response.skills === true;
|
|
31
|
+
}
|
|
23
32
|
export async function promptTemplate() {
|
|
24
33
|
const response = await prompts({
|
|
25
34
|
type: 'select',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bestax",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Create a new bestax-bulma project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"templates"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
14
|
+
"build": "node scripts/sync-skills.mjs && tsc",
|
|
15
|
+
"prepack": "node scripts/sync-skills.mjs",
|
|
15
16
|
"dev": "tsc --watch",
|
|
16
17
|
"test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
|
|
17
18
|
"test:watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"@playwright/test": "^1.57.0",
|
|
62
63
|
"@types/fs-extra": "^11.0.4",
|
|
63
64
|
"@types/jest": "^30.0.0",
|
|
64
|
-
"@types/node": "^
|
|
65
|
+
"@types/node": "^26.0.1",
|
|
65
66
|
"@types/prompts": "^2.4.9",
|
|
66
67
|
"@types/wait-on": "^5.3.4",
|
|
67
68
|
"jest": "^30.2.0",
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bestax-custom-component
|
|
3
|
+
description: Build a new custom Bulma "extra" component for @allxsmith/bestax-bulma β a React + TypeScript component with Bulma v1 SCSS (the CSS-variable pattern), Storybook stories, tests, and docs. Use when adding a component that goes beyond stock Bulma (like Dialog, Carousel, Switch, Slider, Rate, Taginput), or when extending an existing one to match the library's conventions.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Building a custom bestax-bulma component
|
|
8
|
+
|
|
9
|
+
This skill teaches the end-to-end pattern the library uses for its custom "extra"
|
|
10
|
+
components β the ones that aren't part of stock Bulma. Follow it whenever you add a new
|
|
11
|
+
component to `@allxsmith/bestax-bulma`, or refactor a component to match house style.
|
|
12
|
+
|
|
13
|
+
## Use when
|
|
14
|
+
|
|
15
|
+
- Creating a new component beyond stock Bulma (an interactive widget, a composed element).
|
|
16
|
+
- Writing the component's SCSS and you need it to follow the **Bulma v1 CSS-variable pattern**
|
|
17
|
+
(`register-vars` / `getVar`, `--bulma-*` custom properties, the class prefix).
|
|
18
|
+
- Wiring a component into the package exports, the SCSS bundle, Storybook, tests, and docs.
|
|
19
|
+
|
|
20
|
+
For **form** components (Field/Control/Input/etc.) use the `bestax-form` skill instead.
|
|
21
|
+
|
|
22
|
+
## Check for an existing component first
|
|
23
|
+
|
|
24
|
+
Before building anything, **search the library for a component that already does this β or a
|
|
25
|
+
close synonym** β and tell the user what you found. Many requests are covered by an existing
|
|
26
|
+
element, or are best built by composing existing ones. Building a near-duplicate (a "label" when
|
|
27
|
+
`Tag` exists, a "banner" when `Notification` exists) fragments the API and is usually the wrong
|
|
28
|
+
call.
|
|
29
|
+
|
|
30
|
+
Where to look:
|
|
31
|
+
|
|
32
|
+
- `bulma-ui/src/index.ts` β the full export list; scan it for the name and its synonyms.
|
|
33
|
+
- `docs/docs/api/{elements,components,form}/` β one doc page per shipped component.
|
|
34
|
+
- Storybook titles β `Elements/*`, `Components/*`, `Form/*`.
|
|
35
|
+
|
|
36
|
+
Then decide, and **surface the decision to the user**:
|
|
37
|
+
|
|
38
|
+
- **Exact / synonym match exists** β recommend using it. Don't build a duplicate. (E.g. a small
|
|
39
|
+
colored label/badge/chip β `Tag` / `Tags` already exist.)
|
|
40
|
+
- **Partial overlap** β prefer **composing or extending** the existing pieces inside your new
|
|
41
|
+
component rather than re-implementing them. (E.g. a "profile card" β there's no `ProfileCard`,
|
|
42
|
+
but `Card`, `Image`, `Title`, `SubTitle`, and `Content` exist; build `ProfileCard` to compose
|
|
43
|
+
them.)
|
|
44
|
+
- **Genuine gap** β build the new component using the pattern below.
|
|
45
|
+
|
|
46
|
+
State plainly which case applies before writing code, e.g. _"`Tag` already covers a colored
|
|
47
|
+
label β use that instead"_ or _"No `ProfileCard` exists; I'll build one composing the existing
|
|
48
|
+
`Card`/`Image`/`Title` elements."_
|
|
49
|
+
|
|
50
|
+
## File layout
|
|
51
|
+
|
|
52
|
+
Every custom component has five files. Mirror the existing names exactly (PascalCase TSX,
|
|
53
|
+
`_kebab.scss` partial):
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
bulma-ui/src/components/MyComponent.tsx # React + TS component
|
|
57
|
+
bulma-ui/src/components/MyComponent.stories.tsx # Storybook stories
|
|
58
|
+
bulma-ui/src/components/__tests__/MyComponent.test.tsx # Jest + RTL tests
|
|
59
|
+
bulma-ui/src/scss/components/_mycomponent.scss # SCSS partial
|
|
60
|
+
docs/docs/api/components/mycomponent.md # Docusaurus docs page
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then wire two index files (see **Wiring & build**).
|
|
64
|
+
|
|
65
|
+
## Component template
|
|
66
|
+
|
|
67
|
+
Components use `forwardRef`, accept Bulma helper props via `BulmaClassesProps`, run them
|
|
68
|
+
through `useBulmaClasses`, build their own classes with `usePrefixedClassNames`, and merge
|
|
69
|
+
everything with `classNames`. Spread `rest` (the non-helper props) onto the DOM node.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import React, { forwardRef } from 'react';
|
|
73
|
+
import { classNames, usePrefixedClassNames } from '../helpers/classNames';
|
|
74
|
+
import { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';
|
|
75
|
+
|
|
76
|
+
export type MyComponentColor =
|
|
77
|
+
| 'primary'
|
|
78
|
+
| 'link'
|
|
79
|
+
| 'info'
|
|
80
|
+
| 'success'
|
|
81
|
+
| 'warning'
|
|
82
|
+
| 'danger';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Props for the MyComponent component.
|
|
86
|
+
*
|
|
87
|
+
* @property {MyComponentColor} [color] - Bulma color modifier.
|
|
88
|
+
* @property {'small' | 'medium' | 'large'} [size] - Size modifier.
|
|
89
|
+
* @property {boolean} [isActive] - Whether the component is active.
|
|
90
|
+
*/
|
|
91
|
+
export interface MyComponentProps
|
|
92
|
+
extends
|
|
93
|
+
Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,
|
|
94
|
+
Omit<BulmaClassesProps, 'color'> {
|
|
95
|
+
color?: MyComponentColor;
|
|
96
|
+
size?: 'small' | 'medium' | 'large';
|
|
97
|
+
isActive?: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* MyComponent β short description of what it does.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* <MyComponent color="primary" size="large" isActive>Hello</MyComponent>
|
|
105
|
+
*/
|
|
106
|
+
export const MyComponent = forwardRef<HTMLDivElement, MyComponentProps>(
|
|
107
|
+
({ color, size, isActive, className, children, ...props }, ref) => {
|
|
108
|
+
// 1. Pull Bulma helper classes (m/p, text*, display, etc.) out of props.
|
|
109
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
110
|
+
|
|
111
|
+
// 2. Build this component's own classes (respects the Config classPrefix).
|
|
112
|
+
const mainClasses = usePrefixedClassNames('mycomponent', {
|
|
113
|
+
[`is-${color}`]: !!color,
|
|
114
|
+
[`is-${size}`]: !!size,
|
|
115
|
+
'is-active': !!isActive,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// 3. Merge: own classes + helper classes + caller className.
|
|
119
|
+
const combined = classNames(mainClasses, bulmaHelperClasses, className);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div ref={ref} className={combined} {...rest}>
|
|
123
|
+
{children}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
MyComponent.displayName = 'MyComponent';
|
|
130
|
+
|
|
131
|
+
export default MyComponent;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Rules that keep components consistent:
|
|
135
|
+
|
|
136
|
+
- **Always `Omit<β¦, 'color'>`** from both `HTMLAttributes` and `BulmaClassesProps` when the
|
|
137
|
+
component exposes its own typed `color`, so the native/helper `color` doesn't collide.
|
|
138
|
+
- **Never hand-build class strings.** Use `usePrefixedClassNames(base, conditionalMap)` so the
|
|
139
|
+
optional `classPrefix` from `Config` is honored, then `classNames(...)` to merge.
|
|
140
|
+
- **Spread `rest`, not `props`**, onto the DOM node β `useBulmaClasses` has already stripped the
|
|
141
|
+
helper props out of `rest`, so they don't leak to the DOM as invalid attributes.
|
|
142
|
+
- **Set `displayName`** on `forwardRef` components (needed for tests and Storybook autodocs).
|
|
143
|
+
- **Element sizing uses an inline `'small' | 'medium' | 'large'` union**, mapped to `is-small` /
|
|
144
|
+
`is-medium` / `is-large` (see `Tabs.tsx`, `Control.tsx`). Do **not** reach for the `validSizes`
|
|
145
|
+
constant β that one is `'0'β¦'6' | 'auto'` and exists for **spacing** helpers, not element size.
|
|
146
|
+
- **Format before you lint.** The repo enforces Prettier and ESLint fails on unformatted code.
|
|
147
|
+
Run `npx prettier --write` on your new files (or `npm run format` from the repo root) before
|
|
148
|
+
`npm run lint`. Copy snippets as a starting point, then let Prettier normalize them.
|
|
149
|
+
|
|
150
|
+
See `references/api.md` for the full helper API and `references/patterns.md` for the complete
|
|
151
|
+
Dialog walkthrough.
|
|
152
|
+
|
|
153
|
+
## SCSS pattern (required)
|
|
154
|
+
|
|
155
|
+
This is the library's house convention β **the Bulma v1 CSS-variable pattern**. Do not write
|
|
156
|
+
plain hard-coded CSS or homebrew `--mycomponent-*` variables. Import Bulma's utilities, declare
|
|
157
|
+
SCSS vars with `!default`, register them as `--bulma-*` custom properties on the root selector
|
|
158
|
+
with `cv.register-vars`, then consume them with `cv.getVar`. Prefix every selector with
|
|
159
|
+
`iv.$class-prefix`.
|
|
160
|
+
|
|
161
|
+
```scss
|
|
162
|
+
// bulma-ui/src/scss/components/_mycomponent.scss
|
|
163
|
+
@use 'bulma/sass/utilities/initial-variables' as iv;
|
|
164
|
+
@use 'bulma/sass/utilities/css-variables' as cv;
|
|
165
|
+
|
|
166
|
+
// 1. SCSS variables, overridable, referencing Bulma vars via cv.getVar.
|
|
167
|
+
$mycomponent-radius: cv.getVar('radius') !default;
|
|
168
|
+
$mycomponent-background: cv.getVar('scheme-main') !default;
|
|
169
|
+
$mycomponent-color: cv.getVar('text') !default;
|
|
170
|
+
$mycomponent-padding: 1rem !default;
|
|
171
|
+
|
|
172
|
+
// 2. Register them as runtime --bulma-* custom properties on the root selector.
|
|
173
|
+
.#{iv.$class-prefix}mycomponent {
|
|
174
|
+
@include cv.register-vars(
|
|
175
|
+
(
|
|
176
|
+
'mycomponent-radius': #{$mycomponent-radius},
|
|
177
|
+
'mycomponent-background': #{$mycomponent-background},
|
|
178
|
+
'mycomponent-color': #{$mycomponent-color},
|
|
179
|
+
'mycomponent-padding': #{$mycomponent-padding},
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 3. Consume via cv.getVar. Prefix every selector with iv.$class-prefix.
|
|
185
|
+
.#{iv.$class-prefix}mycomponent {
|
|
186
|
+
background-color: cv.getVar('mycomponent-background');
|
|
187
|
+
border-radius: cv.getVar('mycomponent-radius');
|
|
188
|
+
color: cv.getVar('mycomponent-color');
|
|
189
|
+
padding: cv.getVar('mycomponent-padding');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Color variants reuse Bulma's registered color vars.
|
|
193
|
+
.#{iv.$class-prefix}mycomponent.#{iv.$class-prefix}is-primary {
|
|
194
|
+
background-color: cv.getVar('primary');
|
|
195
|
+
color: cv.getVar('primary-invert');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Respect reduced-motion if you animate.
|
|
199
|
+
@media (prefers-reduced-motion: reduce) {
|
|
200
|
+
.#{iv.$class-prefix}mycomponent {
|
|
201
|
+
animation: none;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Why this matters: registering vars makes the component themeable at runtime (the docs site and
|
|
207
|
+
`Theme`/`Config` providers override `--bulma-*` properties), and the `iv.$class-prefix` keeps
|
|
208
|
+
the component working when consumers opt into a class prefix to avoid collisions.
|
|
209
|
+
|
|
210
|
+
The canonical reference file is `bulma-ui/src/scss/components/_dialog.scss`.
|
|
211
|
+
|
|
212
|
+
## Stories
|
|
213
|
+
|
|
214
|
+
`MyComponent.stories.tsx` beside the component. Use `tags: ['autodocs']` so the JSDoc becomes
|
|
215
|
+
the docs page, declare `argTypes`, and write one named `function`-style render per variant.
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
219
|
+
import { MyComponent } from './MyComponent';
|
|
220
|
+
|
|
221
|
+
const meta: Meta<typeof MyComponent> = {
|
|
222
|
+
title: 'Components/MyComponent',
|
|
223
|
+
component: MyComponent,
|
|
224
|
+
tags: ['autodocs'],
|
|
225
|
+
argTypes: {
|
|
226
|
+
color: {
|
|
227
|
+
control: 'select',
|
|
228
|
+
options: ['primary', 'link', 'info', 'success', 'warning', 'danger'],
|
|
229
|
+
},
|
|
230
|
+
isActive: { control: 'boolean' },
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
export default meta;
|
|
234
|
+
type Story = StoryObj<typeof MyComponent>;
|
|
235
|
+
|
|
236
|
+
export const Default: Story = {
|
|
237
|
+
render: function DefaultExample() {
|
|
238
|
+
return <MyComponent>Default</MyComponent>;
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const Colors: Story = {
|
|
243
|
+
render: function ColorsExample() {
|
|
244
|
+
return (
|
|
245
|
+
<>
|
|
246
|
+
<MyComponent color="primary">Primary</MyComponent>
|
|
247
|
+
<MyComponent color="danger">Danger</MyComponent>
|
|
248
|
+
</>
|
|
249
|
+
);
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Tests
|
|
255
|
+
|
|
256
|
+
`__tests__/MyComponent.test.tsx`, Jest + `@testing-library/react`. Cover render, each prop β
|
|
257
|
+
class mapping, the helper-prop passthrough, ref forwarding, and any interaction/a11y.
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
import { render, screen } from '@testing-library/react';
|
|
261
|
+
import { createRef } from 'react';
|
|
262
|
+
import { MyComponent } from '../MyComponent';
|
|
263
|
+
|
|
264
|
+
describe('MyComponent', () => {
|
|
265
|
+
it('renders children', () => {
|
|
266
|
+
render(<MyComponent>Hello</MyComponent>);
|
|
267
|
+
expect(screen.getByText('Hello')).toBeInTheDocument();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('applies the color modifier', () => {
|
|
271
|
+
render(<MyComponent color="primary">x</MyComponent>);
|
|
272
|
+
expect(screen.getByText('x')).toHaveClass('mycomponent', 'is-primary');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('passes Bulma helper props through', () => {
|
|
276
|
+
render(<MyComponent m="3">x</MyComponent>);
|
|
277
|
+
expect(screen.getByText('x')).toHaveClass('m-3');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('forwards the ref', () => {
|
|
281
|
+
const ref = createRef<HTMLDivElement>();
|
|
282
|
+
render(<MyComponent ref={ref}>x</MyComponent>);
|
|
283
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Docs page
|
|
289
|
+
|
|
290
|
+
`docs/docs/api/components/mycomponent.md` β Overview, Import, a Props table, and `Usage` with
|
|
291
|
+
live examples. Live code blocks use the ` ```tsx live ` fence (Docusaurus live-codeblock).
|
|
292
|
+
|
|
293
|
+
````md
|
|
294
|
+
---
|
|
295
|
+
title: MyComponent
|
|
296
|
+
sidebar_label: MyComponent
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
# MyComponent
|
|
300
|
+
|
|
301
|
+
## Overview
|
|
302
|
+
|
|
303
|
+
Short description of the component.
|
|
304
|
+
|
|
305
|
+
## Import
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { MyComponent } from '@allxsmith/bestax-bulma';
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Props
|
|
312
|
+
|
|
313
|
+
| Prop | Type | Default | Description |
|
|
314
|
+
| ---------- | ---------------------------- | ------- | --------------------- |
|
|
315
|
+
| `color` | `'primary' \| 'link' \| ...` | β | Bulma color modifier. |
|
|
316
|
+
| `isActive` | `boolean` | `false` | Active state. |
|
|
317
|
+
|
|
318
|
+
## Usage
|
|
319
|
+
|
|
320
|
+
### Default
|
|
321
|
+
|
|
322
|
+
```tsx live
|
|
323
|
+
<MyComponent>Hello</MyComponent>
|
|
324
|
+
```
|
|
325
|
+
````
|
|
326
|
+
|
|
327
|
+
> Note: the Docusaurus docs load the **built** dist CSS. After SCSS changes, run
|
|
328
|
+
> `cd bulma-ui && npm run build` before the new styles show up in the docs site (Storybook
|
|
329
|
+
> compiles SCSS live and does not need this).
|
|
330
|
+
|
|
331
|
+
## Wiring & build
|
|
332
|
+
|
|
333
|
+
Two index files must be updated or the component won't ship:
|
|
334
|
+
|
|
335
|
+
1. **Package export** β add to `bulma-ui/src/index.ts`, in the **components** group (the file
|
|
336
|
+
groups exports by directory β keep yours next to the other `./components/*` lines):
|
|
337
|
+
```ts
|
|
338
|
+
export * from './components/MyComponent';
|
|
339
|
+
```
|
|
340
|
+
2. **SCSS bundle** β add to `bulma-ui/src/scss/components/_index.scss`:
|
|
341
|
+
```scss
|
|
342
|
+
@use 'mycomponent';
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Then build and verify:
|
|
346
|
+
|
|
347
|
+
```sh
|
|
348
|
+
cd bulma-ui
|
|
349
|
+
npx prettier --write src/components/MyComponent.tsx src/scss/components/_mycomponent.scss
|
|
350
|
+
npm run lint
|
|
351
|
+
npm test
|
|
352
|
+
npm run build # compiles JS + the bestax/extras CSS bundles
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Visually inspect it in a browser
|
|
356
|
+
|
|
357
|
+
Types and unit tests don't see layout. **Render the component and actually look at it** before
|
|
358
|
+
you call it done β spacing, padding, vertical centering, alignment, and every variant/state
|
|
359
|
+
(colors, sizes, hover/active, dark mode). Visual bugs hide from `tsc` and `@testing-library`.
|
|
360
|
+
|
|
361
|
+
1. Run a surface that renders it: `npm run storybook` (compiles SCSS live) or the docs dev server.
|
|
362
|
+
2. Open the component and inspect it. If a browser-automation tool (claude-in-chrome, Playwright)
|
|
363
|
+
is available, drive the browser and screenshot each variant; otherwise open it yourself and
|
|
364
|
+
eyeball it.
|
|
365
|
+
3. Check the usual offenders:
|
|
366
|
+
- **Vertical centering of inline text** β `display: inline-block` + `line-height: 1` makes
|
|
367
|
+
text sit low. For chips/labels/buttons use `display: inline-flex; align-items: center;
|
|
368
|
+
justify-content: center;` with a normal `line-height` (Bulma's `Tag` is the reference).
|
|
369
|
+
- Padding/gaps look balanced; nothing clips or overflows.
|
|
370
|
+
- Every color/size variant renders; dark mode is legible.
|
|
371
|
+
|
|
372
|
+
Fix what you see, then re-inspect. A green test suite with a misaligned component is not done.
|
|
373
|
+
|
|
374
|
+
## Checklist
|
|
375
|
+
|
|
376
|
+
- [ ] **Checked the inventory first** β searched `src/index.ts` / docs / Storybook for an existing
|
|
377
|
+
match or synonym, and told the user (reuse/extend it, or confirm there's a genuine gap).
|
|
378
|
+
- [ ] `MyComponent.tsx` β `forwardRef`, `Omit<β¦, 'color'>`, `useBulmaClasses`,
|
|
379
|
+
`usePrefixedClassNames`, `classNames`, spread `rest`, `displayName` set.
|
|
380
|
+
- [ ] `_mycomponent.scss` β `@use` Bulma utilities, `$vars !default`, `cv.register-vars`,
|
|
381
|
+
`cv.getVar`, every selector prefixed with `iv.$class-prefix`.
|
|
382
|
+
- [ ] `MyComponent.stories.tsx` β `tags: ['autodocs']`, `argTypes`, one story per variant.
|
|
383
|
+
- [ ] `__tests__/MyComponent.test.tsx` β render, propβclass, helper passthrough, ref.
|
|
384
|
+
- [ ] `docs/docs/api/components/mycomponent.md` β Overview / Import / Props / `tsx live`.
|
|
385
|
+
- [ ] `src/index.ts` exports the component (in the `./components/*` group).
|
|
386
|
+
- [ ] `scss/components/_index.scss` `@use`s the partial.
|
|
387
|
+
- [ ] Prettier-formatted, then `npm run lint && npm test && npm run build` all pass.
|
|
388
|
+
- [ ] **Rendered and visually inspected in a browser** β centering/spacing/variants all look
|
|
389
|
+
right (not just green tests).
|