soybean-ui-develop
GitHubSoybeanUI组件开发技能,规范Vue3+TS环境下无头与样式分离架构的组件创建、迁移、扩展及审计流程。
Trigger Scenarios
Install
npx skills add soybeanjs/soybean-ui --skill soybean-ui-develop -g -y
SKILL.md
Frontmatter
{
"name": "soybean-ui-develop",
"description": "Builds, updates, and audits SoybeanUI components with headless\/UI split, delivery phases, and generation workflow. Invoke when adding, migrating, extending, standardizing, fixing, or auditing components, or when work touches packages\/headless\/src\/components, packages\/ui\/src\/components, apps\/docs\/src\/examples, apps\/docs\/src\/content, or packages\/ui\/test\/specs\/components."
}
SoybeanUI Component Development
This skill is the single, self-contained source of truth for SoybeanUI component work. It owns pattern classification, phase order, layer rules, delivery surfaces, generation workflow, and the finish-stage checklist. Load it for any task that creates, migrates, extends, standardizes, fixes, or audits a SoybeanUI component.
For detailed rules, see layers.md (headless admission, implementation layers), surfaces.md (delivery surfaces), e2e.md (browser e2e testing), process.md (finish checklist and commit convention), and audit.md (assessment methodology, seven check dimensions, and regression flows for already-shipped components). For request shapes that trigger this skill, see EXAMPLES.md. TypeScript functional style and Vue SFC structure are owned by the global typescript-functional-style and vue-sfc-structure skills — load them directly; this skill does not restate their content.
Repository context
- Monorepo: pnpm workspaces (private root + 13 child workspaces: 10 packages + 2 apps +
skills/) - Stack: Vue 3 + TypeScript (strict) + UnoCSS +
@soybeanjs/cva - Architecture: headless / styled separation. Compile-time dependency is
packages/ui->packages/headless; headless never imports UI. @soybeanjs/headless(packages/headless/): Logic, state, a11y, structure. Zero styles.@soybeanjs/ui(packages/ui/): Styled wrappers. UnoCSS +cv()/scv().S-prefixed components.@soybeanjs/ui-docs(apps/docs/): ubean SSG (mode: 'ssg') with file routing, content collections, and built-in i18n (en+zh). NOT VitePress.
Quick start
- Classify the task before editing.
- Headless admission: behavioral family, UI-only (refused), or compose an existing primitive. See layers.md -> Headless admission.
- Component pattern: multi-slot base, compact aggregation, or single-class.
- Scenario: new component, migration or normalization, standards alignment, or audit and evaluation.
- Delivery scope: headless only, UI only, or full surface.
- Find concrete local references.
- Inspect at least one neighboring same-pattern headless implementation and one UI implementation before editing.
- For migration, preserve behavior, state, accessibility, slots, and public API before restructuring.
- Reuse existing building blocks first.
- Prefer
packages/headless/src/composables/,packages/headless/src/shared/, andpackages/headless/src/types/. - Use
@vueuse/corecomposables as-is; do not reimplement them in-repo. Date math goes throughpackages/headless/src/date/; positioning, virtualization, carousel, and schema-standard integrations use the existing runtime deps (@floating-ui/dom,@tanstack/vue-virtual,embla-carousel,@standard-schema/spec); drag/drop and motion are self-maintained in-repo (composables/use-sortable-list.ts,shared/collapse-motion.ts). - Only add a new composable, helper, type, or third-party dependency when
shared/composables/types, existing runtime deps, and@vueuse/coreare all insufficient — and state that reason in the result.
- Prefer
Example: "migrate a compound widget into SoybeanUI" usually means migration scenario + multi-slot or compact pattern + full delivery surface.
Example: "audit the dialog component against industry baselines" means audit scenario + follow audit.md for the assessment flow, seven check dimensions, and regression rules.
Component patterns
Multi-slot base component
- Headless exposes multiple slot primitives.
- Uses
UiSlotandUiClass<UiSlot>. - UI injects classes through
provide{Name}Ui(ui). - Examples: accordion, dialog.
Compact aggregation
- Use when structure is stable and data-driven.
- Headless owns iteration, default content, structure, and non-style orchestration via
{Name}Compact. - UI wrappers stay thin: only variants, class injection, prop/listener forwarding, and slot forwarding.
- Exposes
{Name}CompactProps/{Name}CompactEmits/{Name}CompactSlots. - Current examples: accordion, card, date-field, dialog, editable, hover-card, layout, navigation-menu, pagination, popover, stepper, table flows.
Single-class component
- No UiContext.
- UI composes classes directly from variants and
props.classvia{name}Variants({...}, props.class). - Examples: button, link.
Workflows
New or migrated component
- Admit or refuse headless. Apply layers.md -> Headless admission. If refused, skip step 1 and implement UI-only or compose an existing family.
- Build headless first (admitted families only).
types.ts->context.ts-> base slot SFCs -> optional{Name}Compact->index.ts.
- Build UI second.
packages/ui/src/styles/{name}.ts->types.ts-> wrapper.vue->index.ts.
- Complete exports and generated surfaces.
- Update
packages/headless/src/index.tsandpackages/ui/src/index.ts. - Run
pnpm sui gen catalog. - Do not hand-edit generated files.
- Update
- Complete delivery surfaces unless the user explicitly narrows scope.
apps/docs/src/examples/ui/{component}/apps/docs/src/content/en/ui/components/{component}.mdapps/docs/src/content/zh/ui/components/{component}.mdapps/docs/src/constants/menus.tspackages/ui/test/specs/components/{component}.spec.ts- Run
pnpm sui gen apiwhen public API changes; for non-English locales runpnpm sui translate api. - Run
pnpm sui gen changelogwhen changelog mapping or release-facing surfaces change; for non-English locales runpnpm sui translate changelog.
Existing component fix or extension
- Decide whether the change belongs to headless logic or UI wrapping.
- Preserve the boundary:
- No styles in headless (not even
hidden,sr-only). - No ARIA,
role,tabindex, or keyboard semantics in UI. - No reverse dependency from
packages/headlesstopackages/ui.
- No styles in headless (not even
- Check whether playground, docs, tests, exports, or generated API data must move with the change.
- If a new composable, helper, or type is introduced, explain why existing repository utilities and
@vueuse/corewere insufficient.
Audit and evaluation of shipped components
- Load audit.md for the full assessment methodology, seven check dimensions (D1–D7, 106 items), severity levels, acceptance states, and regression rules.
- Run the eight-step assessment flow from audit.md -> Assessment flow. Grade every finding by severity; do not start the next component while a Blocker is open.
- For D2 industry benchmarking, follow audit.md -> Industry benchmarking (six libraries) and record concrete findings.
- Apply the audit.md -> Single-component acceptance checklist before marking the component passed.
- After each category, run the cross-component consistency regression from audit.md -> Cross-component consistency regression. After all components, run the full regression.
Phase order
Execute in this order. Do not skip ahead until the current phase is done.
Phase 0: Classify pattern, scenario, and scope
- Determine component pattern, task scenario, and whether this is full delivery.
- For a new family: apply layers.md -> Headless admission before
types.ts. If the family is not admitted, implement UI-only or compose an existing family. - Do not write code first and reverse-engineer the pattern.
Phase 1: Find reference implementations
- Inspect at least one same-pattern headless reference and one UI reference.
- Migration (scenario B): catalog behavior, state, a11y, slots, and public API that must be preserved.
- Standards alignment (scenario C): list the gap inventory.
- Audit and evaluation (scenario D): the gap inventory is the assessment itself — follow audit.md instead of the implementation phases below.
Phase 2: Implement headless
- Skip this phase when admission refused the family (UI-only or compose existing).
- Default order:
types.ts->context.ts-> base slot SFCs -> optional{Name}Compact->index.ts. - See layers.md -> Headless for layer rules.
Phase 3: Implement UI
- Default order:
packages/ui/src/styles/{name}.ts->types.ts-> wrapper.vue->index.ts. - See layers.md -> UI layer for layer rules.
Phase 4: Wire exports and generated files
- Update
packages/headless/src/index.tsandpackages/ui/src/index.ts. - Run
pnpm sui gen catalog. - Do not hand-edit generated files.
Phase 5: Complete delivery surfaces
- Playground (examples): see surfaces.md -> Playground (examples).
- Docs: see surfaces.md -> Docs.
- Tests: see surfaces.md -> Testing. For interactive components, also add a browser e2e spec — see e2e.md.
- If public API changed, run
pnpm sui gen api; for non-English locales runpnpm sui translate api. - If changelog mapping or release surfaces changed, run
pnpm sui gen changelog; for non-English locales runpnpm sui translate changelog.
Phase 6: Validate and finish
- Run
pnpm typecheck,pnpm lint,pnpm fmt, and the narrowest relevant test:pnpm vitest packages/ui/test/specs/components/{component}.spec.ts. - If a command cannot run, state the blocker explicitly.
- Apply process.md -> Finish checklist only now.
Boundary rules
- Compile-time dependency must remain
packages/ui->packages/headless. Never import@soybeanjs/uifrompackages/headless. - Headless owns logic, state, accessibility, structure aggregation, and default semantics. Structure aggregation does not by itself admit a new family — see layers.md -> Headless admission.
- UI owns variants, UnoCSS classes,
uiinjection, and wrapper composition. - Do not add visual token styles or utility classes in headless (not even
hidden,sr-only). Geometric layout-contract inline styles are allowed (admission R8). - Do not put ARIA,
role,tabindex, keyboard interaction, or state semantics inpackages/ui/src/components. - Do not hand-edit generated files; update source exports and rerun scripts.
- Avoid adding new helpers before checking existing repository utilities and
@vueuse/core.
Guardrails
- Use
UiClass<UiSlot>(frompackages/headless/src/types), notRecord<UiSlot, ClassValue>. - Props always
extends /** @vue-ignore */ HTMLAttributesto suppress IDE noise; if based onPrimitive,extends PrimitiveWithBaseProps. - Context values must be reactive:
ComputedReforShallowRef. UsetoContext(props, keys)(from headlessshared/vue) for prop-derived fields;fromContext(context, keys)snapshots back to plain values when needed. use{Name}Ui('root')->ComputedRef<ClassValue>(single slot);use{Name}Ui()-> full map.- For multi-slot wrappers, pass
props.uiand{ root: props.class }directly into thescv()recipe call. - Multi-slot: only export
provide{Name}Uifrom headless barrel; never exportuse{Name}Ui. packages/ui/src/styles/{name}.tsfirst line must be// @unocss-include. Shared class tokens live in_*.tsfiles (_field.ts,_overlay.ts); recipes compose those tokens or inherit a sibling recipe viaextend/alias/extendBase.- Headless families built on another family alias inner slots that have no domain semantics, and wrap inner primitives in a domain SFC when the slot has a11y, context, UI, or
data-soybean-{family-slot}of its own. See layers.md -> Composing an existing family. Compact only assembles; it does not define the contract of a publicly exported primitive. slotskeys in style recipe must match headless{Name}UiSlotexactly.useOmitPropsmust includeclassto avoid double-binding.- UI component names use
Sprefix (SButton,SDialog); headless names do not (Button,Dialog). - Never use
as any/@ts-ignore/@ts-expect-error. - Never modify
typed-router.d.ts(auto-generated).
Full delivery surface
New components check every entry that applies; existing component changes sync affected entries. Headless paths apply only when the family passed Headless admission; UI-only components skip the headless directory and barrel.
packages/headless/src/components/{component}/(admitted families)packages/ui/src/components/{component}/packages/headless/src/index.ts(admitted families)packages/ui/src/index.tsapps/docs/src/examples/ui/{component}/apps/docs/src/content/en/ui/components/{component}.mdapps/docs/src/content/zh/ui/components/{component}.mdapps/docs/src/constants/menus.tspackages/ui/test/specs/components/{component}.spec.ts
Generated surfaces
Run these after the corresponding source changes. Never hand-edit generated outputs.
pnpm sui gen catalog— regenerates the component catalogs:packages/headless/src/constants/components.ts,packages/headless/src/namespaced/index.ts(frompackages/headless/src/index.ts), andpackages/ui/src/constants/components.ts(frompackages/ui/src/index.ts). Passheadlessoruito regenerate only one.pnpm sui gen api— regeneratesapps/docs/src/generated/api/*.jsonandapps/docs/src/generated/api-locales/*.jsonbase data.pnpm sui gen changelog— regeneratesapps/docs/src/generated/changelog/*.jsonandapps/docs/src/generated/changelog-locales/*.jsonbase data.pnpm sui gen schema/pnpm sui gen skills— regenerates the sbean JSON Schemas / the skills distribution.pnpm sui gen all— regenerates every surface above.pnpm sui translate <api|changelog|locale|all>— refreshes the surface it translates and then fills pending entries through DeepL (--locale,--dry-run,--limit,--overwrite). This is the only command that needsDEEPL_API_KEY.pnpm sui check generated— regenerates every surface and diffs it against git; CI runs it, so committed generated data cannot drift unnoticed.pnpm sui check deps— dependency gate: banned imports plus the headless/ui runtime dependency whitelists.
Generation is content-aware: a document whose payload did not change keeps its committed generatedAt and is not rewritten, so a no-op run leaves git status clean. On top of that, gen api skips the TypeDoc extraction entirely (~40s → ~0.15s) when a fingerprint of its inputs and its on-disk output still matches the last run; the fingerprint lives in node_modules/.cache/sui/. --force bypasses it.
Build and validation commands
pnpm dev:docs # Documentation site (ubean dev)
pnpm build # libs (theme, ui-uno) -> headless -> ui -> sbean via Vite Plus pack
pnpm build:libs # theme -> ui-uno
pnpm lint # vp lint --fix && package Vue lint
pnpm fmt # vp fmt
pnpm test # recursive workspace tests (pnpm -r run test)
pnpm test:e2e # Vitest Browser Mode + Playwright Chromium
pnpm typecheck # recursive workspace type checks
pnpm release # Publish packages (soy release)
pnpm stub # switch headless exports to src (`--reset` restores dist)
- Pre-commit hook (Vite Plus,
.vite-hooks/pre-commit):vp staged. - CI: PR/push typecheck, lint, unit tests, and browser e2e; tag-triggered build/release.
- Formatter: Vite Plus
vp fmt.
Result reporting
In the final handoff, make the following explicit when relevant:
- Component pattern, scenario, and scope.
- Which delivery surfaces were changed or intentionally left unchanged.
- Which generation and validation commands were run.
- Any blocker that prevented validation or full delivery.
- Why a new composable, shared helper, or type was introduced instead of reusing repository utilities or
@vueuse/core.
Version History
-
294c46a
Current 2026-09-27 22:09
移除v0.50文档及重构指南,将无头组件准入规则整合至layers.md并刷新审计维度。
- c080112 2026-09-22 10:21


