Communitygithub.com

mildastudio/milda

读取、编写、审查和修复 Milda 设计系统 – `.milda` 文件 – 并以 Milda 范式(分层令牌、上下文组、原型、平台无关部件、行为驱动的状态)进行推理。当用户提及 Milda、Milda Studio、Milda Playground、`.milda` 文件,或者要求用 Milda 创建或修改设计系统的令牌/组件,或者希望将 CSS、Figma 或现有组件库转换为 Milda 时,请使用本技能。

milda 是什么?

Milda 是一个带图形用户界面的设计系统编译器。核心概念是将设计系统完整地定义在一个 `.milda` 文件中——包括基础令牌、组件、使用规则、文档和资源。然后,Milda 会根据定义自动生成 React 代码、Figma 库、文档站点以及可供 AI 代理读取的文件。开发者仅需修改 `.milda` 文件并重新生成,绝不应手动编辑任何输出代码。该技能覆盖的语言版本为 0.10,预置库版本为 0.0.2。每次编辑 `.milda` 文件后,必须运行 `milda-check --strict` 工具检查错误,直到报告 `0 errors, 0 warnings`。Milda 抛弃了 CSS、DOM 和框架概念,采用平台中立的语法:部件使用 `container`、`text`、`input` 等抽象种类代替 `div`、`button`;状态来源于预定义的交互行为(如 `hovered`、`disabled`、`focus-visible`),而非 CSS 伪类;所有颜色、间距等视觉属性必须引用命名令牌,并通过上下文组(如暗色模式/高密度)实现变体切换。组件应通过原型定义其结构、属性和事件,并利用实例组合复用现有组件。Milda 的设计哲学是“设计系统即源码”,所有输出都是生成结果。该技能支持在命令行和 Milda Playground(在线 playground)中使用,可连接 Milda MCP 服务器查询已发布的设计系统。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/mildastudio/milda-skills/tree/main/skills/milda

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

milda 是做什么的?

Milda 是一个带图形用户界面的设计系统编译器。一个 .milda 文件就是这个设计系统:包含基础(令牌)、组件、使用规则、文档和资源。React 包、Figma 库、文档站点以及 AI 代理可读的文件都由它生成。你永远不需要编辑输出;你只需修改文件并重新生成。

本技能针对0.10版本的语言表层及0.0.2版本的预置库。你编写的每个文件都以该文件头开头。

循环:编写、检查、修复

在验证器同意之前,不要告诉用户 .milda 文件是有效的。每次编辑后,运行:

npx -y -p @mildastudio/milda-lsp milda-check --strict path/to/system/

将目录或所有文件一起传递给命令,因为一个文件中声明的令牌可能会解析另一个文件中的引用。它会输出 path:line:col severity [code] message,与编辑器扩展显示的诊断信息相同,并且在遇到任何错误时会以非零退出(使用 --strict 时,遇到任何警告也会)。修复报告的问题并再次运行,直到它报告 0 errors, 0 warnings。警告不是噪音:unknown facet "background" skipped 意味着那一行什么也没做。

要向用户展示结果,位于 https://play.milda.dev 的 Playground 可以实时渲染粘贴的文件,无需账户。

用 Milda 思考:十条规则

