安装与下载
1 ClawHub CLI
从源直接安装技能的最快方式。
npx clawhub@latest install bestax-custom-component
2 手动安装
将技能文件夹复制到以下位置之一
~/.openclaw/skills/
{'/skills/'}
优先级:工作区 › 全局 › 内置
3 提示词安装
将此提示词复制到 Openclaw 即可自动安装。
请帮我使用 Clawhub 安装 skill-bestax-custom-component。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
更倾向于下载?
获取包含原生技能文件的ZIP压缩包。
基本信息
bestax-custom-component
安装指南
- ClawHub CLI
从源直接安装技能的最快方式。
npx clawhub@latest install bestax-custom-component - 手动安装
将技能文件夹复制到以下位置之一
全局模式
~/.openclaw/skills/
工作区
/skills/
优先级:工作区 > 本地 > 内置 - 提示词安装
将此提示词复制到 OpenClaw 即可自动安装。
请帮我使用 Clawhub 安装 bestax-custom-component。如果尚未安装 Clawhub,请先安装(npm i -g clawhub)。
更倾向于下载?
获取包含原生技能文件的ZIP压缩包。
下载技能 (.zip)
什么是
Bestax Custom Component 指导 AI 编码代理为使用 @allxsmith/bestax-bulma 的应用创建自定义 React 组件。它优先检查是否已有现成组件、组合库中的构建模块,并在添加自定义样式前使用共享的 Bulma 辅助属性 API。
该技能还确立了一种组件模式:使用 useBulmaClasses,并在需要时使用 usePrefixedClassNames,同时确保样式兼容 Bulma 主题变量和深色模式。对于在 bestax 单体仓库中工作的贡献者,它会引导他们使用单独的库贡献者工作流。这种实用方法让 Openclaw Skills 能够帮助构建一致的 UI,避免重复创建现有组件或将辅助属性泄漏到 DOM 中。
应用场景
创建 Bestax 组件库中尚不存在的可复用组件。
Card
、
Image
、
Title
Content
等现有组件组合成更高层级的 UI 模式。
使用遵循其辅助属性约定的组件扩展基于
@allxsmith/bestax-bulma
的应用。
在保留 Bulma 主题、CSS 变量标记和深色模式行为的同时,添加少量自定义样式。
在适当情况下,使用
ClientOnly
、
Portal
useFocusTrap
构建仅限浏览器或浮动界面。
实现重复组件前,先检查现有组件或同义组件是否已满足需求。
在 Vite 应用中验证组件渲染和样式;如果项目已有测试运行器,也可添加测试。
工作原理
确认项目环境:对于依赖 @allxsmith/bestax-bulma 的应用,使用此工作流;在 bestax 单体仓库中则改用 references/library-contributor.md。表单组件请使用 bestax-form 技能。
搜索 references/component-catalog.md 和 Bestax API 文档,查找完全匹配项、同义组件或可复用组件。告知用户应使用现有组件、组合现有组件,还是确实需要新增组件。
优先使用 Bestax 组件和辅助属性进行组合。遵循子组件的特殊限制,并根据渲染和焦点需求使用 ClientOnly、Portal 或 useFocusTrap。
实现组件主体:扩展 BulmaClassesProps,通过 useBulmaClasses 传递辅助属性,将 bulmaHelperClasses 与 className 组合,并将返回的 rest 展开到 DOM 元素上。当组件级 CSS 或变体类需要根类时,添加 usePrefixedClassNames。
选择满足需求的最低复杂度样式方案:优先使用辅助属性,其次使用基于 --bulma-* 变量的作用域 CSS,仅在需要其功能时使用 Sass。避免使用内联 style 属性和字面主题颜色。
运行应用并检查布局、变体、裁切和深色模式。如果无法检查浏览器,则执行构建并进行服务器端渲染冒烟测试,同时说明视觉验证尚未完成。
仅当应用已包含受支持的测试运行器和 Testing Library 时才添加测试;不要在未被要求时安装测试工具链或 Storybook。
配置指南
对于使用 @allxsmith/bestax-bulma 的现有应用:
确认项目是应用而非 bestax 单体仓库。如果仓库包含 bulma-ui/src/,则改用单体仓库贡献者指南。
从 references/component-catalog.md 开始,并查阅 https://bestax.io/docs/api,检查是否已有组件。
在应用中创建组件,从 @allxsmith/bestax-bulma 导入公共 API;不要使用深层或内部包路径。
使用标准的辅助属性组件模式,仅在辅助属性不足时添加样式。
若要选择性使用 Sass,请将 Sass 安装为开发依赖;Vite 会编译导入的 SCSS,Bulma 则可通过 Bestax 运行时依赖使用。
npm run dev
选择性配置 Sass:
npm i -D sass
然后在浏览器中检查组件,包括深色模式。如果无法使用浏览器,则运行构建和 Node renderToString 冒烟检查,并报告未执行视觉验证。除非用户提出要求,否则不要向脚手架生成的应用添加测试运行器或 Storybook。
数据架构
此技能描述的是组件实现工作流,而非生成的数据格式。其主要产物是应用源文件,以及按需创建的组件作用域样式表。
产物或约定 用途
src/components/MyComponent.tsx 可复用 React 组件、属性和标记;从公共包导入 Bestax API。
MyComponentProps 扩展 HTML 属性和 BulmaClassesProps,同时解决组件特定的 color 属性等冲突。
useBulmaClasses(props) 将辅助属性转换为 bulmaHelperClasses 和适用于 DOM 的 rest 对象。
usePrefixedClassNames('mycomponent', variants) 当组件 CSS 或变体需要时,提供带前缀的根类和变体类。
src/components/MyComponent.css 可选的作用域样式,使用 --bulma-* 标记而非字面主题颜色。
references/component-catalog.md 和 Bestax API 文档 用于查找现有组件、同义组件和公共属性的参考资料。
样式采用三级分类:(1)用于间距、排版、颜色和弹性布局的共享辅助属性;(2)使用 Bulma 变量、用于没有对应辅助属性的值的命名作用域 CSS;(3)用于更高级样式表需求的可选 Sass。除组件及实现所需的样式表外,没有规定必须生成的元数据或文件输出。
高级特性
发现现有组件:
首先检查目录和 API 文档,然后明确区分复用、组合和确实存在的库缺口。
集成辅助属性:
useBulmaClasses
使用完整的受支持 Bulma 辅助属性,并展开其返回的
rest
,避免辅助属性泄漏到 DOM 节点。
类名前缀支持:
在需要时使用
usePrefixedClassNames
,并确保作用域 CSS 选择器与配置的前缀行为一致。
适配主题的自定义 CSS:
--bulma-*
变量编写自定义样式,使主题和深色模式继续生效;避免硬编码颜色。
支持水合的渲染:
对依赖浏览器的值使用
ClientOnly
,对浮动 UI 使用
Portal
,而非临时的浏览器检查或
createPortal
。
焦点管理:
对应当限制键盘焦点并在关闭时恢复焦点的可关闭面板,使用
useFocusTrap
。
可选 Sass:
仅在需要时添加
sass
;Vite 支持导入 SCSS,Bulma Sass 可通过运行时依赖使用。
验证规范:
检查真实浏览器渲染和深色模式;无法访问浏览器时,将构建和服务器端渲染冒烟检查作为有限的替代验证。
name: bestax-custom-component description: Build a custom React component in the bestax/Bulma style. In an app using @allxsmith/bestax-bulma — compose existing components, helper props, public hooks (useBulmaClasses, usePrefixedClassNames), and --bulma-* CSS variables. In the bestax monorepo — the full component pipeline (SCSS partial, stories, tests, docs, wiring). Use when creating a component beyond stock Bulma or extending one.
Building a custom component the bestax way
This skill teaches how to build a component that isn't in the library — composed from bestax pieces in an app, or as a full library "extra" inside the bestax monorepo.
Which context are you in?
The bestax monorepo (the repo contains bulma-ui/src/) → follow references/library-contributor.md instead of this file: five-file layout, SCSS partial, stories, jest tests, docs page, wiring.
An app depending on @allxsmith/bestax-bulma (e.g. scaffolded by npm create bestax) → continue here. Everything below assumes public package imports and a plain Vite app.
For form components (Field/Control/Input/etc.) use the bestax-form skill instead.
Check for an existing component first
Before building anything, search the library for a component that already does this — or a close synonym — and tell the user what you found. Many requests are covered by an existing element, or are best built by composing existing ones. Building a near-duplicate (a "label" when Tag exists, a "banner" when Notification exists) fragments the API and is usually the wrong call.
Where to look:
references/component-catalog.md — start here. Every documented component with a one-line purpose, grouped by category. Scan it for the name and its synonyms before anything else.
https://bestax.io/docs/api — one doc page per shipped component (full props).
Then decide, and surface the decision to the user:
Exact / synonym match exists → recommend using it. Don't build a duplicate. (E.g. a small colored label/badge/chip → Tag / Tags already exist.)
Partial overlap → prefer composing the existing pieces inside your new component rather than re-implementing them. (E.g. a "profile card" → there's no ProfileCard, but Card, Image, Title, SubTitle, and Content exist; build ProfileCard to compose them.)
Genuine gap → build the new component using the pattern below.
State plainly which case applies before writing code, e.g. "Tag already covers a colored label — use that instead" or "No ProfileCard exists; I'll build one composing the existing Card/Image/Title elements."
Composition first
Build from existing components before writing any CSS: Box, Card, Title, SubTitle, Icon, Block, Content, Tag, plus the shared Bulma helper props (spacing, color, typography, flexbox). Some compound sub-parts are the exception — Modal.Card, Tabs.Tab, Message.Body take no Bulma helper props, just className + HTML attributes plus their own few (Tabs.Tab requires index={i} and has built-in disabled and icon/iconLibrary/iconVariant/iconSize/iconFeatures — don't nest an there) — so put helper props on the parent or on an element inside them, never invent them there. (Card.* sub-parts do take helper props, like Table./Menu./Hero.*.) Most "custom components" are a composition function — zero new styles. See examples/stat-card.tsx for a complete worked example.
Rendering something only the browser knows (the reader's time zone, a localStorage value)? Wrap it in the library's ClientOnly rather than checking typeof window: it renders a fallback until the page has hydrated, so the server and client markup match. references/api.md has the details.
Building something that floats (a panel opened from a button, a command palette)? Render it through the library's Portal rather than createPortal: it renders nothing on the server and during hydration, so the server and client markup match. references/api.md has the details.
Building a panel that holds focus until it is dismissed (a filter panel opened from a button, a command palette)? Use the library's useFocusTrap rather than a hand-rolled Tab handler: it finds the tab stops the browser visits and hands focus back on close. references/api.md has the signature and a worked panel.
The component spine
Same shape the library itself uses, with all imports from the package. Every reusable component gets it — including pure compositions with zero CSS (a heading block, a labeled wrapper): extend BulmaClassesProps, run your props through useBulmaClasses, merge its bulmaHelperClasses into className, and spread the rest it returns — spreading the raw props instead leaks helper props onto the DOM and emits none of their classes. The usePrefixedClassNames root class is needed only when component-scoped CSS (or a variant class) targets it — a zero-CSS composition may omit that call. File at src/components/MyComponent.tsx:
import type React from 'react';
import {
classNames,
usePrefixedClassNames,
useBulmaClasses,
type BulmaClassesProps,
} from '@allxsmith/bestax-bulma';
export interface MyComponentProps
extends
Omit,
Omit {
color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
}
export function MyComponent({
color,
className,
children,
...props
}: MyComponentProps) {
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
const mainClasses = usePrefixedClassNames('mycomponent', {
[is-${color}]: !!color,
});
return (
{children}
);
}
This gives your component the full Bulma helper-prop surface (m, p, textAlign, …) for free. references/api.md documents the helpers.
Styling ladder — use the lowest rung that works
Rung 1 — helper props only (default). House rules: never style={{}}. Layout with Block/Box and display="flex", flexDirection, alignItems, justifyContent. Flex layouts have no gap helper — space children with m*/p* margins instead (Grid and Columns take a gap prop). Before writing style={{ … }} anywhere, translate each declaration:
Inline style you're about to write Helper props instead
marginTop: '1rem' (any margin/padding) mt="4" — m/mt/mx/p/py/… scale: 1=0.25rem, 2=0.5rem, 3=0.75rem, 4=1rem, 5=1.5rem, 6=3rem (nearest step)
textAlign: 'center' textAlign="centered" (also left, right, justified)
color: '#…' textColor with the nearest Bulma color: primary, link, info, success, warning, danger, white, black, grey (+ grey-light, grey-dark, …)
backgroundColor: '#…' bgColor (same palette)
fontSize: … textSize="1"…"7" (1 largest) — for headings use Title/SubTitle size
fontWeight: … textWeight: light, normal, medium, semibold, bold
textTransform, italics textTransform: uppercase, lowercase, capitalized, italic
display: 'flex' + flex properties same-named props: display="flex", flexDirection, justifyContent, alignItems, flexWrap
height: '100%' on a flex child flexGrow="1"
display: 'none' visibility="hidden", or responsive display* props (displayMobile, displayTablet, …)
Spacing, typography, and flex helpers are on every component; textColor/bgColor are on the content components you'll compose with (Box, Block, Title, Content, Card, …) — the ones with a semantic color variant (Tag, Tabs, Panel) take color instead. Notification is the mixed case: it takes textColor, but its background comes from the semantic color prop, not bgColor.
A value with no helper equivalent (maxWidth: 720, a one-off gradient) moves you to rung 2 — a named class in a stylesheet — never to inline style.
Rung 2 — a plain CSS file, scoped under the component's class, consuming --bulma-* variables — never literal colors, so Theme and dark mode keep working:
/* src/components/MyComponent.css — import from the .tsx file /
.mycomponent {
/ component-scoped custom props, initialized from Bulma tokens:
any ancestor (or Theme) can re-theme by overriding them */
--mycomponent-radius: var(--bulma-radius);
--mycomponent-accent: var(--bulma-primary);
border-radius: var(--mycomponent-radius);
border: 1px solid var(--bulma-border);
background: var(--bulma-scheme-main);
color: var(--bulma-text);
}
.mycomponent .mycomponent-value {
color: var(--mycomponent-accent);
}
Caveat: with the prefixed CSS flavor / ConfigProvider classPrefix, usePrefixedClassNames prefixes your classes too — your CSS selectors must match (or build them with plain classNames instead).
Rung 3 — real Sass (optional). npm i -D sass — nothing else; Vite compiles imported .scss zero-config, and bulma is resolvable because it's a runtime dependency of bestax-bulma. Then the full register-vars/getVar pattern from references/library-contributor.md works in-app. Prefixed flavor: @use 'bulma/sass/utilities/initial-variables' with ($class-prefix: 'bestax-').
Verify in the browser
Types don't see layout. Run npm run dev, render the component, and actually look at it: vertical centering of inline text (use display="flex" alignItems="center", not line-height hacks), balanced padding, nothing clipping, every color/size variant, and dark mode legibility. Fix what you see, then re-check. No browser available (headless)? Fall back to npm run build plus a Node renderToString smoke render, grep the emitted HTML for the expected classes, and flag the visual pass as not done.
Tests and stories in an app
The scaffolded app has no test runner and no Storybook — do not install or scaffold them unasked. If the app already has vitest/jest + Testing Library, write the four test shapes: render, prop→class mapping, helper-prop passthrough (m="3" → m-3), and the ConfigProvider classPrefix case if the app uses a prefix.
Checklist
Inventory checked (catalog + bestax.io/docs/api) and the decision surfaced to the user.
All imports from @allxsmith/bestax-bulma (no deep/internal paths).
Composition first — existing components + helper props before any CSS; every reusable component gets the spine.
No inline style={{}} anywhere — translate via the rung-1 mapping table; values with no helper equivalent get a named class (rung 2).
Lowest sufficient ladder rung (helper props → scoped CSS vars → Sass).
All colors/radii derived from --bulma-* variables — no literals.
Renders correctly via npm run dev, including dark mode.
Bestax 布局脚手架
Bestax 表单构建