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
1---2name: figma3description: Translates between Figma designs and Vue 3 + shadcn-vue code, mapping Auto Layout to Tailwind, extracting design tokens, and pushing components back to Figma via MCP.4---56# Skill: Figma ↔ Vue 3 + shadcn-vue78## Reading flow via MCP (Figma → Code)910```111. mcp:figma → get_design_context(nodeId or URL) ← always first122. mcp:figma → get_variable_defs(nodeId) ← formal tokens133. mcp:figma → search_design_system(query) ← DS components144. mcp:figma → get_screenshot(nodeId) ← visual reference15```1617---1819## Figma → Tailwind Translation2021### Auto Layout → Flexbox/Grid Vue2223| Figma Auto Layout | Tailwind | Vue template |24|---|---|---|25| Horizontal, gap 16 | `flex gap-4` | `<div class="flex gap-4">` |26| Vertical, gap 24 | `flex flex-col gap-6` | `<div class="flex flex-col gap-6">` |27| Space between | `flex justify-between` | |28| Fill container (width) | `flex-1` or `w-full` | |29| Fixed width 320px | `w-80` or `w-[320px]` | |30| Wrap | `flex flex-wrap` | |31| Align center | `items-center` | |32| Grid 3 cols, gap 16 | `grid grid-cols-3 gap-4` | |3334### Colors → shadcn-vue CSS Variables3536| Figma Semantic | shadcn CSS Variable | Tailwind |37|---|---|---|38| Primary | `--primary` | `bg-primary text-primary-foreground` |39| Background | `--background` | `bg-background` |40| Foreground | `--foreground` | `text-foreground` |41| Muted | `--muted-foreground` | `text-muted-foreground` |42| Destructive | `--destructive` | `text-destructive border-destructive` |43| Border | `--border` | `border-border` |44| Card | `--card` | `bg-card` |4546### Figma Typography → Tailwind4748| Figma | Tailwind |49|---|---|50| 12px Regular | `text-xs` |51| 14px Regular | `text-sm` |52| 14px Medium 500 | `text-sm font-medium` |53| 14px Semibold 600 | `text-sm font-semibold` |54| 16px Regular | `text-base` |55| 18px Semibold | `text-lg font-semibold` |56| 20px Bold | `text-xl font-bold` |57| 24px Bold | `text-2xl font-bold` |58| 30px Bold | `text-3xl font-bold` |5960Figma line-height (absolute) → Tailwind (relative):61- 16px line-height with 14px font → `leading-none` (1)62- 20px / 14px → `leading-tight` (1.25)63- 20px / 16px → `leading-5` (1.25)64- 24px / 16px → `leading-6` (1.5) ← default body6566### Spacing → Tailwind Scale (base 4px)6768| Figma px | Tailwind | rem |69|---|---|---|70| 2 | `gap-0.5 p-0.5` | 0.125 |71| 4 | `gap-1 p-1` | 0.25 |72| 8 | `gap-2 p-2` | 0.5 |73| 12 | `gap-3 p-3` | 0.75 |74| 16 | `gap-4 p-4` | 1 |75| 20 | `gap-5 p-5` | 1.25 |76| 24 | `gap-6 p-6` | 1.5 |77| 32 | `gap-8 p-8` | 2 |78| 40 | `gap-10 p-10` | 2.5 |79| 48 | `gap-12 p-12` | 3 |8081### Border radius → Tailwind8283| Figma | Tailwind |84|---|---|85| 4px | `rounded` |86| 6px | `rounded-md` |87| 8px | `rounded-lg` |88| 12px | `rounded-xl` |89| 16px | `rounded-2xl` |90| 50% / circle | `rounded-full` |9192### Shadows → Tailwind9394| Figma | Tailwind |95|---|---|96| `0 1px 2px rgba(0,0,0,0.05)` | `shadow-sm` |97| `0 1px 3px rgba(0,0,0,0.1)` | `shadow` |98| `0 4px 6px rgba(0,0,0,0.07)` | `shadow-md` |99| `0 10px 15px rgba(0,0,0,0.1)` | `shadow-lg` |100| `0 25px 50px rgba(0,0,0,0.25)` | `shadow-2xl` |101102---103104## Figma → shadcn-vue Vue Component Mapping105106| Visual pattern in Figma | shadcn-vue component |107|---|---|108| Surface / Card with padding | `<Card>` + sub-parts |109| Primary button | `<Button>` |110| Destructive button | `<Button variant="destructive">` |111| Ghost button | `<Button variant="ghost">` |112| Text input | `<Input>` |113| Text area | `<Textarea>` |114| Dropdown / Select | `<Select>` + sub-parts |115| Select with search | Combobox (Command + Popover) |116| Modal / Overlay | `<Dialog>` |117| Bottom sheet | `<Sheet>` |118| Temporary notification | `toast()` from vue-sonner |119| Badge / Chip | `<Badge>` |120| Context menu | `<DropdownMenu>` |121| Tooltip | `<Tooltip>` |122| Tabs | `<Tabs>` + sub-parts |123| Accordion | `<Accordion>` + sub-parts |124| Checkbox | `<Checkbox>` |125| Toggle on/off | `<Switch>` |126| Loading placeholder | `<Skeleton>` |127| Empty state | `<Empty>` + sub-parts |128| Data table | `<DataTable>` |129| Breadcrumb | `<Breadcrumb>` + sub-parts |130| Form field | `<Field>` or `<FormField>` |131| Button group | `<ButtonGroup>` |132| Input with icon | `<InputGroup>` |133| Spinner | `<Spinner>` |134| Stepper | `<Stepper>` + sub-parts |135136---137138## Components without shadcn-vue equivalent → Reka UI primitive139140```vue141<!-- Custom component with Reka UI -->142<script setup lang="ts">143import { ComboboxRoot, ComboboxInput, ComboboxContent, ComboboxItem } from 'reka-ui'144</script>145<template>146 <ComboboxRoot>147 <ComboboxInput class="..." />148 <ComboboxContent class="...">149 <ComboboxItem v-for="item in items" :key="item.id" :value="item">150 {{ item.label }}151 </ComboboxItem>152 </ComboboxContent>153 </ComboboxRoot>154</template>155```156157---158159## Expected output: figma-spec.md160161```markdown162# Figma Spec — [Frame Name]163164## Extracted tokens165166### Colors167| Token | Hex | Tailwind equiv. | CSS var |168|---|---|---|---|169| Brand blue | #2563EB | `bg-blue-600` | `--primary` |170171### Typography172| Usage | Size | Weight | Tailwind |173|---|---|---|---|174| Heading | 24px | 700 | `text-2xl font-bold` |175176### Dominant spacing177| Usage | Value | Tailwind |178|---|---|---|179| Gap between cards | 24px | `gap-6` |180181## Mapped components182183### [ComponentName]184- **shadcn-vue**: `<Card>` with `<CardHeader>`, `<CardContent>`, `<CardFooter>`185- **Reka UI base**: `N/A` (shadcn-vue abstracts it)186- **Required props**: title, description, actions[]187- **Variants**: default, highlighted188- **States**: default, loading (Skeleton), empty189- **Notes**: Status Badge in CardHeader190191## Documented behaviors192- Hover: card elevates with shadow-md193- Loading: 3 Skeleton cards of same dimensions194195## Deviations from standard shadcn-vue196- Badge uses custom color #7C3AED — add as CSS variable197```198199---200201## Writing flow via MCP (Code → Figma)202203### Prerequisites204205**MANDATORY**: Before every `use_figma` call, load the `/figma:figma-use` skill.206This skill teaches the Figma Plugin API syntax needed for write operations.207208```2091. Load /figma:figma-use skill ← ALWAYS first2102. mcp:figma → whoami ← verify connection2113. mcp:figma → search_design_system(query) ← discover existing DS2124. mcp:figma → use_figma(javascript) ← create/edit nodes213```214215### Available write tools216217| Tool | What it does |218|------|-------------|219| `use_figma` | Execute JavaScript in Figma context — create frames, text, components, set properties |220| `create_new_file` | Create a new Figma or FigJam file |221| `generate_diagram` | Create diagrams in FigJam |222223### Available design skills224225| Skill | When to use |226|-------|------------|227| `/figma:figma-use` | **Required** before every `use_figma` call |228| `/figma:figma-generate-design` | Push full screens/pages to Figma |229| `/figma:figma-generate-library` | Build design system with tokens and components |230231### Tailwind → Figma reverse mapping232233| Tailwind | Figma Auto Layout property |234|----------|---------------------------|235| `flex gap-4` | `layoutMode: "HORIZONTAL"`, `itemSpacing: 16` |236| `flex flex-col gap-6` | `layoutMode: "VERTICAL"`, `itemSpacing: 24` |237| `justify-between` | `primaryAxisAlignItems: "SPACE_BETWEEN"` |238| `items-center` | `counterAxisAlignItems: "CENTER"` |239| `p-4` | `paddingTop/Right/Bottom/Left: 16` |240| `px-4 py-2` | `paddingLeft/Right: 16`, `paddingTop/Bottom: 8` |241| `w-full` | `layoutSizingHorizontal: "FILL"` |242| `flex-1` | `layoutGrow: 1` |243244| Tailwind | Figma node property |245|----------|---------------------|246| `rounded-lg` | `cornerRadius: 8` |247| `shadow-md` | Drop shadow effect: `y: 4, blur: 6, spread: -1` |248| `text-sm font-medium` | `fontSize: 14, fontWeight: 500` |249| `bg-primary` | Fill bound to variable `--primary` |250| `border-border` | Stroke bound to variable `--border` |251252### Writing components — pattern253254```javascript255// Example: Create a Card component in Figma256const card = figma.createFrame()257card.name = "Card"258card.layoutMode = "VERTICAL"259card.itemSpacing = 0260card.cornerRadius = 8261card.paddingTop = card.paddingBottom = 24262card.paddingLeft = card.paddingRight = 24263264// Bind to variable instead of hardcoding265const bgVar = figma.variables.getLocalVariables().find(v => v.name === "card")266if (bgVar) {267 card.setBoundVariable("fills", bgVar.id)268}269```270271### Writing tokens — pattern272273```javascript274// Create a variable collection for design tokens275const collection = figma.variables.createVariableCollection("Design Tokens")276277// Add color variable with light/dark modes278const primaryVar = figma.variables.createVariable("primary", collection.id, "COLOR")279primaryVar.setValueForMode(lightModeId, { r: 0.09, g: 0.09, b: 0.09 })280primaryVar.setValueForMode(darkModeId, { r: 0.98, g: 0.98, b: 0.98 })281```282283### Gotchas — Write mode284285- Always use `figma.variables` for colors — never hardcode hex286- Auto Layout must be set via `layoutMode` before setting `itemSpacing`287- Text nodes require `figma.loadFontAsync()` before setting `characters`288- Component variants: use `figma.combineAsVariants()` after creating all variants289- Figma plugin API is synchronous except font loading — handle `await` properly