figma-to-landing-page
Figma to Landing Page
1. Overview
Converts Figma designs into production-ready Nuxt 3 pages composed from @rds-vue-ui/* components. This skill implements a two-tier approach to accommodate different Figma account levels:
| Tier | Input | Context Quality | When Used |
|---|---|---|---|
| Rich Path (Dev+) | Figma URL | Full layout tree, typography, colors, component structure | get_design_context returns rich data |
| Screenshot Path (Basic) | Figma URL, prototype URL, or image | Visual analysis only | get_design_context fails, returns limited data, or user provides an image/screenshot |
Both tiers produce the same output: a pages/<page-name>.vue file with a companion assets/content/<page-name>.json data file, built entirely from @rds-vue-ui/* components.
2. Prerequisites
Before starting, confirm the following are available:
- Figma MCP server — connected and responding (required for Figma URL inputs)
- Playwright MCP — available for screenshot capture (required for prototype URLs and visual validation)
rds-component-mapperskill — must be available; it provides the shared component selection decision tree used by both tiers- User input — one of the following:
- A Figma design URL (e.g.
https://figma.com/design/:fileKey/:fileName?node-id=1-2) - A Figma prototype URL (e.g.
https://figma.com/proto/:fileKey/...) - A screenshot or image file of the design
- A Figma design URL (e.g.
3. Step 1 — Detect Input Type & Account Tier
Determine which processing tier to use based on the input and available Figma access.
3.1 Parse the Input
-
If Figma design URL → extract
fileKeyandnodeIdfrom the URL format:https://figma.com/design/:fileKey/:fileName?node-id=<nodeId>fileKeyis the path segment after/design/nodeIdis thenode-idquery parameter (e.g.1-2)
-
If Figma prototype URL → extract
fileKeyfrom:https://figma.com/proto/:fileKey/...- A prototype URL indicates the Screenshot Path (no design context available)
-
If screenshot/image → proceed directly to Screenshot Path (Step 2b)
3.2 Probe Account Tier
If a fileKey and nodeId were extracted:
- Call
get_design_context(fileKey, nodeId)via the Figma MCP server - Evaluate the response:
- Rich data returned (layout tree, typography tokens, colors, component structure) → proceed to Rich Path (Step 2a)
- Call fails, times out, or returns limited/empty data → fall back to Screenshot Path (Step 2b)
4. Step 2a — Rich Path (Dev+ Account)
Use this path when get_design_context returns comprehensive design data.
4.1 Extract Design Context
get_design_context(fileKey, nodeId)
From the response, extract:
- Layout structure — frame hierarchy, auto-layout direction, spacing, padding
- Typography — font family, size, weight, line height, letter spacing
- Colors — fills, strokes, effects (map to RDS theme CSS variables)
- Component structure — instances, variants, nested components
4.2 Get Visual Reference
get_screenshot(fileKey, nodeId)
Capture a screenshot for visual validation later.
4.3 Download Assets
Download any assets (icons, illustrations, images) from the Figma MCP localhost endpoints.
IMPORTANT: Use the localhost URLs returned by the Figma MCP server directly. Do NOT import icon packages or substitute with icon libraries. The Figma assets are the source of truth for the design.
4.4 Handle Large / Truncated Responses
If get_design_context returns a response that is too large or appears truncated:
- Call
get_metadata(fileKey, nodeId)first to get the top-level structure - Identify child node IDs from the metadata
- Fetch each child node individually with
get_design_context(fileKey, childNodeId) - Reassemble the full design context from the individual responses
4.5 Map Sections to RDS Components
For each design section identified in the context:
- Invoke the
rds-component-mapperskill decision tree to select the best@rds-vue-ui/*component for the section's visual intent - Map Figma design tokens to RDS component props:
- Figma auto-layout → RDS section layout props
- Figma text styles → RDS typography props (heading level, size, weight)
- Figma color fills → RDS theme CSS variables (
var(--rds-color-*)) - Figma component instances → nested RDS sub-components
- Extract content: headings, body text, CTA labels, image URLs, link targets
- Extract colors: map hex values to the closest RDS theme variable
4.6 Compose the Page
Assemble all mapped sections into a single pages/<page-name>.vue file:
<script setup>
const content = await import('~/assets/content/<page-name>.json')
</script>
<template>
<div>
<!-- Each section maps to an @rds-vue-ui/* component -->
<HeroStandardApollo v-bind="content.hero" />
<SectionCardsApollo v-bind="content.cards" />
<!-- ... -->
</div>
</template>
4.7 Create Content JSON
Create assets/content/<page-name>.json with all extracted content:
{
"hero": {
"heading": "Extracted heading from Figma",
"subheading": "Extracted subheading",
"ctaLabel": "Get Started",
"ctaUrl": "#",
"backgroundImage": "/images/hero-bg.jpg"
},
"cards": {
"items": [
{
"title": "Card Title",
"description": "Card description text",
"image": "/images/card-1.jpg"
}
]
}
}
4.8 Validate Against Screenshot
Compare the composed page output against the Figma screenshot captured in Step 4.2. Check alignment, spacing, typography, and color fidelity.
5. Step 2b — Screenshot Path (Basic Account / Image Input)
Use this path when rich design context is unavailable.
5.1 Obtain the Screenshot
| Input Type | Method |
|---|---|
| Figma design URL | Call get_screenshot(fileKey, nodeId) — works on all Figma account tiers |
| Figma prototype URL | Use Playwright MCP: browser_navigate to the URL, then browser_take_screenshot |
| User-provided image | Use the image directly |
5.2 Analyze the Screenshot
Use the vision model to analyze the screenshot and identify:
- Page sections — hero area, content blocks, testimonial strips, card grids, CTAs, footer
- Approximate layout — full-width sections, grid columns (2-col, 3-col, 4-col), sidebars, stacked layouts
- Content — headings (H1, H2, H3), body text, button labels, image placeholders
- Color palette — dominant colors, accent colors, background tones
- Typography hints — relative font sizes, weight variations (bold headings vs. regular body), text alignment
5.3 Map Sections to RDS Components
For each visually identified section:
- Invoke the
rds-component-mapperskill decision tree to select the closest@rds-vue-ui/*component - Infer props from visual analysis:
- Section height/width → layout sizing props
- Observed colors → closest RDS theme CSS variables
- Text content → extract where readable, use descriptive placeholders where not (e.g.
"[Hero heading text]") - Image areas → placeholder image paths
- Generate placeholder content for any text that cannot be reliably extracted from the screenshot
5.4 Compose the Page
Assemble into pages/<page-name>.vue following the same structure as the Rich Path (Step 4.6).
5.5 Create Content JSON
Create assets/content/<page-name>.json following the same structure as the Rich Path (Step 4.7). Mark placeholder content clearly:
{
"hero": {
"heading": "[Hero heading — replace with actual copy]",
"subheading": "[Subheading — replace with actual copy]",
"ctaLabel": "Learn More",
"backgroundImage": "/images/placeholder-hero.jpg"
}
}
5.6 Visual Comparison
- Take a screenshot of the generated page using Playwright MCP (
browser_navigate→browser_take_screenshot) - Compare side-by-side with the original Figma screenshot / user image
- Iterate on component selection and props until the output closely matches the original design
6. Translation Rules
Figma MCP outputs React + Tailwind CSS by default. All output must be translated to the RDS stack:
| Figma MCP Output | RDS Target |
|---|---|
| React JSX | Vue 3 <template> with <script setup> |
| Tailwind utility classes | Bootstrap 5 classes or custom SCSS |
Inline style objects |
SCSS <style lang="scss" scoped> blocks |
| Hardcoded hex colors | RDS theme CSS variables (var(--rds-primary), var(--rds-secondary), etc.) |
className |
class |
onClick / onChange |
@click / @change |
{condition && <El/>} |
v-if="condition" |
{items.map(i => ...)} |
v-for="item in items" |
| React component imports | None — RDS components are auto-imported via Nuxt component scanner |
Component Naming
- Use PascalCase for all component names in templates:
<HeroStandardApollo>,<SectionCardsApollo> - Do NOT add
importstatements for@rds-vue-ui/*components — they are auto-imported
Styling
- Use Bootstrap 5 utility classes where they match the design intent
- For custom styling beyond Bootstrap, use scoped SCSS:
<style lang="scss" scoped> .custom-section { padding: 4rem 0; background-color: var(--rds-surface); } </style> - Never use Tailwind classes — the project uses Bootstrap 5 + SCSS exclusively
7. Desktop + Mobile
When Both Viewports Are Provided
If the user provides separate Figma nodes for desktop and mobile:
- Extract/analyze both viewports
- Implement responsive behavior using Bootstrap responsive classes:
col-lg-*/col-md-*/col-sm-*for grid columnsd-none d-lg-block/d-lg-nonefor viewport-specific elementsorder-lg-*for reordering on different breakpoints
- Validate both viewports against their respective Figma screenshots
When Only Desktop Is Provided
- Implement the desktop design faithfully
- Apply sensible mobile defaults using Bootstrap's responsive grid
- Stack columns on mobile (
col-12on small screens,col-lg-*on large)
Breakpoint Reference
| Bootstrap Class | Breakpoint |
|---|---|
col-sm-* |
≥ 576px |
col-md-* |
≥ 768px |
col-lg-* |
≥ 992px |
col-xl-* |
≥ 1200px |
col-xxl-* |
≥ 1400px |
8. Validation Checklist
Before considering the page complete, verify every item:
- Layout matches — spacing, alignment, sizing match the Figma design
- Typography matches — font family, size, weight, line height are correct
- Colors match — all colors use RDS theme CSS variables (no hardcoded hex)
- Components used — all sections use
@rds-vue-ui/*components where a suitable match exists - Content is JSON-driven — all text, images, and data come from
assets/content/<page-name>.json - No manual imports — no
importstatements for RDS components (auto-imported) - Vue 3 syntax —
<script setup>,v-if,v-for,@click(no React patterns) - Bootstrap 5 only — no Tailwind classes; Bootstrap utilities + SCSS
- Responsive — desktop and mobile viewports render correctly
- Assets downloaded — icons and images from Figma are saved locally (not imported from packages)
- Visual comparison — side-by-side screenshot matches the original design
9. Examples
Example 1: Figma URL with Dev+ Account (Rich Path)
User input:
Build a landing page from this Figma design:
https://figma.com/design/ABC123xyz/campaign-summer-2025?node-id=1-2
Agent workflow:
- Parse URL →
fileKey = "ABC123xyz",nodeId = "1-2" - Call
get_design_context("ABC123xyz", "1-2")→ returns rich data:Frame "Hero Section" - Auto-layout: vertical, gap 24px, padding 64px - Background: linear-gradient(#1a2b3c, #2c3d4e) - Text "Summer Campaign 2025" — Inter Bold 48px #FFFFFF - Text "Discover what's new" — Inter Regular 18px #B0B8C4 - Button "Explore Now" — fill #FF6B35, text #FFFFFF Frame "Features Grid" - Auto-layout: horizontal, gap 32px, wrap - 3x Card children with icon + title + description Frame "Testimonials" - Carousel with 4 testimonial cards - Call
get_screenshot("ABC123xyz", "1-2")→ save reference image - Download icon assets from Figma MCP localhost URLs
- Invoke
rds-component-mapperfor each section:- "Hero Section" →
HeroStandardApollo - "Features Grid" →
SectionCardsApollo - "Testimonials" →
SectionTestimonialsApollo
- "Hero Section" →
- Map Figma tokens to RDS props:
#1a2b3cgradient →var(--rds-primary-dark)#FF6B35button →var(--rds-accent)- Inter Bold 48px → heading level 1
- Create
pages/campaign-summer-2025.vue:<script setup> import content from '~/assets/content/campaign-summer-2025.json' </script> <template> <div> <HeroStandardApollo :heading="content.hero.heading" :subheading="content.hero.subheading" :cta-label="content.hero.ctaLabel" :cta-url="content.hero.ctaUrl" :background-image="content.hero.backgroundImage" /> <SectionCardsApollo :heading="content.features.heading" :cards="content.features.items" /> <SectionTestimonialsApollo :testimonials="content.testimonials.items" /> </div> </template> - Create
assets/content/campaign-summer-2025.jsonwith all extracted text, images, and links - Validate: compare rendered page screenshot against Figma screenshot
Example 2: User Provides a Screenshot (Screenshot Path)
User input:
Here's a screenshot of the design I need built:
[attached: design-mockup.png]
Agent workflow:
- No Figma URL → proceed to Screenshot Path
- Analyze
design-mockup.pngwith vision model:Identified sections: - Hero: full-width, dark background, large white heading, subtitle, orange CTA button - Stats bar: horizontal row of 4 stat counters with numbers and labels - Cards: 3-column grid, each card has an icon, heading, and paragraph - CTA banner: accent-colored background, centered heading, two buttons - Footer: dark background, 4-column links layout, social icons - Invoke
rds-component-mapperfor each section:- Hero →
HeroStandardApollo - Stats bar →
SectionCountersApollo - Cards grid →
SectionCardsApollo - CTA banner →
SectionCtaApollo - Footer →
FooterStandardApollo
- Hero →
- Infer props from visual analysis:
- Dark hero background →
var(--rds-primary-dark) - Orange CTA →
var(--rds-accent) - 3-column layout →
columns: 3
- Dark hero background →
- Create
pages/design-mockup.vue:<script setup> import content from '~/assets/content/design-mockup.json' </script> <template> <div> <HeroStandardApollo :heading="content.hero.heading" :subheading="content.hero.subheading" :cta-label="content.hero.ctaLabel" :cta-url="content.hero.ctaUrl" /> <SectionCountersApollo :counters="content.stats.items" /> <SectionCardsApollo :heading="content.cards.heading" :cards="content.cards.items" :columns="3" /> <SectionCtaApollo :heading="content.cta.heading" :primary-label="content.cta.primaryLabel" :secondary-label="content.cta.secondaryLabel" /> <FooterStandardApollo :columns="content.footer.columns" :social-links="content.footer.socialLinks" /> </div> </template> - Create
assets/content/design-mockup.jsonwith placeholder content:{ "hero": { "heading": "[Hero heading — replace with actual copy]", "subheading": "[Subheading — replace with actual copy]", "ctaLabel": "Get Started", "ctaUrl": "#" } } - Take screenshot of rendered page via Playwright MCP
- Compare side-by-side with original
design-mockup.png - Iterate: adjust component props and layout until visual match is satisfactory
More from chandima/rds-lp-factory
rds-component-mapper
Maps visual descriptions, design context, or screenshots to the closest @rds-vue-ui/* design system components. Use when you need to select RDS components for a landing page section based on visual intent, Figma data, or natural-language descriptions.
2markdown-spec-to-page
Converts a markdown specification document into a complete Nuxt 3 landing page using RDS Vue UI components. Use when user provides a markdown file or text describing the desired page structure, components, and content.
1iterative-build-page
Builds a Nuxt 3 landing page section-by-section from natural language descriptions using RDS Vue UI components. Use when user describes page sections incrementally (e.g., "Add a hero with video background", "Now add a 3-column card grid").
1reference-to-landing-page
Creates a new Nuxt 3 landing page inspired by a reference website URL, adapted with user-specified modifications, using RDS Vue UI components. Use when user provides an existing page URL and describes how they want the new page to differ.
1