Skip to main content
Illustration of design system components on a canvas flowing into a grid of consistent web pages
Topic: Design and user experience excellence

From Figma to Drupal: building design systems that scale

By Brian Danin

The handoff between design and development is where beautiful visions go to die. Designers create pixel-perfect interfaces in Figma. Developers build functional components in Drupal. And somewhere in between, consistency evaporates like morning dew.

The problem isn't lack of talent—it's lack of system.

A design system is the bridge between Figma and your CMS, ensuring that what designers envision is what developers build, and what gets deployed stays consistent as your site grows. But most organizations approach design systems backwards: they try to document what exists rather than establishing a shared language from the start.

Here's how to build a design system that actually scales—from Figma artboards to Drupal production.

What a design system actually is

A design system is not: - ❌ A style guide (though it includes one) - ❌ A component library (though it contains one) - ❌ Brand guidelines (though it incorporates them) - ❌ A Figma file (though it lives there too)

A design system is: - ✅ A shared language between design and development - ✅ A set of reusable components with consistent behavior - ✅ Design tokens that define visual properties programmatically - ✅ Documentation that explains when and how to use each piece - ✅ Governance that ensures consistency over time

Most importantly: A design system is a single source of truth that exists in both design tools and production code, kept in sync through deliberate process.

Design System Workflow

From Figma to Drupal Production

Token Architecture

Design Tokens

color.primary

Source of truth

Figma Variables

Primary/500

Design tool format

SCSS Variables

$color-primary

Build-time values

CSS Custom Properties

--color-primary

Runtime variables

Component Styles

var(--color-primary)

Applied in components

Example Token Flow

color.primary → Primary/500: #3B82F6 → $color-primary: #3B82F6 → --color-primary: #3B82F6 → background: var(--color-primary)

Main Workflow

Figma Design

Design tokens & components

Token Export

JSON format variables

Code Generation

SCSS & CSS output

Drupal Components

Twig templates

Production Site

Live deployment

Component Hierarchy (Atomic Design)

Atoms

Basic building blocks

Button
Input
Label
Icon

Molecules

Simple component groups

Form Field
Search Bar
Card Header
Menu Item

Organisms

Complex UI sections

Navigation Bar
Product Card
Contact Form
Footer

Templates

Page-level structures

Homepage Layout
Article Template
Dashboard Layout

Pages

Complete instances

Home
About Us
Product Page
Contact

How Components Build Up

Atoms combine to form Molecules → Molecules combine to form Organisms → Organisms are arranged in Templates → Templates are populated with real content to create Pages

Sync Workflow

Progress 0 / 5

If you're interested in how design systems fit into broader development workflows, check out these related posts:

The token foundation

Design tokens are the atomic units of your design system—the named values that define visual properties across your entire system.

What are design tokens?

Instead of hardcoding values everywhere:

/* ❌ Bad: Magic numbers everywhere */
.button {
  padding: 12px 24px;
  background: #0066CC;
  border-radius: 4px;
  font-size: 16px;
  font-weight: 600;
}

.card {
  padding: 24px;
  background: #0066CC;
  border-radius: 8px;
}

Use named tokens that represent decisions:

/* ✅ Good: Semantic tokens */
.button {
  padding: var(--spacing-md) var(--spacing-lg);
  background: var(--color-primary);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
}

.card {
  padding: var(--spacing-lg);
  background: var(--color-primary);
  border-radius: var(--radius-md);
}

Token categories

Color Tokens:

{
  "color": {
    "brand": {
      "primary": "#0066CC",
      "secondary": "#2D7738",
      "accent": "#F59E0B"
    },
    "neutral": {
      "900": "#1F2937",
      "700": "#374151",
      "500": "#6B7280",
      "300": "#D1D5DB",
      "100": "#F3F4F6"
    },
    "semantic": {
      "success": "#2D7738",
      "warning": "#F59E0B",
      "error": "#DC2626",
      "info": "#0066CC"
    }
  }
}

Spacing Tokens:

{
  "spacing": {
    "xs": "4px",
    "sm": "8px",
    "md": "12px",
    "lg": "24px",
    "xl": "32px",
    "2xl": "48px",
    "3xl": "64px"
  }
}

Typography Tokens:

