# Figma

> 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.

- Skill: `pwdev-solucoes/figma` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pwdev-solucoes/figma`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pwdev-solucoes/figma/raw
- Safety review: CAUTION (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media, Coding & Dev Tools, Figma Tools
- Tags: Design Tokens, Figma, Mcp, Reka Ui, Shadcn Vue, Tailwind, Vue
- Author: pwdev-solucoes (https://skillmd.com/u/pwdev-solucoes)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/pwdev-solucoes/figma

---


# 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

```vue
<!-- 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

```markdown
# 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

```javascript
// 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

```javascript
// 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

