Figma Integration and Design Systems

Bridge the gap between design and development by integrating Figma with your Nuxt UI components for consistent design implementation.

Why Figma Integration

  • ✅ Maintain design-dev consistency
  • ✅ Automate design token extraction
  • ✅ Reduce manual translation errors
  • ✅ Speed up development workflow
  • ✅ Enable designer-developer collaboration

Design Tokens from Figma

Figma Tokens Plugin

Install the Figma Tokens plugin in Figma to export design tokens.

Export Tokens

// design-tokens.json (exported from Figma)
{
  "colors": {
    "primary": {
      "50": { "value": "#eff6ff", "type": "color" },
      "100": { "value": "#dbeafe", "type": "color" },
      "500": { "value": "#3b82f6", "type": "color" },
      "900": { "value": "#1e3a8a", "type": "color" }
    },
    "semantic": {
      "success": { "value": "{colors.green.500}", "type": "color" },
      "error": { "value": "{colors.red.500}", "type": "color" },
      "warning": { "value": "{colors.yellow.500}", "type": "color" }
    }
  },
  "spacing": {
    "xs": { "value": "4", "type": "spacing" },
    "sm": { "value": "8", "type": "spacing" },
    "md": { "value": "16", "type": "spacing" },
    "lg": { "value": "24", "type": "spacing" },
    "xl": { "value": "32", "type": "spacing" }
  },
  "typography": {
    "fontFamily": {
      "sans": { "value": "Inter, sans-serif", "type": "fontFamilies" },
      "mono": { "value": "JetBrains Mono, monospace", "type": "fontFamilies" }
    },
    "fontSize": {
      "xs": { "value": "12", "type": "fontSizes" },
      "sm": { "value": "14", "type": "fontSizes" },
      "base": { "value": "16", "type": "fontSizes" },
      "lg": { "value": "18", "type": "fontSizes" },
      "xl": { "value": "20", "type": "fontSizes" }
    },
    "fontWeight": {
      "normal": { "value": "400", "type": "fontWeights" },
      "medium": { "value": "500", "type": "fontWeights" },
      "semibold": { "value": "600", "type": "fontWeights" },
      "bold": { "value": "700", "type": "fontWeights" }
    }
  },
  "borderRadius": {
    "none": { "value": "0", "type": "borderRadius" },
    "sm": { "value": "4", "type": "borderRadius" },
    "md": { "value": "8", "type": "borderRadius" },
    "lg": { "value": "12", "type": "borderRadius" },
    "full": { "value": "9999", "type": "borderRadius" }
  }
}

Transform Tokens to CSS Variables

// scripts/transform-tokens.ts
import fs from 'fs'
import tokens from './design-tokens.json'

function transformTokens(obj: any, prefix = ''): string {
  let css = ''
  
  for (const [key, value] of Object.entries(obj)) {
    const fullKey = prefix ? `${prefix}-${key}` : key
    
    if (typeof value === 'object' && 'value' in value) {
      // Leaf node - actual token value
      const tokenValue = resolveReference(value.value)
      css += `  --${fullKey}: ${tokenValue};\n`
    } else if (typeof value === 'object') {
      // Nested object
      css += transformTokens(value, fullKey)
    }
  }
  
  return css
}

function resolveReference(value: string): string {
  // Resolve references like {colors.primary.500}
  if (typeof value === 'string' && value.startsWith('{')) {
    const path = value.slice(1, -1).split('.')
    let current: any = tokens
    
    for (const key of path) {
      current = current[key]
    }
    
    return current.value
  }
  
  return value
}

const css = `:root {\n${transformTokens(tokens)}}
`

fs.writeFileSync('./assets/css/design-tokens.css', css)
console.log('Design tokens transformed!')

Generated CSS Variables

/* assets/css/design-tokens.css */
:root {
  --colors-primary-50: #eff6ff;
  --colors-primary-100: #dbeafe;
  --colors-primary-500: #3b82f6;
  --colors-primary-900: #1e3a8a;
  
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  
  --typography-fontFamily-sans: Inter, sans-serif;
  --typography-fontSize-base: 16px;
  --typography-fontWeight-normal: 400;
  
  --borderRadius-sm: 4px;
  --borderRadius-md: 8px;
  --borderRadius-lg: 12px;
}

Use in Nuxt

// nuxt.config.ts
export default defineNuxtConfig({
  css: ['~/assets/css/design-tokens.css']
})

UnoCSS Integration with Figma Tokens

Install UnoCSS

npm install -D @unocss/nuxt

Configure with Design Tokens

// uno.config.ts
import { defineConfig } from 'unocss'
import tokens from './design-tokens.json'