{
  "typography": {
    "font-family": {
      "sans": "Inter, -apple-system, sans-serif",
      "serif": "Georgia, serif",
      "mono": "Consolas, monospace"
    },
    "font-size": {
      "xs": "12px",
      "sm": "14px",
      "base": "16px",
      "lg": "18px",
      "xl": "20px",
      "2xl": "24px",
      "3xl": "30px",
      "4xl": "36px"
    },
    "font-weight": {
      "normal": "400",
      "medium": "500",
      "semibold": "600",
      "bold": "700"
    },
    "line-height": {
      "tight": "1.25",
      "base": "1.5",
      "relaxed": "1.75"
    }
  }
}

Border Radius Tokens:

{
  "radius": {
    "none": "0",
    "sm": "4px",
    "md": "8px",
    "lg": "12px",
    "full": "9999px"
  }
}

Shadow Tokens:

{
  "shadow": {
    "sm": "0 1px 2px rgba(0, 0, 0, 0.05)",
    "md": "0 4px 6px rgba(0, 0, 0, 0.1)",
    "lg": "0 10px 15px rgba(0, 0, 0, 0.1)",
    "xl": "0 20px 25px rgba(0, 0, 0, 0.15)"
  }
}

Implementing tokens in Figma

1. Create Figma Variables (Figma's native token system):

In Figma, create variable collections: - Colors → Map to color tokens - Spacing → Use for consistent padding/margins - Typography → Font sizes, weights, line heights - Border Radius → Consistent corner rounding

2. Use Figma Styles for Typography and Colors:

Create text styles that reference your tokens: - Heading/H1 → font: sans, size: 4xl, weight: bold, line-height: tight - Heading/H2 → font: sans, size: 3xl, weight: bold, line-height: tight - Body/Large → font: sans, size: lg, weight: normal, line-height: relaxed - Body/Base → font: sans, size: base, weight: normal, line-height: base

3. Export Tokens Programmatically:

Use plugins like Figma Tokens or Design Tokens to export your Figma variables as JSON:

# Install Figma Tokens plugin
# Configure token sets
# Export to tokens.json

Output:

{
  "global": {
    "color": {
      "primary": {
        "value": "#0066CC",
        "type": "color"
      }
    },
    "spacing": {
      "md": {
        "value": "12px",
        "type": "spacing"
      }
    }
  }
}

Building components in Figma

Component architecture

Structure your Figma components to match your planned Drupal component architecture:

Atomic Design Hierarchy:

Atoms (smallest units): - Buttons - Input fields - Labels - Icons - Badges

Molecules (simple combinations): - Form fields (label + input + error message) - Search box (input + button) - Card header (title + badge)

Organisms (complex combinations): - Navigation bar - Hero section - Card (header + body + footer) - Form (multiple fields + submit button)

Templates (page layouts): - Article page - Landing page - List page - Dashboard

Figma component best practices

1. Use Component Properties (Variants + Props):

Button (Component Set)
├─ Variant: Primary
│  ├─ State: Default
│  ├─ State: Hover
│  ├─ State: Disabled
│  └─ Size: Small, Medium, Large
├─ Variant: Secondary
│  ├─ State: Default
│  ├─ State: Hover
│  └─ ...
└─ Variant: Ghost
   └─ ...

2. Use Auto Layout:

Configure auto layout properties that match your CSS: - Padding: Use spacing tokens (xs, sm, md, lg) - Gap: Space between items - Flex direction: Row or column - Alignment: Flex start, center, end

3. Name Components to Match Code:

// Figma component name
Button/Primary/Medium/Default

// Becomes Drupal component
components/button/button--primary.html.twig

4. Create Component Documentation in Figma:

Use Figma's description field to document: - When to use: "Use primary buttons for main actions" - Accessibility: "Must have accessible label" - States: "Supports default, hover, focus, disabled" - Props: "Required: text. Optional: icon, size"

Translating design to Drupal

Drupal component architecture

Drupal's component structure maps well to design systems:

Directory Structure:

web/themes/custom/yourtheme/
├── components/
│   ├── atoms/
│   │   ├── button/
│   │   │   ├── button.twig
│   │   │   ├── button.scss
│   │   │   └── button.yml (component metadata)
│   │   ├── input/
│   │   └── icon/
│   ├── molecules/
│   │   ├── card/
│   │   ├── form-field/
│   │   └── search-box/
│   ├── organisms/
│   │   ├── header/
│   │   ├── footer/
│   │   └── hero/
│   └── templates/
│       ├── page/
│       └── node/
├── assets/
│   ├── tokens/
│   │   └── tokens.json (exported from Figma)
│   ├── scss/
│   │   ├── _tokens.scss (generated from tokens.json)
│   │   └── _variables.scss
│   └── js/
└── patterns/
    └── pattern-lab/ (optional: living style guide)

Converting tokens to Drupal/SCSS

1. Transform JSON tokens to SCSS variables:

Use a build tool like Style Dictionary or Theo:

// build-tokens.js
const StyleDictionary = require('style-dictionary');

const sd = StyleDictionary.extend({
  source: ['assets/tokens/**/*.json'],
  platforms: {
    scss: {
      transformGroup: 'scss',
      buildPath: 'assets/scss/',
      files: [{
        destination: '_tokens.scss',
        format: 'scss/variables'
      }]
    },
    css: {
      transformGroup: 'css',
      buildPath: 'assets/css/',
      files: [{
        destination: 'tokens.css',
        format: 'css/variables'
      }]
    }
  }
});

sd.buildAllPlatforms();

Input (tokens.json):

{
  "color": {
    "primary": { "value": "#0066CC" }
  },
  "spacing": {
    "md": { "value": "12px" }
  }
}

Output (_tokens.scss):

$color-primary: #0066CC;
$spacing-md: 12px;

:root {
  --color-primary: #{$color-primary};
  --spacing-md: #{$spacing-md};
}

2. Use tokens in component styles:

// components/atoms/button/button.scss
@import '../../../assets/scss/tokens';

.button {
  padding: var(--spacing-md) var(--spacing-lg);
  background: var(--color-primary);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-sm);
  font-family: var(--font-family-sans);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: white;
  transition: all 0.2s;

  &:hover {
    background: var(--color-primary-dark);
    border-color: var(--color-primary-dark);
  }

  &--secondary {
    background: transparent;
    color: var(--color-primary);

    &:hover {
      background: var(--color-primary-light);
    }
  }

  &--large {
    padding: var(--spacing-lg) var(--spacing-xl);
    font-size: var(--font-size-lg);
  }
}

