marko-ui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesemarko-ui
marko-ui
marko-ui is shadcn/ui for Marko 6: accessible components whose behavior comes from Zag.js state machines and whose styling is Tailwind v4. Use the CLI for everything below — do not hand-write a component that the registry already ships, and do not guess a component's props.
marko-uiRun the CLI with the project's package runner (, , ). Every command is non-interactive when stdin is not a TTY: confirmations are taken as yes, uses its defaults, and with no component names exits 2 instead of opening a picker.
bunx marko-uinpx marko-uipnpm dlx marko-uiinitaddmarko-ui 是面向 Marko 6 的 shadcn/ui:可访问组件,其行为由 Zag.js 状态机驱动,样式基于 Tailwind v4。对于以下所有操作,请使用 CLI——不要手写 registry 已经提供的组件,也不要猜测组件的 props。
marko-ui使用项目的 package runner 运行 CLI(、、)。当 stdin 不是 TTY 时,所有命令都会以非交互方式运行:确认操作一律视为“是”, 使用默认配置, 未指定组件名称时会以退出码 2 退出,而不是打开选择器。
bunx marko-uinpx marko-uipnpm dlx marko-uiinitaddFirst: which distribution is this project on?
首先:该项目使用的是哪种分发方式?
Read in (absent means ). It changes how components are obtained and customized.
distributioncomponents.jsoncopy | | |
|---|---|---|
| Components live in | the project, under the | |
| Get a component | | nothing to add — import |
| Customize | edit the component file (it is the project's code) | override |
No means marko-ui is not set up: run (add to also write the agent docs).
components.jsonmarko-ui init--agents读取 中的 (不存在则默认为 )。它决定了组件的获取和自定义方式。
components.jsondistributioncopy | | |
|---|---|---|
| 组件存放位置 | 项目中 | |
| 获取组件 | | 无需添加——import |
| 自定义 | 编辑组件文件(它属于项目代码) | 从项目 stylesheet 中覆盖 |
如果不存在 ,说明 marko-ui 尚未配置:运行 (添加 还会写入 Agent 文档)。
components.jsonmarko-ui init--agentsWorkflow
工作流
- — find a component (
marko-ui search -q <query>for machine output). The installed ones are listed in the project's--json.AGENTS.md - — usage, props, keyboard contract, and examples as markdown. Read this before writing markup for a component you have not used in this session.
marko-ui docs <name>prints the index.marko-ui docs --list - — the registry item as JSON;
marko-ui show <name>lists what would be written,--fileslists npm and registry dependencies.--deps - — install (copy distribution).
marko-ui add <name> -ypreviews the file changes;--dry-runreplaces local files.--overwrite - — compare local edits against the registry version.
marko-ui diff <name> - — health checks; exit code 3 means something is broken and each failed check names its fix. Run it after
marko-ui doctor --json/initand before reporting the work done.add - — refresh the component list in
marko-ui agents syncafter adding or removing components.AGENTS.md
marko-ui manifest- ——查找组件(
marko-ui search -q <query>用于机器可读输出)。已安装的组件列在项目的--json中。AGENTS.md - ——以 markdown 形式提供用法、props、键盘交互约定和示例。在本次会话中尚未使用过的组件编写 markup 前,请务必先阅读此文档。
marko-ui docs <name>可打印索引。marko-ui docs --list - ——以 JSON 格式显示 registry 条目;
marko-ui show <name>列出将要写入的文件,--files列出 npm 和 registry 依赖项。--deps - ——安装组件(copy 分发方式)。
marko-ui add <name> -y可预览文件变更;--dry-run可替换本地文件。--overwrite - ——将本地修改与 registry 版本进行比较。
marko-ui diff <name> - ——执行健康检查;退出码 3 表示存在损坏项,每项失败的检查都会指出对应的修复方法。应在
marko-ui doctor --json/init后以及汇报工作完成前运行。add - ——添加或移除组件后,刷新
marko-ui agents sync中的组件列表。AGENTS.md
需要准确了解完整的命令接口时,可运行 ,它会以 JSON 格式输出每个命令、flag、退出码和错误码。
marko-ui manifestUsing components
使用组件
marko
import Dialog from "../components/ui/dialog/dialog.marko";
import Button from "../components/ui/button/button.marko";
<let/open=false/>
<Dialog open:=open>
<@trigger|props|>
<Button ...props>Edit profile</Button>
</@trigger>
<@title>Edit profile</@title>
<@description>Make changes to your profile here.</@description>
<p>Form fields go here.</p>
</Dialog>- Parts are attribute tags (,
<@trigger>), not separately imported subcomponents.<@title>lists each component's parts.marko-ui docs <name> - hands you the machine props to spread onto whatever element you render. It is the equivalent of Radix's
<@trigger|props|>.asChild - State binds with (
:=,open:=open,value:=value), or pass the value plus itschecked:=checkedhandler (xChange).open=open openChange=(v) => { open = v } - On the copy distribution also generates a taglib (
add), unless the project already has its ownmarko.json, so installed components are usable as tags without an import (marko.json,<Button>). An explicit import always works too.<CardHeader> - Never import a component as . It shadows Marko's reserved props type. The input component is imported as
Input.TextInput
marko
import Dialog from "../components/ui/dialog/dialog.marko";
import Button from "../components/ui/button/button.marko";
<let/open=false/>
<Dialog open:=open>
<@trigger|props|>
<Button ...props>Edit profile</Button>
</@trigger>
<@title>Edit profile</@title>
<@description>Make changes to your profile here.</@description>
<p>Form fields go here.</p>
</Dialog>- 组成部分是 attribute tags(、
<@trigger>),而不是单独导入的子组件。<@title>会列出每个组件的组成部分。marko-ui docs <name> - 会将 machine props 交给你,以便 spread 到所渲染的任何元素上。它相当于 Radix 的
<@trigger|props|>。asChild - 状态使用 绑定(
:=、open:=open、value:=value),或者传入该值及其checked:=checkedhandler(xChange)。open=open openChange=(v) => { open = v } - 在 copy 分发方式下,还会生成一个 taglib(
add),除非项目已有自己的marko.json;这样已安装的组件无需 import 即可作为 tag 使用(marko.json、<Button>)。显式 import 也始终有效。<CardHeader> - 绝不将组件导入为 。 这会遮蔽 Marko 的保留 props 类型。input 组件应导入为
Input。TextInput
Pitfalls that break components silently
导致组件静默失效的陷阱
Each of these has caused a real bug. Check your code against the list before finishing.
- Controlled props pin the machine. /
open=/value=WITHOUT the matchingchecked=/openChange/valueChange(or thecheckedChangebind) freezes the component in that state. For an initial value only, use:=/defaultOpen=/defaultValue=.defaultChecked= - Boolean /
aria-*attributes serialize wrong. Marko renders booleandata-*as a bare attribute:trueemitsaria-selected=(bool)andaria-selected=""never matches adata-active=(bool)Tailwind variant. Wrap indata-[active=true]:.String(...) - Nothing from Zag crosses a tag boundary or lives in state. Machine services and objects contain functions and cannot be serialized for resume. Pass plain data, or a closure written in the template (
api), and callparentApi=() => api()at the use site.api() - Do not read a parent component's inside a child's attribute-tag body. Route it through the child's input as a closure instead. The symptom is
api()after an overlay opens.TypeError: ... is not a function - Overrides that do nothing (import distribution). The style layer must be imported with and the matching
layer(components)line must exist in the project's stylesheet. Both fail silently.@custom-variant style-<name> - Unstyled components (copy distribution). The theme stylesheet is not loading. Check that the file recorded as in
tailwind.cssis imported by the root layout and thatcomponents.jsonis in the Vite config.@tailwindcss/vitewires both; a hand-rolled setup often misses one.marko-ui init - or
"Cannot find module @zag-js/...". A component's npm dependencies are missing.marko-zaginstalls them;marko-ui addlists them.marko-ui show <name> --deps
For Marko 6 language rules (, , , event handlers, TypeScript in templates) use the skill; for routing use .
<let><const><for>marko6@marko/runmarko-run以下每种情况都曾引发过真实的 bug。完成前请对照清单检查代码。
- 受控 props 会锁定 machine。 传入 /
open=/value=,但未提供对应的checked=/openChange/valueChange(也未使用checkedChange绑定),会将组件冻结在该状态。如果只需设置初始值,请使用:=/defaultOpen=/defaultValue=。defaultChecked= - 布尔 /
aria-*attributes 的序列化结果错误。 Marko 会将布尔值data-*渲染为无值 attribute:true会输出aria-selected=(bool),而aria-selected=""永远无法匹配data-active=(bool)Tailwind variant。请使用data-[active=true]:包装。String(...) - Zag 中的任何内容都无法跨越 tag 边界或存在于 state 中。 machine services 和 对象包含函数,无法序列化后用于 resume。请传入纯数据,或传入在 template 中编写的 closure(
api),并在使用位置调用parentApi=() => api()。api() - 不要在子组件的 attribute-tag body 中读取父组件的 。 应改为通过子组件的 input 以 closure 形式传入。否则,overlay 打开后会出现
api()。TypeError: ... is not a function - 覆盖样式不起作用(import 分发方式)。 样式层必须使用 导入,并且项目 stylesheet 中必须存在匹配的
layer(components)行。两者缺失时都会静默失败。@custom-variant style-<name> - 组件没有样式(copy 分发方式)。 theme stylesheet 未加载。检查 中记录为
components.json的文件是否已由 root layout 导入,以及 Vite config 中是否包含tailwind.css。@tailwindcss/vite会配置好这两项;手动搭建时经常会漏掉其中一项。marko-ui init - 出现 或
Cannot find module @zag-js/...。 组件缺少 npm dependencies。marko-zag会安装这些依赖;marko-ui add会列出它们。marko-ui show <name> --deps
对于 Marko 6 语言规则(、、、event handlers、模板中的 TypeScript),请使用 skill;对于 路由,请使用 。
<let><const><for>marko6@marko/runmarko-runEditing a component (copy distribution)
编辑组件(copy 分发方式)
The file is project code. Each file starts with a block holding the Tailwind classes per part: change classes there. Interactive components wire their machine with one tag, , which picks the machine's props out of and returns the getter; keyboard handling, focus management, and ARIA come from the machine, so restyling cannot break accessibility. After editing, shows what diverged from the registry.
.markostatic const styles = { ... }<zag/api=() => machine from=input/>inputapimarko-ui diff <name>该文件属于项目代码。每个 文件都以 代码块开头,其中按组成部分保存 Tailwind classes:请在该处修改 classes。交互式组件通过单个 tag 绑定 machine,它会从 中提取 machine 的 props,并返回 getter;键盘处理、焦点管理和 ARIA 均来自 machine,因此调整样式不会破坏可访问性。编辑后, 会显示与 registry 版本之间的差异。
.markostatic const styles = { ... }<zag/api=() => machine from=input/>inputapimarko-ui diff <name>More
更多信息
- Docs: https://marko-ui.saulo.tech/docs (component pages as markdown: )
/docs/components/<name>.md - Writing a new component: https://marko-ui.saulo.tech/docs/creating-components
- Agent setup and troubleshooting: https://marko-ui.saulo.tech/docs/working-with-ai
- 文档:https://marko-ui.saulo.tech/docs(组件页面的 markdown 格式:)
/docs/components/<name>.md - 编写新组件:https://marko-ui.saulo.tech/docs/creating-components
- Agent 设置与故障排查:https://marko-ui.saulo.tech/docs/working-with-ai