export default defineConfig({
  theme: {
    colors: {
      primary: {
        50: tokens.colors.primary['50'].value,
        100: tokens.colors.primary['100'].value,
        500: tokens.colors.primary['500'].value,
        900: tokens.colors.primary['900'].value
      }
    },
    spacing: {
      xs: `${tokens.spacing.xs.value}px`,
      sm: `${tokens.spacing.sm.value}px`,
      md: `${tokens.spacing.md.value}px`,
      lg: `${tokens.spacing.lg.value}px`,
      xl: `${tokens.spacing.xl.value}px`
    },
    borderRadius: {
      sm: `${tokens.borderRadius.sm.value}px`,
      md: `${tokens.borderRadius.md.value}px`,
      lg: `${tokens.borderRadius.lg.value}px`
    }
  }
})

Use in Components

<template>
  <button class="bg-primary-500 text-white px-md py-sm rounded-md">
    Click me
  </button>
</template>

Component Synchronization

Figma to Vue Component

Use the Figma to Code plugin to export Figma components.

Manual Component Creation

<!-- components/ui/Button.vue -->
<script setup lang="ts">
interface Props {
  variant?: 'primary' | 'secondary' | 'outline'
  size?: 'sm' | 'md' | 'lg'
  disabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  variant: 'primary',
  size: 'md',
  disabled: false
})

const buttonClasses = computed(() => {
  const base = 'inline-flex items-center justify-center font-medium transition-colors'
  
  const variants = {
    primary: 'bg-primary-500 text-white hover:bg-primary-600',
    secondary: 'bg-secondary-500 text-white hover:bg-secondary-600',
    outline: 'border-2 border-primary-500 text-primary-500 hover:bg-primary-50'
  }
  
  const sizes = {
    sm: 'text-sm px-3 py-1.5 rounded-sm',
    md: 'text-base px-4 py-2 rounded-md',
    lg: 'text-lg px-6 py-3 rounded-lg'
  }
  
  return [
    base,
    variants[props.variant],
    sizes[props.size],
    props.disabled && 'opacity-50 cursor-not-allowed'
  ].filter(Boolean).join(' ')
})
</script>

<template>
  <button
    :class="buttonClasses"
    :disabled="disabled"
  >
    <slot />
  </button>
</template>

Designer-Developer Workflow

Setup Process

  1. Design in Figma
    • Create components with consistent naming
    • Use Auto Layout for responsive designs
    • Define variants (hover, active, disabled states)
    • Apply design tokens throughout
  2. Export Design Tokens
    • Use Figma Tokens plugin
    • Export as JSON
    • Version control tokens file
  3. Transform to Code
    • Run transformation script
    • Generate CSS variables or config
    • Update UnoCSS/Tailwind config
  4. Build Components
    • Match Figma component structure
    • Implement all variants
    • Add TypeScript types
    • Test all states
  5. Review & Iterate
    • Designer reviews implementation
    • Adjust as needed
    • Update documentation

Automation Script

// scripts/sync-design-tokens.ts
import { exec } from 'child_process'
import { promisify } from 'util'
import fetch from 'node-fetch'

const execAsync = promisify(exec)

async function syncDesignTokens() {
  console.log('Fetching design tokens from Figma...')
  
  // Use Figma API to fetch tokens
  const FIGMA_TOKEN = process.env.FIGMA_TOKEN
  const FILE_KEY = process.env.FIGMA_FILE_KEY
  
  const response = await fetch(
    `https://api.figma.com/v1/files/${FILE_KEY}`,
    {
      headers: {
        'X-Figma-Token': FIGMA_TOKEN
      }
    }
  )
  
  const data = await response.json()
  
  // Extract tokens from Figma file
  // Transform to design-tokens.json
  // ... token extraction logic
  
  console.log('Running transformation...')
  await execAsync('tsx scripts/transform-tokens.ts')
  
  console.log('Design tokens synced!')
}

syncDesignTokens()

Package Script

// package.json
{
  "scripts": {
    "sync:tokens": "tsx scripts/sync-design-tokens.ts",
    "sync:tokens:watch": "nodemon --watch design-tokens.json --exec npm run sync:tokens"
  }
}

Visual Consistency Checks

Component Documentation

<!-- components/ui/Button.stories.vue -->
<script setup lang="ts">
const variants = ['primary', 'secondary', 'outline']
const sizes = ['sm', 'md', 'lg']
</script>

<template>
  <div class="stories">
    <h1>Button Component</h1>
    
    <section>
      <h2>Variants</h2>
      <div class="flex gap-4">
        <UiButton
          v-for="variant in variants"
          :key="variant"
          :variant="variant"
        >
          {{ variant }}
        </UiButton>
      </div>
    </section>
    
    <section>
      <h2>Sizes</h2>
      <div class="flex gap-4 items-center">
        <UiButton
          v-for="size in sizes"
          :key="size"
          :size="size"
        >
          {{ size }}
        </UiButton>
      </div>
    </section>
    
    <section>
      <h2>States</h2>
      <div class="flex gap-4">
        <UiButton>Default</UiButton>
        <UiButton disabled>Disabled</UiButton>
      </div>
    </section>
  </div>
