Loading...
Loading...
Extract component metadata from a Figma design system and generate production-ready QML controls. Use this skill whenever someone wants to turn Figma components into QML files — whether they say "generate components from Figma", "create QML controls based on a design system", "convert Figma components to QML", "build the component library", "extract button/input/checkbox from Figma", or anything similar. Requires design-tokens.json and QML design system singletons to already exist (from the token extraction skill). Uses Figma MCP to inspect components one at a time and maps variants, states, sizing, and token usage to idiomatic Qt Quick Controls 2 patterns. Trigger this skill at the component generation step of any QML design-system workflow.
npx skill4agent add theqtcompanyrnd/agent-skills qt-figma-component-generationdesign-tokens.jsonPrimitives.qmlTheme.qmlSpacing.qmlFontInterface.qmldesign-system/qt-figma-token-extractionget_metadataget_design_context"The Figma MCP connector isn't connected yet. Connect it via your MCP configuration, then come back and we can start."
get_metadataTool: get_metadata
Input: { "fileKey": "<file key>" }"I can see the following component groups in the Figma file: [list]. Which ones should I generate QML files for? Or should I do all of them?"
| Figma component name | Node ID | QML file | Status |
|---|---|---|---|
| Button | 67:139 | Button.qml | pending |
| Text Field | ... | TextField.qml | pending |
pendingextractingmappingdoneblockedtool: 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"Qt.ThemesTokenInterfacePattern B uses integer enum variants, not strings. Pattern A uses. Pattern B usesproperty string variant: "primary". Do not mix the two approaches — pick one and use it consistently throughout all components.property int typeVariant: ButtonStyle.TypeVariant.Primary
references/readonly property| Reference file | Output file | Demonstrates |
|---|---|---|
| | AbstractButton, multi-variant state machine, size helpers, accent family mapping |
| | TextInput wrapped in ColumnLayout, label + error + helper text, clear button |
| | CheckBox indicator, Canvas tick mark, indeterminate state |
| | Switch track + animated thumb, NumberAnimation |
| | Custom Item with Popup, ListView delegate, chevron |
qt-development-skills:qt-qmlassets/qt-controls/Button.qmlButtonStyle.qmlpragma SingletoncomponentButton.qmlButtonStyle.qmlCheckBox.qmlCheckBoxStyle.qmlComboBox.qmlComboBoxStyle.qmlSwitch.qmlSwitchStyle.qmlTextField.qmlTextFieldStyle.qmlAlways callon an individual main component node — NOT the parent component set node. Component sets return oversized JSON mixing all variants. Inspect the default/base variant first, then representative variants (Hover, Pressed, Disabled) individually.get_design_context
Tool: get_design_context
Input: { "fileKey": "<key>", "nodeId": "<individual component node id>" }get_design_contextpropertyreadonly propertydesign-tokens.jsonBefore writing any token reference, open,Theme.qml,Primitives.qml, andSpacing.qmland read the actual property names. Do not copy token names from the reference assets — the project's token naming convention may differ from the examples. Every token name you write in a component must exist in the project's singletons.FontInterface.qml
| Figma component | QML base type |
|---|---|
| Button (any style) | |
| Checkbox | |
| Radio button | |
| Toggle / switch | |
| Text input / field | |
| Text area (multiline) | |
| Select / dropdown | Custom |
| Slider | |
| Tab bar | |
| Progress bar | |
| Spinner / spin box | |
| Card / container | |
| Divider | |
| Badge | |
| Tooltip | |
property string variant: "primary" // primary | secondary | ghost | tertiary | danger
property string size: "medium" // small | medium | large (sm | md | lg accepted)// Use integer enums, not strings — do not mix with Pattern A string variants
property int typeVariant: ButtonStyle.TypeVariant.Primaryreadonly 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.qmlFontInterface.qml// TODO: add to Spacing.qmlControlAbstractButtonCheckBoxSwitchSliderColumnLayoutItemControlactiveFocus// 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 itHoverHandler { cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor }components/Button.qmlTextField.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() }// ── 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.x4readonly property colorTheme.qmlPrimitives.qmlSpacing.qmlFontInterface.qmlHoverHandlerdoneblockedqt_add_qml_module QML_FILESSpacing.qmlFontInterface.qmlQt.PartiallyCheckedAbstractButtonControlBehaviorBehavior on color { ColorAnimation { duration: Theme.duration_fast } }100Popupparent: Overlay.overlay