Transform 3 Figma screens (Desktop, Tablet, Mobile) into a single responsive component with pure CSS media queries
╔════════════════════════════════════════════════════════════╗
║ ║
║ Desktop (1440px) ──┐ ║
║ ├──> Responsive Merger ──> Page.tsx ║
║ Tablet (960px) ───┤ ║
║ │ + Media Queries ║
║ Mobile (420px) ───┘ ║
║ ║
╚════════════════════════════════════════════════════════════╝
- Overview
- How It Works
- Pipeline Architecture
- Usage
- Output Structure
- Responsive Transformations
- Puck Integration
- API Endpoints
- Technical Details
- Troubleshooting
The Responsive Merge feature is a powerful tool that combines 3 separately exported Figma screens into a single responsive component. Unlike traditional responsive design where you adapt one design, this system intelligently merges three complete screen designs while preserving visual fidelity across all breakpoints.
✅ Intelligent Component Detection - Automatically identifies common components across breakpoints
✅ Pure CSS Media Queries - No Tailwind or framework dependencies
✅ Conflict Resolution - Detects and resolves className conflicts between breakpoints
✅ Modular Architecture - Generates Page.tsx + reusable Subcomponents/
✅ Helper Function Injection - Automatically extracts and injects shared utilities
✅ Visual Validation - Side-by-side comparison reports
✅ Puck Integration - Visual editor-ready components with drag-and-drop
✅ Desktop-First Approach - Prioritizes desktop layout with mobile/tablet overrides
Before creating a responsive merge, you need 3 separate Figma exports:
- Desktop - Typically 1440px width
- Tablet - Typically 960px width
- Mobile - Typically 420px width
Important: Each export must be processed with --split-components flag to generate modular components.
# Export each screen separately
docker exec mcp-figma-v1 node scripts/figma-cli.js \
"https://figma.com/design/FILE?node-id=DESKTOP" \
--split-components
docker exec mcp-figma-v1 node scripts/figma-cli.js \
"https://figma.com/design/FILE?node-id=TABLET" \
--split-components
docker exec mcp-figma-v1 node scripts/figma-cli.js \
"https://figma.com/design/FILE?node-id=MOBILE" \
--split-componentsThe system matches components across breakpoints using:
- Exact name matching - Components with identical names (e.g.,
Header,Footer) - Normalized names - "title section" → "Titlesection"
- Common components only - Only components present in all 3 breakpoints are merged
Example:
Desktop: Header, Hero, Features, Footer
Tablet: Header, Hero, Features, Footer
Mobile: Header, Hero, Footer
→ Common: Header, Hero, Footer (Features skipped - not in mobile)
The merge happens in 4 phases:
- Scan all 3 exports for modular components
- Identify common components
- Determine component order from Desktop
metadata.xml - Extract helper functions from Desktop
Component-clean.tsx
For each common component:
- Parse Desktop, Tablet, Mobile
.tsxfiles into AST - Run Responsive Pipeline (7 specialized transforms)
- Merge classNames with conflict detection
- Inject helper functions if needed
- Fix image import paths (
./img/→../img/) - Generate responsive TSX code
For each component:
- Parse Desktop, Tablet, Mobile
.cssfiles - Extract Desktop styles (baseline)
- Calculate Tablet overrides (differences from Desktop)
- Calculate Mobile overrides (differences from Tablet)
- Generate media queries:
/* Desktop styles (default) */ .header { height: 80px; } /* Tablet overrides */ @media (max-width: 960px) { .header { height: 60px; } } /* Mobile overrides */ @media (max-width: 420px) { .header { height: 50px; } }
- Parse Desktop
Component-clean.tsxstructure - Merge with Tablet/Mobile versions
- Replace
<div data-name="...">with<ComponentName /> - Generate
Page.tsxwith all imports - Generate
Page.csswith component imports - Compile responsive classes to pure CSS
- Generate Puck-ready components
- Create visual report + technical analysis
The Responsive Pipeline includes 7 specialized transforms:
| Priority | Transform | Purpose |
|---|---|---|
| 10 | detect-missing-elements |
Find elements missing in tablet/mobile |
| 20 | normalize-identical-classes |
Normalize className formatting across breakpoints |
| 30 | detect-class-conflicts |
Detect className differences (by data-name or position) |
| 40 | merge-desktop-first |
Merge classNames (Desktop base + Tablet/Mobile overrides) |
| 50 | add-horizontal-scroll |
Add overflow-x: auto to prevent layout breaks |
| 60 | reset-dependent-properties |
Reset conflicting properties (width, height, etc.) |
| 70 | inject-visibility-classes |
Add visibility classes (max-md:hidden, max-lg:block) |
Desktop AST ──┐
├──> Detect Missing Elements
Tablet AST ──┤
│──> Normalize ClassNames
Mobile AST ──┘
│
├──> Detect Conflicts (data-name matching)
│
├──> Merge Desktop-First (base + overrides)
│
├──> Add Horizontal Scroll (prevent breaks)
│
├──> Reset Dependent Properties
│
└──> Inject Visibility Classes
│
└──> Generate Responsive TSX
- Navigate to Responsive Merges page
- Click "New Responsive Merge"
- Select 3 exports:
- Desktop - Select desktop export + enter width (e.g., 1440)
- Tablet - Select tablet export + enter width (e.g., 960)
- Mobile - Select mobile export + enter width (e.g., 420)
- Click "Launch Merge"
- Watch real-time logs in progress modal
- View result in Responsive Merges list
docker exec mcp-figma-v1 node scripts/responsive-merger.js \
--desktop 1440 node-6055-2436-1762733564 \
--tablet 960 node-6055-2654-1762712319 \
--mobile 420 node-6055-2872-1762733537Arguments:
--desktop <width> <exportId>- Desktop export and breakpoint width--tablet <width> <exportId>- Tablet export and breakpoint width--mobile <width> <exportId>- Mobile export and breakpoint width
Validation:
- Breakpoint order must be: Desktop > Tablet > Mobile
- All exports must have
modular/directory (requires--split-components)
Each merge creates a folder in src/generated/responsive-screens/:
responsive-merger-{timestamp}/
├── Page.tsx # Main page component
├── Page.css # Consolidated CSS with media queries
├── Subcomponents/ # Modular components
│ ├── Header.tsx
│ ├── Header.css
│ ├── Hero.tsx
│ ├── Hero.css
│ ├── Footer.tsx
│ └── Footer.css
├── img/ # Images (from Desktop export)
│ ├── logo.png
│ └── hero-bg.jpg
├── puck/ # Puck editor components
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── Hero.tsx
│ │ └── Footer.tsx
│ ├── config.tsx # Puck configuration
│ └── data.json # Initial Puck data
├── responsive-metadata.json # Merge metadata + stats
├── responsive-analysis.md # Technical analysis report
└── responsive-report.html # Visual comparison report
{
"timestamp": "2025-01-13T10:30:00.000Z",
"mergeId": "responsive-merger-1736762400000",
"type": "responsive-merge",
"breakpoints": {
"desktop": {
"testId": "node-6055-2436-1762733564",
"screenSize": "1440px",
"width": 1440,
"height": 900
},
"tablet": { /* ... */ },
"mobile": { /* ... */ }
},
"mediaQueries": {
"tablet": "@media (max-width: 960px)",
"mobile": "@media (max-width: 420px)"
},
"components": ["Header", "Hero", "Features", "Footer"],
"transformations": {
"totalElementsProcessed": 42,
"totalClassesMerged": 128,
"matchingStrategy": {
"byDataName": 35,
"byPosition": 7
},
"conflicts": {
"elementsWithConflicts": 12,
"totalConflicts": 18
},
"elementsMerged": 42,
"horizontalScrollAdded": 3,
"resetsApplied": 8,
"visibilityClassesInjected": 5
}
}Purpose: Identify elements that exist in Desktop but are missing in Tablet/Mobile.
Example:
// Desktop has:
<div data-name="sidebar">...</div>
// Mobile doesn't have sidebar
→ Logged as missing elementStats:
elementsDetected- Total elements scannedelements- List of missing element data-names
Purpose: Ensure consistent className formatting across breakpoints.
Example:
// Desktop: className="flex gap-4 items-center"
// Tablet: className="items-center flex gap-4"
// Mobile: className="flex items-center gap-4"
→ All normalized to: "flex gap-4 items-center"Purpose: Find elements with different classNames across breakpoints.
Matching Strategy:
- By data-name (preferred) - Match
<div data-name="header"> - By position (fallback) - Match by index in parent
Example:
// Desktop: <div data-name="card" className="w-full p-4">
// Tablet: <div data-name="card" className="w-full p-3">
// Mobile: <div data-name="card" className="w-full p-2">
→ Conflict detected on paddingStats:
matchedByDataName- Elements matched by data-namematchedByPosition- Elements matched by positionelementsWithConflicts- Total elements with className differencestotalConflicts- Total className properties that differ
Purpose: Merge classNames using desktop-first approach with media query overrides.
Strategy:
Desktop classes (base, no media query)
↓
Calculate differences: Tablet - Desktop
↓
Add Tablet overrides with max-md: prefix
↓
Calculate differences: Mobile - Tablet
↓
Add Mobile overrides with max-lg: prefix
Example:
// Desktop: className="w-96 h-80 p-4"
// Tablet: className="w-80 h-64 p-3"
// Mobile: className="w-full h-48 p-2"
→ Merged: "w-96 h-80 p-4 max-md:w-80 max-md:h-64 max-md:p-3 max-lg:w-full max-lg:h-48 max-lg:p-2"Note: These responsive classes are then compiled to pure CSS by responsive-css-compiler.js.
Stats:
elementsMerged- Total elements with merged classNamestotalClassesMerged- Total className properties merged
Purpose: Prevent layout breaks on narrow screens by adding horizontal scroll.
Targets:
- Elements with
flex-rowlayout - Elements wider than viewport
- Grid layouts with fixed columns
Example:
// Before
<div className="flex flex-row gap-4">
// After
<div className="flex flex-row gap-4 max-md:overflow-x-auto">Stats:
parentsUpdated- Elements with overflow-x added
Purpose: Reset conflicting properties when breakpoints change dimensions.
Resets Applied:
- Width conflicts → Add
max-md:w-auto - Height conflicts → Add
max-md:h-auto - Flex-basis conflicts → Add
max-md:flex-basis-auto
Example:
// Desktop: w-96
// Tablet: w-80
// Mobile: w-full
→ Add resets: max-md:w-auto max-lg:w-autoStats:
totalResetsAdded- Number of reset classes added
Purpose: Hide/show elements based on breakpoint.
Use Cases:
- Desktop-only navigation
- Mobile-only hamburger menu
- Tablet-specific layouts
Example:
// Element missing in mobile
→ Add: max-lg:hidden
// Element only in mobile
→ Add: hidden max-lg:blockStats:
visibilityClassesInjected- Total visibility classes added
Puck is a visual editor for React. The Responsive Merge generates Puck-ready components for easy visual customization.
puck/
├── components/ # Puck-wrapped components
│ ├── Header.tsx
│ ├── Hero.tsx
│ └── Footer.tsx
├── config.tsx # Puck configuration
└── data.json # Initial Puck data
- Navigate to Responsive Merge detail page
- Click "Open in Puck Editor" tab
- Drag and drop components
- Edit props in real-time
- Save changes back to
puck/data.json
// Get Puck config
GET /api/responsive-merges/:mergeId/puck-config
// Get Puck data
GET /api/responsive-merges/:mergeId/puck-data
// Save Puck data
POST /api/responsive-merges/:mergeId/puck-save
Body: { data: {...} }POST /api/responsive-merges
Content-Type: application/json
{
"desktop": {
"size": "1440",
"exportId": "node-6055-2436-1762733564"
},
"tablet": {
"size": "960",
"exportId": "node-6055-2654-1762712319"
},
"mobile": {
"size": "420",
"exportId": "node-6055-2872-1762733537"
}
}Response:
{
"success": true,
"jobId": "merge-1736762400000",
"mergeId": "responsive-merger-1736762400000",
"message": "Merge responsive lancé avec succès"
}GET /api/responsive-merges/logs/:jobIdSSE Events:
// Log event
{ "type": "log", "message": "🔍 Validating breakpoints\n" }
// Done event
{ "type": "done", "success": true }
// Error event
{ "type": "error", "message": "Error message" }GET /api/responsive-mergesResponse:
[
{
"mergeId": "responsive-merger-1736762400000",
"timestamp": 1736762400000,
"components": ["Header", "Hero", "Footer"],
"breakpoints": { /* ... */ }
}
]GET /api/responsive-merges/:mergeId/dataResponse:
{
"metadata": { /* responsive-metadata.json */ },
"analysis": "# Technical Analysis\n..."
}DELETE /api/responsive-merges/:mergeIdGET /api/responsive-merges/:mergeId/downloadReturns ZIP archive with all files.
Responsive classes like max-md:w-80 are compiled to pure CSS by responsive-css-compiler.js:
// Input (TSX)
<div className="w-96 max-md:w-80 max-lg:w-full" />
// Output (CSS)
.w-custom-96 { width: 24rem; }
@media (max-width: 960px) {
.max-md-w-custom-80 { width: 20rem; }
}
@media (max-width: 420px) {
.max-lg-w-custom-full { width: 100%; }
}Supported Prefixes:
max-md:→ Tablet and below (≤960px)max-lg:→ Mobile and below (≤420px)
Helper functions from Desktop Component-clean.tsx are automatically extracted and injected into subcomponents that use them.
Example:
// Desktop Component-clean.tsx
function formatCurrency(amount) {
return `$${amount.toFixed(2)}`
}
// Subcomponents/PriceCard.tsx uses {formatCurrency(price)}
→ Helper is automatically injected into PriceCard.tsxImport Path Fixes:
./img/logo.png→../img/logo.png(subcomponents are nested)
Component order is extracted from Desktop metadata.xml hierarchy to preserve visual layout:
<frame name="HomePage">
<frame name="Header" />
<frame name="Hero Section" /> ← "Hero Section" → "Herosection"
<frame name="Footer" />
</frame>
→ Order: Header, Herosection, FooterProblem: Export was not split into modular components.
Solution:
# Re-export with --split-components flag
docker exec mcp-figma-v1 node scripts/figma-cli.js \
"FIGMA_URL" \
--split-componentsProblem: Component names don't match across breakpoints.
Solution:
- Ensure Figma layer names are identical across screens
- Check component naming normalization (spaces, hyphens)
- Verify all breakpoints have the same components
Problem: Breakpoint widths not in descending order.
Solution:
# Correct order: Desktop > Tablet > Mobile
--desktop 1440 ... \
--tablet 960 ... \
--mobile 420 ...
# NOT: --desktop 960 --tablet 1440 (wrong!)Problem: Responsive classes not generating CSS.
Solution:
- Check
responsive-css-compiler.jsis running - Verify
Page.cssincludes compiled classes - Look for errors in merge logs
Problem: Components not appearing in Puck.
Solution:
- Check
puck/config.tsxexists - Verify
puck/data.jsonis valid JSON - Check browser console for errors
Problem: Images broken in subcomponents.
Solution:
- Images are copied from Desktop export only
- Subcomponents use
../img/path - Verify
img/directory exists in merge output
| Script | Purpose |
|---|---|
| responsive-merger.js | Main orchestrator |
| responsive-pipeline.js | AST transform pipeline |
| responsive-css-compiler.js | Compile responsive classes to CSS |
| puck-generator.js | Generate Puck components |
| generate-responsive-report.js | Visual comparison report |
| generate-responsive-analysis.js | Technical analysis |
| Component | Purpose |
|---|---|
| MergeDialog.tsx | Merge creation UI |
| ResponsiveMergesPage.tsx | Merge list page |
| ResponsiveMergeDetailPage.tsx | Merge detail view |
| ResponsivePreviewPage.tsx | Responsive preview |
| PuckEditorPage.tsx | Puck editor |
| PuckRenderPage.tsx | Puck render view |
- Try Responsive Merge - Create your first merge
- Explore Puck Editor - Visual customization
- Read Architecture Guide - Understand the full pipeline
- View API Reference - Integration endpoints
Made with ❤️ for responsive design