Skill: Figma ↔ Vue 3 + shadcn-vue
Reading flow via MCP (Figma → Code)
1. mcp:figma → get_design_context(nodeId or URL) ← always first
2. mcp:figma → get_variable_defs(nodeId) ← formal tokens
3. mcp:figma → search_design_system(query) ← DS components
4. mcp:figma → get_screenshot(nodeId) ← visual reference
Figma → Tailwind Translation
Auto Layout → Flexbox/Grid Vue
| Figma Auto Layout |
Tailwind |
Vue template |
| Horizontal, gap 16 |
flex gap-4 |
<div class="flex gap-4"> |
| Vertical, gap 24 |
flex flex-col gap-6 |
<div class="flex flex-col gap-6"> |
| Space between |
flex justify-between |
|
| Fill container (width) |
flex-1 or w-full |
|
| Fixed width 320px |
w-80 or w-[320px] |
|
| Wrap |
flex flex-wrap |
|
| Align center |
items-center |
|
| Grid 3 cols, gap 16 |
grid grid-cols-3 gap-4 |
|
Colors → shadcn-vue CSS Variables
| Figma Semantic |
shadcn CSS Variable |
Tailwind |
| Primary |
--primary |
bg-primary text-primary-foreground |
| Background |
--background |
bg-background |
| Foreground |
--foreground |
text-foreground |
| Muted |
--muted-foreground |
text-muted-foreground |
| Destructive |
--destructive |
text-destructive border-destructive |
| Border |
--border |
border-border |
| Card |
--card |
bg-card |
Figma Typography → Tailwind
| Figma |
Tailwind |
| 12px Regular |
text-xs |
| 14px Regular |
text-sm |
| 14px Medium 500 |
text-sm font-medium |
| 14px Semibold 600 |
text-sm font-semibold |
| 16px Regular |
text-base |
| 18px Semibold |
text-lg font-semibold |
| 20px Bold |
text-xl font-bold |
| 24px Bold |
text-2xl font-bold |
| 30px Bold |
text-3xl font-bold |
Figma line-height (absolute) → Tailwind (relative):
- 16px line-height with 14px font →
leading-none (1)
- 20px / 14px →
leading-tight (1.25)
- 20px / 16px →
leading-5 (1.25)
- 24px / 16px →
leading-6 (1.5) ← default body
Spacing → Tailwind Scale (base 4px)
| Figma px |
Tailwind |
rem |
| 2 |
gap-0.5 p-0.5 |
0.125 |
| 4 |
gap-1 p-1 |
0.25 |
| 8 |
gap-2 p-2 |
0.5 |
| 12 |
gap-3 p-3 |
0.75 |
| 16 |
gap-4 p-4 |
1 |
| 20 |
gap-5 p-5 |
1.25 |
| 24 |
gap-6 p-6 |
1.5 |
| 32 |
gap-8 p-8 |
2 |
| 40 |
gap-10 p-10 |
2.5 |
| 48 |
gap-12 p-12 |
3 |
Border radius → Tailwind
| Figma |
Tailwind |
| 4px |
rounded |
| 6px |
rounded-md |
| 8px |
rounded-lg |
| 12px |
rounded-xl |
| 16px |
rounded-2xl |
| 50% / circle |
rounded-full |
Shadows → Tailwind
| Figma |
Tailwind |
0 1px 2px rgba(0,0,0,0.05) |
shadow-sm |
0 1px 3px rgba(0,0,0,0.1) |
shadow |
0 4px 6px rgba(0,0,0,0.07) |
shadow-md |
0 10px 15px rgba(0,0,0,0.1) |
shadow-lg |
0 25px 50px rgba(0,0,0,0.25) |
shadow-2xl |
Figma → shadcn-vue Vue Component Mapping
| Visual pattern in Figma |
shadcn-vue component |
| Surface / Card with padding |
<Card> + sub-parts |
| Primary button |
<Button> |
| Destructive button |
<Button variant="destructive"> |
| Ghost button |
<Button variant="ghost"> |
| Text input |
<Input> |
| Text area |
<Textarea> |
| Dropdown / Select |
<Select> + sub-parts |
| Select with search |
Combobox (Command + Popover) |
| Modal / Overlay |
<Dialog> |
| Bottom sheet |
<Sheet> |
| Temporary notification |
toast() from vue-sonner |
| Badge / Chip |
<Badge> |
| Context menu |
<DropdownMenu> |
| Tooltip |
<Tooltip> |
| Tabs |
<Tabs> + sub-parts |
| Accordion |
<Accordion> + sub-parts |
| Checkbox |
<Checkbox> |
| Toggle on/off |
<Switch> |
| Loading placeholder |
<Skeleton> |
| Empty state |
<Empty> + sub-parts |
| Data table |
<DataTable> |
| Breadcrumb |
<Breadcrumb> + sub-parts |
| Form field |
<Field> or <FormField> |
| Button group |
<ButtonGroup> |
| Input with icon |
<InputGroup> |
| Spinner |
<Spinner> |
| Stepper |
<Stepper> + sub-parts |
Components without shadcn-vue equivalent → Reka UI primitive
<!-- Custom component with Reka UI -->
<script setup lang="ts">
import { ComboboxRoot, ComboboxInput, ComboboxContent, ComboboxItem } from 'reka-ui'
</script>
<template>
<ComboboxRoot>
<ComboboxInput class="..." />
<ComboboxContent class="...">
<ComboboxItem v-for="item in items" :key="item.id" :value="item">
{{ item.label }}
</ComboboxItem>
</ComboboxContent>
</ComboboxRoot>
</template>
Expected output: figma-spec.md
# Figma Spec — [Frame Name]
## Extracted tokens
### Colors
| Token | Hex | Tailwind equiv. | CSS var |
|---|---|---|---|
| Brand blue | #2563EB | `bg-blue-600` | `--primary` |
### Typography
| Usage | Size | Weight | Tailwind |
|---|---|---|---|
| Heading | 24px | 700 | `text-2xl font-bold` |
### Dominant spacing
| Usage | Value | Tailwind |
|---|---|---|
| Gap between cards | 24px | `gap-6` |
## Mapped components
### [ComponentName]
- **shadcn-vue**: `<Card>` with `<CardHeader>`, `<CardContent>`, `<CardFooter>`
- **Reka UI base**: `N/A` (shadcn-vue abstracts it)
- **Required props**: title, description, actions[]
- **Variants**: default, highlighted
- **States**: default, loading (Skeleton), empty
- **Notes**: Status Badge in CardHeader
## Documented behaviors
- Hover: card elevates with shadow-md
- Loading: 3 Skeleton cards of same dimensions
## Deviations from standard shadcn-vue
- Badge uses custom color #7C3AED — add as CSS variable
Writing flow via MCP (Code → Figma)
Prerequisites
MANDATORY: Before every use_figma call, load the /figma:figma-use skill.
This skill teaches the Figma Plugin API syntax needed for write operations.
1. Load /figma:figma-use skill ← ALWAYS first
2. mcp:figma → whoami ← verify connection
3. mcp:figma → search_design_system(query) ← discover existing DS
4. mcp:figma → use_figma(javascript) ← create/edit nodes
Available write tools
| Tool |
What it does |
use_figma |
Execute JavaScript in Figma context — create frames, text, components, set properties |
create_new_file |
Create a new Figma or FigJam file |
generate_diagram |
Create diagrams in FigJam |
Available design skills
| Skill |
When to use |
/figma:figma-use |
Required before every use_figma call |
/figma:figma-generate-design |
Push full screens/pages to Figma |
/figma:figma-generate-library |
Build design system with tokens and components |
Tailwind → Figma reverse mapping
| Tailwind |
Figma Auto Layout property |
flex gap-4 |
layoutMode: "HORIZONTAL", itemSpacing: 16 |
flex flex-col gap-6 |
layoutMode: "VERTICAL", itemSpacing: 24 |
justify-between |
primaryAxisAlignItems: "SPACE_BETWEEN" |
items-center |
counterAxisAlignItems: "CENTER" |
p-4 |
paddingTop/Right/Bottom/Left: 16 |
px-4 py-2 |
paddingLeft/Right: 16, paddingTop/Bottom: 8 |
w-full |
layoutSizingHorizontal: "FILL" |
flex-1 |
layoutGrow: 1 |
| Tailwind |
Figma node property |
rounded-lg |
cornerRadius: 8 |
shadow-md |
Drop shadow effect: y: 4, blur: 6, spread: -1 |
text-sm font-medium |
fontSize: 14, fontWeight: 500 |
bg-primary |
Fill bound to variable --primary |
border-border |
Stroke bound to variable --border |
Writing components — pattern
// Example: Create a Card component in Figma
const card = figma.createFrame()
card.name = "Card"
card.layoutMode = "VERTICAL"
card.itemSpacing = 0
card.cornerRadius = 8
card.paddingTop = card.paddingBottom = 24
card.paddingLeft = card.paddingRight = 24
// Bind to variable instead of hardcoding
const bgVar = figma.variables.getLocalVariables().find(v => v.name === "card")
if (bgVar) {
card.setBoundVariable("fills", bgVar.id)
}
Writing tokens — pattern
// Create a variable collection for design tokens
const collection = figma.variables.createVariableCollection("Design Tokens")
// Add color variable with light/dark modes
const primaryVar = figma.variables.createVariable("primary", collection.id, "COLOR")
primaryVar.setValueForMode(lightModeId, { r: 0.09, g: 0.09, b: 0.09 })
primaryVar.setValueForMode(darkModeId, { r: 0.98, g: 0.98, b: 0.98 })
Gotchas — Write mode
- Always use
figma.variables for colors — never hardcode hex
- Auto Layout must be set via
layoutMode before setting itemSpacing
- Text nodes require
figma.loadFontAsync() before setting characters
- Component variants: use
figma.combineAsVariants() after creating all variants
- Figma plugin API is synchronous except font loading — handle
await properly