</template>

Percy Visual Testing

npm install -D @percy/cli @percy/puppeteer
// tests/visual/button.spec.js
import puppeteer from 'puppeteer'
import percySnapshot from '@percy/puppeteer'

describe('Button Visual Tests', () => {
  let browser, page
  
  beforeAll(async () => {
    browser = await puppeteer.launch()
    page = await browser.newPage()
  })
  
  afterAll(async () => {
    await browser.close()
  })
  
  test('captures button variants', async () => {
    await page.goto('http://localhost:3000/stories/button')
    await percySnapshot(page, 'Button Variants')
  })
})

Design System Documentation

Auto-generated Docs

<!-- pages/design-system/index.vue -->
<script setup lang="ts">
import designTokens from '~/design-tokens.json'

const colorGroups = computed(() => {
  return Object.entries(designTokens.colors).map(([name, shades]) => ({
    name,
    shades: Object.entries(shades).map(([shade, token]) => ({
      shade,
      value: token.value
    }))
  }))
})
</script>

<template>
  <div class="design-system">
    <h1>Design System</h1>
    
    <section>
      <h2>Colors</h2>
      <div
        v-for="group in colorGroups"
        :key="group.name"
        class="color-group"
      >
        <h3>{{ group.name }}</h3>
        <div class="color-swatches">
          <div
            v-for="shade in group.shades"
            :key="shade.shade"
            class="swatch"
          >
            <div
              class="color-preview"
              :style="{ backgroundColor: shade.value }"
            />
            <div class="color-info">
              <div class="shade">{{ shade.shade }}</div>
              <div class="value">{{ shade.value }}</div>
            </div>
          </div>
        </div>
      </div>
    </section>
  </div>
</template>

<style scoped>
.color-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 1rem;
}

.swatch {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
}

.color-preview {
  height: 80px;
}

.color-info {
  padding: 0.5rem;
  font-size: 0.875rem;
}
</style>

Best Practices

✅ Do's

  1. ✅ Use design tokens as single source of truth
  2. ✅ Automate token synchronization
  3. ✅ Version control design tokens
  4. ✅ Document all component variants
  5. ✅ Use semantic naming conventions
  6. ✅ Implement visual regression testing
  7. ✅ Keep designers in the loop

❌ Don'ts

  1. ❌ Don't hardcode values in components
  2. ❌ Don't ignore design system guidelines
  3. ❌ Don't create components without Figma counterpart
  4. ❌ Don't skip documentation
  5. ❌ Don't forget responsive variants
  6. ❌ Don't mix different design systems

Advanced Integration

Figma API Integration

// utils/figma-api.ts
export class FigmaAPI {
  private token: string
  private baseURL = 'https://api.figma.com/v1'
  
  constructor(token: string) {
    this.token = token
  }
  
  async getFile(fileKey: string) {
    const response = await fetch(
      `${this.baseURL}/files/${fileKey}`,
      {
        headers: {
          'X-Figma-Token': this.token
        }
      }
    )
    return response.json()
  }
  
  async getStyles(fileKey: string) {
    const response = await fetch(
      `${this.baseURL}/files/${fileKey}/styles`,
      {
        headers: {
          'X-Figma-Token': this.token
        }
      }
    )
    return response.json()
  }
  
  async getComponents(fileKey: string) {
    const response = await fetch(
      `${this.baseURL}/files/${fileKey}/components`,
      {
        headers: {
          'X-Figma-Token': this.token
        }
      }
    )
    return response.json()
  }
}

CI/CD Token Sync

# .github/workflows/sync-tokens.yml
name: Sync Design Tokens

on:
  schedule:
    - cron: '0 0 * * *'  # Daily
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Sync tokens
        env:
          FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }}
          FIGMA_FILE_KEY: ${{ secrets.FIGMA_FILE_KEY }}
        run: npm run sync:tokens
      
      - name: Create PR if changed
        uses: peter-evans/create-pull-request@v5
        with:
          commit-message: 'chore: sync design tokens from Figma'
          title: 'Sync Design Tokens'
          branch: 'sync-design-tokens'

Integrating Figma with Nuxt creates a seamless design-to-development workflow. Automate design token extraction and maintain visual consistency across your application.

Nuxt UI Best Practices
Tips and patterns for building great UIs with Nuxt UI.
Files
Editor
Initializing WebContainer
Mounting files
Installing dependencies
Starting Nuxt server
Waiting for Nuxt to ready
Terminal