关于 qt-figma-component-generation
该 Agent skill 根据 Figma 组件定义生成使用设计系统单例的 QML 控件,适用于 Qt UI 开发。它通过 MCP 获取设计规格,并提供两种组件实现模式及逐项生成流程。
主要功能
- 01通过 MCP 提取 Figma 组件规格
- 02生成 QML 控件文件
- 03支持内联与样式单例模式
- 04映射状态、变体与设计令牌
- 05维护组件清单与待办事项
适用场景
- 01将 Figma 按钮和输入框转为 QML
- 02按设计令牌构建一致的控件
- 03为多主题项目选择样式单例模式
MCP 相关
要求连接 Figma MCP,并使用 get_metadata 发现组件、get_design_context 提取组件设计信息。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ theqtcompanyrnd/ agent-skills --skill qt-figma-component-generation安装后在 Claude Code 中输入/qt-figma-component-generation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/qt-controls/BadgeLabel.qmlBadgeLabelStyle.qmlBadgeNotification.qmlBadgeNotificationStyle.qmlButton.qmlButtonStyle.qmlCard.qmlCardStyle.qmlCheckBox.qmlCheckBoxStyle.qmlComboBox.qmlComboBoxStyle.qmlDialog.qmlDialogButtonBox.qmlDialogButtonBoxStyle.qmlDialogStyle.qmlIconButton.qmlIconButtonStyle.qmlIndicator.qmlIndicatorStyle.qmlItemDelegate.qmlItemDelegateStyle.qmlLabel.qmlLabelStyle.qmlLinkInline.qmlLinkInlineStyle.qmlLinkList.qmlLinkListStyle.qmlLinkStandalone.qmlLinkStandaloneStyle.qml详细介绍
Figma 组件生成技能
此技能通过 MCP 从 Figma 文件中读取组件定义,并生成可用于生产环境的 QML 控件文件,这些文件使用 Token 提取技能生成的设计系统单例。
前置条件
生成任何组件之前,请确认项目中存在以下所有内容:
design-tokens.json— 来自 Token 提取技能的合并令牌文件- QML 设计系统单例 — 位于
design-system/文件夹中的Primitives.qml、Theme.qml、Spacing.qml、FontInterface.qml
如果缺少其中任何一项,请停止并先运行 Token 提取技能(qt-figma-token-extraction)。
确认 Figma MCP 已连接 — 确认工具列表中包含 get_metadata 和 get_design_context。如果没有,请告知用户:
“Figma MCP 连接器尚未连接。请通过 MCP 配置连接,然后回来继续。”
确认连接之前请勿继续。
步骤 1 — 组件发现
使用 get_metadata 获取文件结构,并确定哪些页面和画框包含组件:
Tool: get_metadata
Input: { "fileKey": "<file key>" }
从响应中记录所有作为组件组命名的页面和画框或组件集(例如 “Button”“Text Field”“Checkbox”)。
询问用户:
“我在 Figma 文件中看到以下组件组:[列表]。你希望我为哪些组件生成 QML 文件?还是全部生成?”
建立一张组件清单表,并在整个工作流中持续更新——之后不要再创建第二张表:
| Figma 组件名称 | 节点 ID | QML 文件 | 状态 |
|---|---|---|---|
| Button | 67:139 | Button.qml | pending |
| Text Field | ... | TextField.qml | pending |
状态值:pending → extracting → mapping → done / blocked
步骤 2 — 选择模式
在读取任何资源或编写任何代码之前,请让用户选择一种实现模式。如果 AskUserQuestion 工具可用,请使用它:
tool: AskUserQuestion
question: "Which code style should the generated components use?"
options:
- "Pattern A — Inline (self-contained file, all state logic inside the component)"
- "Pattern B — Style singleton (ComponentStyle.qml + Component.qml, supports multiple themes)"
- "I'm not sure — recommend one"
如果该工具不可用(例如在 Claude Code、Codex 或 Copilot 中),请以纯文本提问,并等待用户回复后再继续。
如果用户选择“我不确定”,对于大多数项目,建议使用 模式 A——它更简单、独立性更强,也更易于调试。只有当项目已有 Qt.Themes / TokenInterface 层,或需要支持多种可切换主题时,才建议使用模式 B。
模式 B 使用整数枚举变体,而不是字符串。 模式 A 使用
property string variant: "primary"。模式 B 使用property int typeVariant: ButtonStyle.TypeVariant.Primary。不要混用这两种方法——选择其中一种,并在所有组件中始终如一地使用。
步骤 3 — 准备所选模式
在提取或编写任何内容之前,请确保所选模式的结构已准备就绪。模式 B 从随附资源中读取;模式 A 则使用步骤 5 中的内嵌片段构建。
Pattern A 资源 — references/
此文件夹包含经 Figma 验证的 Pattern A 控件。每个文件都是独立的,所有状态逻辑都在组件内部通过对 readonly property 值使用条件表达式实现。
| 参考文件 | 输出文件 | 演示内容 |
|---|---|---|
references/Button.qml | Button.qml | AbstractButton、多变体状态机、尺寸辅助项、强调色系列映射 |
references/TextField.qml | TextField.qml | 在 ColumnLayout 中包装的 TextInput、标签 + 错误 + 辅助文本、清除按钮 |
references/Checkbox.qml | Checkbox.qml | CheckBox 指示器、Canvas 勾选标记、不确定状态 |
references/Toggle.qml | Toggle.qml | Switch 轨道 + 动画滑块、NumberAnimation |
references/Select.qml | Select.qml | 带 Popup 的自定义 Item、ListView 委托、箭头 |
编写代码前,请先阅读与正在生成的组件最相符的文件。 如果有 QML 编码技能(qt-development-skills:qt-qml)可用,编写时请使用它,以确保输出符合 Qt 6 的惯用模式。
Pattern B 资源 — assets/qt-controls/
此文件夹包含来自生产级 Qt 控件库的 QML 文件对。每个组件拆分为两个文件:
Button.qml— 组件逻辑、布局、基类型、公共 APIButtonStyle.qml— 定义各状态和尺寸变体的类型化component对象的pragma Singleton
编写代码前,请先阅读即将生成的组件对应的资源文件对。 生成的文件必须遵循参考资源的结构、属性顺序和模式选择。如果输出与参考资源不一致,且无法用特定的 Figma 组件来解释,请思考原因并进行修正。存在参考资源时,不要另行设计不同的结构。
请先阅读以下核心文件对(阅读与正在生成的组件对应的文件对):
Button.qml+ButtonStyle.qmlCheckBox.qml+CheckBoxStyle.qmlComboBox.qml+ComboBoxStyle.qmlSwitch.qml+SwitchStyle.qmlTextField.qml+TextFieldStyle.qml
第 4 步 — 按组件提取
针对清单中的每个组件,通过 MCP 提取其规格。
始终对单个主组件节点调用
get_design_context,而不是对父组件集节点调用。 组件集会返回包含所有变体的超大 JSON。先检查默认/基础变体,再分别检查有代表性的变体(Hover、Pressed、Disabled)。
Tool: get_design_context
Input: { "fileKey": "<key>", "nodeId": "<individual component node id>" }
如果尚未获取单个节点 ID,请对父级框架调用 get_design_context 并查找子组件节点,然后再对每个节点重新调用。
每个组件需要提取的内容
- 变体 / 属性 — Figma 变体属性及允许值 → QML
property声明 - 状态 — Default、Hover、Pressed、Disabled、Focus、Error → 对
readonly property值使用条件表达式 - 尺寸 — 高度、内边距(水平 + 垂直)、间距、字号、字重、圆角半径
- 颜色令牌 — 各状态中出现的语义令牌;记录 Figma 中的确切名称及其在
design-tokens.json中解析出的值 - 排版 — 每个文本元素的字体系列、字号、字重、行高
- 边框 — 描边宽度、颜色令牌、出现此边框的状态
- 图标 / 插槽 — 组件是否包含图标插槽、插槽尺寸、位置(左侧/右侧/两侧)
编写代码前,将所有提取的数据记录在临时笔记中。
第 5 步 — Figma → QML 映射
引用任何令牌前,请先打开
Theme.qml、Primitives.qml、Spacing.qml和FontInterface.qml,并阅读实际的属性名称。 不要从参考资源中复制令牌名称 — 项目的令牌命名规范可能与示例不同。组件中编写的每个令牌名称都必须存在于项目的单例中。
基础类型选择
| Figma 组件 | QML 基础类型 |
|---|---|
| 按钮(任意样式) | AbstractButton(来自 QtQuick.Controls) |
| 复选框 | CheckBox(来自 QtQuick.Controls.Basic) |
| 单选按钮 | RadioButton(来自 QtQuick.Controls.Basic) |
| 切换开关 | Switch(来自 QtQuick.Controls.Basic) |
| 文本输入框/字段 | 用 Rectangle + TextInput 包装的 ColumnLayout |
| 文本区域(多行) | 用 TextArea(来自 QtQuick.Controls.Basic)包装的 ScrollView — 暂无参考资源;遵循 TextField 模式,但添加 wrapMode: TextArea.Wrap 并移除固定高度 |
| 选择框/下拉菜单 | 带 Popup 的自定义 Item |
| 滑块 | Slider(来自 QtQuick.Controls.Basic) |
| 标签栏 | TabBar + TabButton |
| 进度条 | ProgressBar(来自 QtQuick.Controls.Basic) |
| 微调框/数值微调框 | SpinBox(来自 QtQuick.Controls.Basic) |
| 卡片/容器 | Rectangle 或普通 Item |
| 分隔线 | Rectangle(1 px,fillWidth) |
| 徽标 | 包装 Text 的 Rectangle |
| 工具提示 | ToolTip(来自 QtQuick.Controls.Basic) |
变体 → 属性模式(模式 A)
property string variant: "primary" // primary | secondary | ghost | tertiary | danger
property string size: "medium" // small | medium | large (sm | md | lg accepted)
变体 → 枚举模式(模式 B)
// Use integer enums, not strings — do not mix with Pattern A string variants
property int typeVariant: ButtonStyle.TypeVariant.Primary
状态 → 条件表达式模式
readonly property color _bg: {
if (!enabled) return Theme.background_muted
return pressed ? Theme.accent_subtle
: hovered ? Theme.accent_muted
: Theme.accent_default
}
图标槽模式
// Icon slot — rendered via icon font glyph in a Text item
property string iconGlyph: ""
property int iconLayoutDir: Qt.LeftToRight // Qt.LeftToRight | Qt.RightToLeft
// controls which side the icon appears on
contentItem: RowLayout {
layoutDirection: root.iconLayoutDir
spacing: root._iconGap
Text {
text: root.iconGlyph
font.family: FontInterface.iconFont.name
visible: root.iconGlyph !== ""
}
Text {
id: _label
text: root.label
// ... font properties
}
}
尺寸 — 优先使用令牌,必要时使用字面值
首先检查 Spacing.qml 和 FontInterface.qml。只有在没有令牌涵盖相应尺寸时,才使用字面值,并添加 // TODO: add to Spacing.qml 注释。
焦点环模式
焦点环仅适用于基于 Control 的组件(AbstractButton、CheckBox、Switch、Slider 等)。Text Field(根类型为 ColumnLayout)和 Select(根类型为 Item)不是 Control 子类 — 对它们使用 activeFocus 和固定半径。
// For Control-based components (AbstractButton, CheckBox, Switch …)
Rectangle {
anchors { fill: parent; margins: -2 }
radius: parent.radius + 2 // only valid when parent is a Rectangle
color: "transparent"
border.color: Theme.stroke_focus // use a token — never a literal color
border.width: 2 // TODO: promote to Spacing token if available
visible: root.visualFocus // Control property — gives keyboard-only focus ring
}
// For non-Control roots (ColumnLayout, Item) — use activeFocus and fixed radius
Rectangle {
anchors { fill: parent; margins: -2 }
radius: 4 // TODO: use Spacing token
color: "transparent"
border.color: Theme.stroke_focus
border.width: 2
visible: root.activeFocus
}
颜色动画模式
仅在交互期间(悬停、按下)会发生动画的颜色属性上添加 Behavior 块。禁用状态下跳过这些块 — 此时通常应直接切换,而不是渐变。
// On the Rectangle or contentItem that holds the interactive color:
Behavior on color { ColorAnimation { duration: Theme.duration_fast } }
Behavior on border.color { ColorAnimation { duration: Theme.duration_fast } }
// If no duration token exists yet: duration: 100 — add a TODO to promote it
光标模式
HoverHandler { cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor }
步骤 6 — 编写 QML 文件
将每个组件放在项目的 components/ 文件夹中。使用与 Figma 组件名称一致的 PascalCase 命名(Button.qml、TextField.qml 等)。
文件头
// ComponentName.qml — [Project] Design System — [component description]
// Maps to Figma: [file name] → [component name] (node [id])
//
// Figma variants (inspected via MCP, [date]):
// Prop1: "value1" | "value2"
// Prop2: "valueA" | "valueB"
// States: Default | Hover | Pressed | Disabled [| Error | Focus]
// Sizes: "small" | "medium" | "large"
//
// Usage:
// import MyProject
// ComponentName { prop: "value"; onAction: doThing() }
公共 API 部分
// ── Public API ────────────────────────────────────────────────────────────
property string variant: "primary"
property string size: "medium"
property string label: "Button"
// ── Private helpers ───────────────────────────────────────────────────────
readonly property bool _isSmall: size === "small" || size === "sm"
readonly property color _bg: ...
缺失值
// TODO: add Spacing.buttonIconGapSm to Spacing.qml (Figma: 0px for small buttons)
readonly property int _iconGap: _isSmall ? 0 : Spacing.x4
生成所有组件后,为用户汇总完整的 TODO 列表。
步骤 7 — 生成后检查
编写完所有组件后,进行一次一致性检查:
- 每个引用主题令牌的
readonly property color都必须使用Theme.qml或Primitives.qml中实际存在的名称。标记出不存在的名称。 - 每个数值尺寸都必须来自
Spacing.qml或FontInterface.qml。收集所有应提升为令牌的字面值。 - 每个交互式组件都有焦点环。
- 每个交互式组件都有带光标形状的
HoverHandler。 - 文件头记录已检查的节点 ID。
- 更新步骤 1 中的清单表格 — 将所有组件标记为
done或blocked。
向用户简要汇报:
- 已生成的组件(数量和名称)
- 已跳过或受阻的组件(及原因)
- 完整 TODO 列表:需要添加到设计系统单例中的令牌
- 建议的下一步:将组件添加到 CMakeLists.txt 中的
qt_add_qml_module QML_FILES,并在组件展示页中进行冒烟测试
常见陷阱
检查组件集,而不是主组件。 组件集会堆叠显示所有变体。务必深入查找单个组件节点。
使用参考资源中的令牌名称,而不是项目中的名称。 参考资源使用的示例令牌名称可能与项目的单例不匹配。务必先读取实际的单例文件。
将令牌中已有的值硬编码。 编写任何数字字面量之前,先检查 Spacing.qml 和 FontInterface.qml。
遗漏不确定/部分选中状态。 复选框和单选按钮通常有第三种状态。务必检查 Qt.PartiallyChecked。
未将 AbstractButton 的默认内边距设为零。 AbstractButton 和其他 Control 子类有默认内边距,会增加渲染高度。自行管理几何属性时,请显式将内边距设为零。
忘记 Behavior 块。 对交互期间会变化的颜色属性(悬停、按下)添加 Behavior on color { ColorAnimation { duration: Theme.duration_fast } }。时长请使用令牌,不要硬编码为 100。禁用状态下不要添加 Behavior;此时通常应直接切换。
Popup 的 z 轴顺序。 如果 Popup 项被父容器裁剪,需要设置 parent: Overlay.overlay。
混用模式 A 字符串和模式 B 枚举。 选择一种变体实现方式,并在所有组件中保持一致。