with one click
card-component
// Build a card component with an image placeholder, title, body text, and a CTA button instance using auto-layout and exposed text properties.
// Build a card component with an image placeholder, title, body text, and a CTA button instance using auto-layout and exposed text properties.
| name | card-component |
| version | 1.0.0 |
| title | Card Component |
| description | Build a card component with an image placeholder, title, body text, and a CTA button instance using auto-layout and exposed text properties. |
| author | ufira-ai |
| tags | ["components","cards","layout"] |
| vibma_min | 0.1.0 |
| tools | ["create_frame","create_text","components","styles","variables","instances"] |
You are building a reusable card component in Figma. The card contains an image placeholder, a title, body text, and a CTA button instance. Follow each step in order—IDs produced in earlier steps are required by later ones.
This skill assumes a button component already exists in the file (e.g., created via the button-system skill). If no button component exists, create a minimal one first: a frame named Button/Variant=Primary, Size=Medium converted to a component via components({ method: "create", type: "from_node", ... }).
You also need text styles for the title and body. Create them now if they don't exist:
styles({ method: "create", type: "text", name: "Text/Heading/SM",
fontFamily: "Inter", fontWeight: 600, fontSize: 16, lineHeight: 24 })
styles({ method: "create", type: "text", name: "Text/Body/MD",
fontFamily: "Inter", fontWeight: 400, fontSize: 14, lineHeight: 20 })
Record the returned styleId for each.
create_frame({
name: "Card",
layoutMode: "VERTICAL",
primaryAxisAlignItems: "MIN",
counterAxisAlignItems: "MIN",
counterAxisSizingMode: "FIXED",
width: 320,
primaryAxisSizingMode: "AUTO",
paddingLeft: 0, paddingRight: 0, paddingTop: 0, paddingBottom: 0,
itemSpacing: 0,
cornerRadius: 12,
strokeWeight: 1,
strokes: [{ type: "SOLID", color: "#E2E8F0" }],
fills: [{ type: "SOLID", color: "#FFFFFF" }],
clipsContent: true
})
Record the returned cardFrameId.
Inside the card frame, create a rectangle acting as the image area:
create_frame({
parentId: "<cardFrameId>",
name: "Image Placeholder",
layoutSizingHorizontal: "FILL",
counterAxisSizingMode: "FIXED",
height: 180,
fills: [{ type: "SOLID", color: "#E2E8F0" }]
})
layoutSizingHorizontal: "FILL" makes it stretch to the card width automatically. Record the returned imageFrameId.
Create a vertical auto-layout frame inside the card for text and the CTA:
create_frame({
parentId: "<cardFrameId>",
name: "Content",
layoutMode: "VERTICAL",
primaryAxisAlignItems: "MIN",
counterAxisAlignItems: "MIN",
layoutSizingHorizontal: "FILL",
primaryAxisSizingMode: "AUTO",
paddingLeft: 16, paddingRight: 16, paddingTop: 16, paddingBottom: 16,
itemSpacing: 12,
fills: []
})
Record the returned contentFrameId.
create_text({
parentId: "<contentFrameId>",
name: "Title",
characters: "Card Title",
textStyleId: "<Text/Heading/SM styleId>",
layoutSizingHorizontal: "FILL",
fills: [{ type: "SOLID", color: "#0F172A" }]
})
Record the returned titleNodeId.
create_text({
parentId: "<contentFrameId>",
name: "Body",
characters: "A short description that supports the card title and gives users context before they act.",
textStyleId: "<Text/Body/MD styleId>",
layoutSizingHorizontal: "FILL",
fills: [{ type: "SOLID", color: "#475569" }]
})
Record the returned bodyNodeId.
Find the primary medium button component. Use get_node_info on the file root or rely on a known componentId from the button-system skill. Then create an instance:
instances({
method: "create",
componentId: "<primary-medium-button-componentId>",
parentId: "<contentFrameId>"
})
The instance inherits its width from the component. If you want the button to stretch full-width inside the content frame, patch it after creation:
patch_nodes({
nodeId: "<button-instanceId>",
layoutSizingHorizontal: "FILL"
})
components({
method: "create",
type: "from_node",
nodeId: "<cardFrameId>"
})
Record the returned componentId.
Expose the title and body as component text properties so consumers can override them without detaching:
components({
method: "create",
type: "component_property",
nodeId: "<componentId>",
propertyName: "Title",
propertyType: "TEXT",
defaultValue: "Card Title",
boundNodeId: "<titleNodeId>"
})
components({
method: "create",
type: "component_property",
nodeId: "<componentId>",
propertyName: "Body",
propertyType: "TEXT",
defaultValue: "A short description...",
boundNodeId: "<bodyNodeId>"
})
lint_node({
nodeId: "<componentId>",
rules: ["hardcoded-color", "no-text-style", "no-autolayout", "default-name"]
})
Common issues to watch for:
hardcoded-color on the image placeholder fill—acceptable for placeholder, note it to the userno-text-style on title or body—means textStyleId was not applied; use patch_nodes to set the styledefault-name on the button instance—rename it via patch_nodes({ nodeId: "<instanceId>", name: "CTA Button" })Report to the user:
componentIdTitle, BodyBuild a complete multi-variant button component system with primary, secondary, danger, and ghost styles across small, medium, and large sizes.
Build a semantic color system with primitive paint styles and semantic design tokens as Figma variables supporting light and dark modes.
Run a systematic design audit using lint_node across an entire selection or page, group issues by rule, and produce a prioritized report.
Build a WRAP auto-layout icon grid with 24×24 icon frames, a base icon component, and instanced icons arranged in a responsive wrap layout.