Building Twig components

Button Component Example:

{# components/atoms/button/button.twig #}
{%
  set classes = [
    'button',
    variant ? 'button--' ~ variant,
    size ? 'button--' ~ size,
    disabled ? 'button--disabled',
  ]
%}

<button
  class="{{ classes|join(' ')|trim }}"
  type="{{ type|default('button') }}"
  {{ disabled ? 'disabled' }}
  {{ attributes }}
>
  {% if icon %}
    {% include '@atoms/icon/icon.twig' with {
      name: icon,
      size: 'sm'
    } %}
  {% endif %}
  {{ text }}
</button>

Component Metadata (button.yml):

'$schema': 'https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json'
name: Button
description: 'Reusable button component with variants'
props:
  type: object
  properties:
    text:
      type: string
      title: Button Text
      description: The text displayed in the button
      required: true
    variant:
      type: string
      title: Variant
      description: Visual style of the button
      enum:
        - primary
        - secondary
        - ghost
      default: primary
    size:
      type: string
      title: Size
      enum:
        - small
        - medium
        - large
      default: medium
    icon:
      type: string
      title: Icon
      description: Optional icon name
    disabled:
      type: boolean
      title: Disabled
      default: false
    type:
      type: string
      title: Button Type
      enum:
        - button
        - submit
        - reset
      default: button

Using the Component:

{# In a template or another component #}
{% include '@atoms/button/button.twig' with {
  text: 'Get Started',
  variant: 'primary',
  size: 'large',
  icon: 'arrow-right'
} %}

Card component (more complex example)

Figma Structure:

Card (Component Set)
├─ Header
│  ├─ Title (text)
│  └─ Badge (optional)
├─ Body
│  └─ Content (slot)
└─ Footer
   └─ Actions (slot)

Drupal Implementation:

{# components/molecules/card/card.twig #}
{%
  set classes = [
    'card',
    variant ? 'card--' ~ variant,
  ]
%}

<article class="{{ classes|join(' ')|trim }}" {{ attributes }}>
  {% if header %}
    <header class="card__header">
      <h3 class="card__title">{{ header.title }}</h3>
      {% if header.badge %}
        {% include '@atoms/badge/badge.twig' with header.badge %}
      {% endif %}
    </header>
  {% endif %}

  <div class="card__body">
    {{ body }}
  </div>

  {% if footer %}
    <footer class="card__footer">
      {{ footer }}
    </footer>
  {% endif %}
</article>

Card SCSS:

// components/molecules/card/card.scss
@import '../../../assets/scss/tokens';

.card {
  background: white;
  border: 1px solid var(--color-neutral-300);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.2s;

  &:hover {
    box-shadow: var(--shadow-md);
  }

  &__header {
    padding: var(--spacing-lg);
    border-bottom: 1px solid var(--color-neutral-200);
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  &__title {
    margin: 0;
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    color: var(--color-neutral-900);
  }

  &__body {
    padding: var(--spacing-lg);
    color: var(--color-neutral-700);
    line-height: var(--line-height-relaxed);
  }

  &__footer {
    padding: var(--spacing-md) var(--spacing-lg);
    background: var(--color-neutral-100);
    border-top: 1px solid var(--color-neutral-200);
  }

  &--featured {
    border: 2px solid var(--color-primary);
    box-shadow: var(--shadow-lg);
  }
}

Usage:

{% include '@molecules/card/card.twig' with {
  variant: 'featured',
  header: {
    title: 'Featured Article',
    badge: {
      text: 'New',
      variant: 'success'
    }
  },
  body: content,
  footer: include('@atoms/button/button.twig', {
    text: 'Read More',
    variant: 'secondary'
  })
} %}

Keeping Figma and Drupal in sync

The biggest challenge with design systems: maintaining consistency over time.

Workflow for updates

1. Design Changes Start in Figma: - Update design tokens in Figma variables - Update component designs - Document changes in component descriptions

2. Export Tokens:

# Export tokens from Figma using plugin
# Place in assets/tokens/tokens.json

3. Generate Code:

# Run token build script
npm run build-tokens

# Output: Updated _tokens.scss with new values

4. Update Components: - Review component styles for token usage - Update any hardcoded values to use tokens - Test components in Storybook or Pattern Lab

5. Document Changes:

## Changelog

### 2025-10-27
- Updated primary color from #0055B8 to #0066CC
- Added new spacing token: 3xl (64px)
- Button component: Added 'large' size variant
- Card component: Updated shadow from md to lg on hover

Automation opportunities

GitHub Actions Workflow:

# .github/workflows/design-tokens.yml
name: Update Design Tokens

on:
  push:
    paths:
      - 'assets/tokens/**'

jobs:
  build-tokens:
    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 install

      - name: Build tokens
        run: npm run build-tokens

      - name: Commit generated files
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"
          git add assets/scss/_tokens.scss
          git commit -m "chore: Update generated token files" || echo "No changes"
          git push

Figma API Integration (Advanced):

// sync-figma-tokens.js
const Figma = require('figma-api');
const fs = require('fs');

const api = new Figma.Api({
  personalAccessToken: process.env.FIGMA_TOKEN
});

async function syncTokens() {
  // Fetch Figma file
  const file = await api.getFile(process.env.FIGMA_FILE_ID);

  // Extract variables/styles
  const tokens = extractTokensFromFile(file);

  // Write to tokens.json
  fs.writeFileSync(
    'assets/tokens/tokens.json',
    JSON.stringify(tokens, null, 2)
  );

  console.log('Tokens synced from Figma');
}

syncTokens();

Documentation and governance

Living style guide

Use Storybook or Pattern Lab to create a living documentation site:

Storybook Example:

// components/atoms/button/button.stories.js
import button from './button.twig';
import './button.scss';

export default {
  title: 'Atoms/Button',
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'ghost']
    },
    size: {
      control: 'select',
      options: ['small', 'medium', 'large']
    },
    disabled: {
      control: 'boolean'
    }
  }
};

const Template = (args) => button(args);

export const Primary = Template.bind({});
Primary.args = {
  text: 'Primary Button',
  variant: 'primary',
  size: 'medium'
};

export const Secondary = Template.bind({});
Secondary.args = {
  text: 'Secondary Button',
  variant: 'secondary',
  size: 'medium'
};

export const Large = Template.bind({});
Large.args = {
  text: 'Large Button',
  variant: 'primary',
  size: 'large'
};

export const WithIcon = Template.bind({});
WithIcon.args = {
  text: 'Button with Icon',
  variant: 'primary',
  icon: 'arrow-right'
};

Run Storybook:

npm run storybook
# Opens http://localhost:6006 with component playground

Governance model

Who owns what:

Design Team: - Owns Figma source of truth - Proposes new components and patterns - Documents design decisions - Approves visual changes

Development Team: - Owns Drupal implementation - Ensures technical feasibility - Maintains component code quality - Approves technical changes

Product/Content Team: - Validates component usefulness - Provides real-world usage feedback - Requests new patterns based on needs

Design System Core Team (Cross-functional): - Reviews all proposed changes - Maintains documentation - Ensures consistency - Manages releases

Contribution process

1. Propose New Component:

## Component Proposal: Alert Banner

### Use Case
Display important messages to users (errors, warnings, success, info)

### Variants Needed
- Error (red)
- Warning (amber)
- Success (green)
- Info (blue)

### Props
- message (string, required)
- variant (enum, required)
- dismissible (boolean, optional)
- icon (string, optional)

### Design
[Link to Figma prototype]

### Questions for Review
- Should this be dismissible by default?
- Do we need an "action" prop for CTA buttons?

2. Design Review: - Design team creates Figma component - Reviews with stakeholders - Approves for development

3. Development: - Dev creates Drupal component - Matches Figma specifications - Writes Storybook documentation - Creates unit tests

4. Review & Merge: - Design team verifies visual match - Code review by senior dev - Merge to design system - Update changelog

Real-world implementation tips

Start small, scale gradually

Phase 1: Foundation (Month 1) - Establish design tokens - Create 3-5 atoms (button, input, heading, paragraph, link) - Set up build process - Document workflow

Phase 2: Core Components (Months 2-3) - Build essential molecules (form field, card, navigation item) - Create 2-3 organisms (header, footer, hero) - Establish pattern library - Train team on usage

Phase 3: Expansion (Months 4-6) - Add domain-specific components - Refine based on real usage - Optimize workflow - Improve documentation

Common pitfalls to avoid

❌ Trying to document everything upfront - Start with what you need, expand as you grow

❌ Making components too generic - "One-size-fits-all" becomes "fits-nothing-well"

❌ Making components too specific - Too many variants = maintenance nightmare

❌ Not involving developers early - Beautiful designs that are impossible to build

❌ No clear governance - Everyone adding components = chaos

❌ Treating it as one-time project - Design systems are living, evolving systems

Success metrics

Efficiency Metrics: - Time to build new page (should decrease) - Component reuse rate (should increase) - Design-to-dev handoff time (should decrease)

Quality Metrics: - Visual consistency across site (audit periodically) - Accessibility compliance rate - Performance scores (smaller CSS, less duplication)

Adoption Metrics: - % of pages using design system components - % of new features built with system - Team satisfaction scores

Tools and resources

Design Tools: - Figma - Design and prototyping - Figma Tokens - Plugin for token management - Design Tokens - Alternative token plugin

Build Tools: - Style Dictionary - Transform tokens to multiple formats - Theo - Salesforce's token tool - Token Transformer - Figma Tokens → Style Dictionary

Component Development: - Storybook - Component playground and documentation - Pattern Lab - Alternative to Storybook - Drupal SDC - Single Directory Components module

Version Control: - Abstract - Version control for Figma (if needed) - Git - Version control for code - GitHub/GitLab - Repository hosting

Documentation: - Storybook Docs - Auto-generated documentation - Zeroheight - Design system documentation platform - Notion/Confluence - General documentation

The bottom line

A design system isn't about restricting creativity—it's about creating a shared language that makes teams faster and products better.

With a well-implemented design system: - ✅ Designers spend time solving problems, not reinventing buttons - ✅ Developers build faster with reusable components - ✅ Products stay visually consistent as they scale - ✅ New team members onboard quicker - ✅ Accessibility and performance improve across the board

The key is the bridge: Figma and Drupal must speak the same language through design tokens, shared naming conventions, and documented patterns.

Start with tokens. Build atoms. Compose molecules. Document as you go. Govern with intention.

Your design system will never be "done"—but that's the point. It should evolve as your product and team evolve, always staying slightly ahead of your current needs.

Ready to build a design system that bridges Figma and Drupal? Let's map out your component architecture and create a system that scales with your ambitions.

Great design systems don't constrain—they liberate teams to move faster with confidence.

More from Design and user experience excellence

View all

Ready to get started?

Contact us to discuss your business needs