大多数错误源于用 CSS、DOM 树或 React 组件的思维来写 Milda 语法。这些规则解释了该语法存在的目的。

  1. 系统是源头;代码是输出。 所有与平台相关的东西都在生成时决定,因此文件中永远不会出现 CSS 属性、标签、框架的事件名称或伪类。
  2. 描述事物是什么,而不是由什么构成。 部件具有中性的 种类(container、text、input、icon、control……),绝不用 div 或 button。每个目标平台会选择自己的元素。
  3. 值有名称,名称有层级。 组件永远不会直接使用字面量。一个方面指向一个令牌;Semantic 层是 Base 层(字面量层)的别名。重新样式就是改变基础层。
  4. 语境是一个维度,而不是一份副本。 暗色模式、密度、品牌:每个都是一个 context-group,一个令牌通过 alias by Group { ... } 按语境给出答案。由语境组类型化的属性(variant)在每个实例上使用相同的机制。
  5. 状态来自行为。 hovered、pressed、focus-visible、disabled、open、selected 由原型的生命周期行为发出。用 state hovered { } 来样式化它们,绝不要使用任何与指针或平台相关的东西。该列表是封闭的(见 reference/vocabulary.md),验证器会警告任何其他词语:比如 state hover 会被报告为拼写错误。
  6. 合约即身份。 props 和 events 是消费者编写代码的依据,也是版本化差异的依据。为它们赋予有意义的名称(如 variant、checkedChange),而不是平台相关的名称。
  7. 从已有的东西构建。 从预置库中的 archetype 开始创建组件:它会带来合约形状、必需的部件和行为(键盘、ARIA、焦点)。通过 instance 部件将较小的组件组合成更大的组件。
  8. 每个引用都通过名称。 部件使用点分隔路径(Header.Title),令牌使用路径(space.sm 表示 space/sm),组件使用标识符。没有 ID。注释(#)是唯一无法在轮换中保留的东西。
  9. 通过接缝扩展,而不是修补输出。 当词汇表无法表达某些内容时,使用 slot、按目标选项,或者作为最后手段,使用标记了目标的 foreign 内容。永远不要建议编辑生成的代码。
  10. 说明它的预期用途。 在 props 中放置接口约束(required、required when、one-of),在顶层放置组合规则(rule ... { never X within Y }),每条规则都要说明原因。

一个文件,从上到下

milda 0.10
prelude 0.0.2

meta { name "Acme" }

context-group ColorScheme { Light Dark }

foundations {
  layer Base {
    token violet/600 : color = hex(7c3aed)
    token white : color = hex(ffffff)
    token gray/900 : color = hex(0f172a)
  }
  layer Semantic {
    token accent : color = alias by ColorScheme { default -> violet.600 }
    token on-accent : color = alias by ColorScheme { default -> white; Dark -> gray.900 }
    token space/sm : spacing = 8px
    token radius/sm : radius = 6px
  }
}

component Button "主操作按钮。" {
  archetype Button

  props {
    label : string = "Get started"
  }

  events {
    click
  }

  part Button: container {
    emits { click on activate }
    layout { stack row; gap space.sm; align center }
    fill accent
    ink on-accent
    corner radius.sm
    inset space.sm
    state focus-visible {
      ring accent
    }
  }

  part label: text {
    content bind label
  }
}

顺序:文件头、meta、context-group、group(库分区)、foundations、type、component、rule,然后是 example、assets、icons、docs。根部件以组件命名;其他部件根据原型的解剖结构(label、icon、control……)命名,或者由你使用点分隔路径嵌套命名。

从 CSS 和 React 到 Milda

你通常写的方式Milda 的说法
<div>, <span>, <button>part X: container / text / 原型的根部件
backgroundfill
colorink
padding, padding-inline-startinset, inset.inline-start
margin无:使用父级的 gap、对齐或 bleed
border, border-radiusborder <color> <width>, corner
box-shadow, outline (焦点)elevation, ring
font-size + line-height, font-weighttext <typography or font-size token>, weight
display: flex; flex-direction: rowlayout { stack row }
justify-content, align-items, flex: 1distribute, align, size fill
position: absolutelayout { stack layers } + 在子级上使用 position overlay
:hover, :focus-visible, :disabledstate hovered { }, state focus-visible { }, ...
.btn--secondary, [data-variant]when variant = Secondary { }
.card:hover .icon在图标部件上使用 when ancestor Card hovered { }
@media (prefers-color-scheme: dark)context-group ColorScheme + alias by ColorScheme
--space-sm: 8px / var(--space-sm)token space/sm : spacing = 8px / space.sm
#7c3aed, oklch(...)hex(7c3aed), oklch(...),仅用在令牌内部
color-mix(), calc()compute <token> in oklch { darken 10% }
onClick propevents { click } + emits { click on activate }
{children}在容纳它们的部件上使用 slot of content default
{cond && <X/>}在部件上使用 present when <prop>
z-index, text-align, box-sizing无:部件顺序、容器,只有一个盒子

部件上的方面、状态和条件

  part Panel: container {
    layout { stack column; gap space.sm; size fill fit }
    fill surface
    ink text
    border line hairline
    corner radius.sm
    inset space.sm space.md
    when tone = danger {
      border danger hairline
    }
    state hovered {
      fill surface-raised
    }
    state hovered when tone = danger {
      fill danger-subtle
    }
  }

一个方面(facet)的值首先解析为一个令牌;只有在没有令牌声明该词时,才接受裸字面量(例如 opacity 50、inset 0)。优先使用令牌。when 作用于属性,state 作用于行为状态,两者可以组合。

属性、事件和约束

  archetype TextField

  props {
    label : string required "字段要求输入的内容。同时也是它的可访问名称。"
    value : string
    invalid : boolean = false
    error : string required when invalid "控件无效时,在控件下方显示的错误信息。"
    size : enum(sm, md) = md
    tone : context Tone = Neutral
  }

  events {
    valueChange
  }

类型:string、number、boolean、enum(a, b)、context <Group>、array<T>、你自定义的 type。跟在后面的字符串是该属性的文档。one-of a, b / any-of / exclusive 用于约束一组属性。

下一步查阅

完成前检查清单

  • 文件头声明了 milda 0.10 和 prelude 0.0.2。
  • 组件中没有字面量的颜色、长度或字体;每个值都是令牌引用。
  • 任何地方都没有平台相关的词:没有标签、CSS 属性、伪类或框架事件名称。
  • 每个组件都从原型开始,除非它纯粹是由实例组合而成。
  • 对整个系统运行 milda-check --strict 输出 0 errors, 0 warnings。

相关技能