Reference-to-Landing-Page
1. Overview
This skill takes a reference website URL plus a user modification prompt and produces a complete Nuxt 3 landing page built entirely with @rds-vue-ui/* components. It uses Playwright MCP for browser automation to capture the reference page, then translates every section into the RDS design system.
Inputs:
- A URL to an existing web page (the "reference")
- Natural-language instructions describing how the new page should differ
Outputs:
pages/<page-name>.vue— the composed landing pageassets/content/<page-name>.json— JSON-driven content for the page- Screenshots of both reference and final page for visual comparison
2. Prerequisites
Before executing this skill, verify:
- Playwright MCP server is connected (
@playwright/mcp@latest). Test withbrowser_navigate. - rds-component-mapper skill is available — it is required for Step 4 (component selection).
- User has provided:
- A reference URL (must be publicly accessible or accessible from the current network)
- Natural-language description of desired modifications
- Project context: You are working inside a Nuxt 3 project scaffolded from the
rds-lp-factorytemplate with@rds-vue-ui/*packages available.
3. Workflow
Execute these steps in order. Do not skip steps.
Step 1: Capture the Reference Page
Use Playwright MCP tools to fully capture the reference page:
1. browser_navigate({ url: "<reference-url>" })
2. Wait for page to fully load (network idle)
3. browser_take_screenshot({ fullPage: true })
→ Save/note this as "reference screenshot"
4. browser_snapshot()
→ This returns the accessibility tree — the primary structural data
For long pages (common with landing pages):
5. browser_execute_javascript({ script: "document.body.scrollHeight" })
→ Get total page height
6. If height > 3000px, scroll in increments and capture additional screenshots:
- browser_execute_javascript({ script: "window.scrollTo(0, 1500)" })
- browser_take_screenshot()
- Repeat until entire page is captured
7. browser_snapshot() again after scrolling to ensure all lazy-loaded content is captured
What to extract from the accessibility tree:
- Document title and meta description
- Navigation structure (links, dropdowns)
- All text content (headings, paragraphs, list items)
- Image alt text and approximate placement
- Link targets and button labels
- Form fields and their labels
- ARIA landmarks (banner, main, contentinfo, navigation)
Step 2: Analyze Page Structure
Parse the accessibility snapshot into a section inventory. Identify each distinct section of the page and classify it:
| Section Type | What to Look For |
|---|---|
| Navigation | <nav> landmark, logo, menu links, CTA button |
| Hero/Banner | First large section, prominent heading (h1), background image, CTA |
| Introduction | Short text block after hero, often with subheading |
| Features/Benefits | Grid of items with icons/images + short text, often 3-4 columns |
| Card Grid | Repeated card-like structures with image + title + description |
| Testimonials | Quotes with attribution, photos, star ratings |
| Statistics/Numbers | Large numbers with labels (e.g., "500+ students") |
| Video Section | Embedded video or play button with thumbnail |
| Parallax/Full-width Image | Full-width background image with overlaid text |
| Call to Action | Prominent button/form section urging user action |
| Form Section | Input fields, labels, submit button |
| FAQ/Accordion | Question-answer pairs, expandable items |
| Footer | Contact info, social links, copyright, secondary nav |
For each identified section, record:
- section_index: 1
type: hero
purpose: "Primary hero with program headline and apply CTA"
content:
heading: "Welcome to the MBA Program"
subheading: "Transform your career..."
cta_text: "Apply Now"
cta_url: "/apply"
background_image: true
layout: full-width, centered text
approximate_style: "dark overlay on image, white text, large heading"
Step 3: Apply User's Modification Intent
Parse the user's natural-language prompt and categorize the requested changes:
| Change Category | Examples | How to Apply |
|---|---|---|
| Content swap | "Make it for basketball instead of baseball" | Replace all program-specific text, images, links |
| Branding change | "Use maroon and gold colors" | Update color variables, theme selection |
| Section addition | "Add a video hero section" | Insert new section in appropriate position |
| Section removal | "Remove the contact form" | Delete section from inventory |
| Section reorder | "Put testimonials before features" | Rearrange section order |
| Layout change | "Make the cards 2 columns instead of 3" | Adjust grid configuration |
| CTA change | "Change apply button to schedule a visit" | Update CTA text, URL, styling |
| Tone/audience | "Make it more casual and student-focused" | Rewrite copy with new tone |
Produce a modified section inventory that reflects all user changes applied to the original structure.
Step 4: Map to RDS Components
For every section in the modified inventory, select the appropriate @rds-vue-ui/* component.
IMPORTANT: Invoke the rds-component-mapper skill for component selection. Do not guess component names — use the decision tree from that skill.
Mapping process for each section:
1. Take the section type + purpose from Step 2/3
2. Match against the rds-component-mapper decision tree:
- Hero → HeroStandardApollo | HeroVideoApollo | HeroArticleAtlas
- Card grid → SectionCardApollo | SectionCardAtlas + CardIcon | CardImageArticle | etc.
- Testimonials → SectionTestimonialFalcon | SectionTestimonialAtlas | etc.
- Video → SectionVideoApollo | SectionVideoModal
- Parallax → SectionParallaxApollo | SectionParallaxAtlas
- FAQ → RdsAccordion | OverlapAccordionAtlas
- Footer → FooterDefault | FooterPartner
- Navigation → NavbarDefault | NavbarSticky
- Forms → RdsInput, RdsCheckbox, RdsDropdown, etc.
3. Record: component name, package name, required props, slot content
Component selection constraints:
- Every visible section MUST use an RDS component — no raw HTML sections
- If no exact match exists, use the closest container (
SectionApollo,SectionContainerAtlas) and compose with smaller RDS components inside - Cards inside card sections must also be RDS card components
- Bootstrap 5 grid (
container,row,col-*) is allowed for custom layout within sections
Step 5: Compose the Landing Page
5a. Create the content JSON file
Create assets/content/<page-name>.json with all page content:
{
"meta": {
"title": "Page Title",
"description": "Meta description for SEO"
},
"hero": {
"heading": "Main Headline",
"subheading": "Supporting text...",
"ctaText": "Apply Now",
"ctaUrl": "/apply",
"backgroundImage": "/images/hero-bg.jpg"
},
"features": {
"heading": "Why Choose Us",
"items": [
{
"icon": "fa-star",
"title": "Feature One",
"description": "Description text..."
}
]
},
"testimonials": {
"heading": "Student Stories",
"items": [
{
"quote": "This program changed my life...",
"name": "Jane Doe",
"title": "Class of 2024",
"image": "/images/testimonial-1.jpg"
}
]
}
}
5b. Create the page component
Create pages/<page-name>.vue:
<template>
<div>
<!-- Navigation -->
<NavbarDefault v-bind="navbarProps" />
<!-- Hero Section -->
<HeroStandardApollo v-bind="heroProps">
<template #content>
<h1>{{ content.hero.heading }}</h1>
<p>{{ content.hero.subheading }}</p>
<a :href="content.hero.ctaUrl" class="btn btn-primary">
{{ content.hero.ctaText }}
</a>
</template>
</HeroStandardApollo>
<!-- Features Section -->
<SectionCardApollo v-bind="featuresProps">
<template #cards>
<CardIcon
v-for="(item, index) in content.features.items"
:key="index"
v-bind="mapFeatureToCardProps(item)"
/>
</template>
</SectionCardApollo>
<!-- Additional sections... -->
<!-- Footer -->
<FooterDefault v-bind="footerProps" />
</div>
</template>
<script setup>
import content from '~/assets/content/<page-name>.json'
// Map content to component props
const heroProps = computed(() => ({
// ... map from content.hero
}))
</script>
<style lang="scss" scoped>
// Use RDS theme variables for any custom styling
// @use '~/assets/scss/variables' as *;
</style>
Composition rules:
- Use
<script setup>with Composition API - All RDS components are auto-imported — do NOT add import statements for them
- Content comes from JSON file, imported at the top of
<script setup> - Use
computed()to map JSON content to component props - Use Bootstrap 5 grid classes for layout adjustments
- Use SCSS with RDS theme variables for any custom styling
- Sections are composed vertically in the
<template>, one after another
Step 6: Visual Validation
If a dev server is running (or can be started):
1. browser_navigate({ url: "http://localhost:3000/<page-name>" })
2. Wait for page to fully render
3. browser_take_screenshot({ fullPage: true })
→ Save as "output screenshot"
4. Compare visually with the reference screenshot from Step 1
5. Check for:
- Missing sections (compare section count)
- Broken layout (overlapping elements, misaligned grids)
- Missing content (text, images, links)
- Spacing inconsistencies (too cramped or too spread out)
- Color/theme mismatches
6. Fix any issues found and re-screenshot until satisfactory
If no dev server is available, perform a code review instead:
- Verify every section from the modified inventory is present in the template
- Verify all content JSON keys are referenced in the template
- Verify component props match the rds-component-mapper documentation
- Verify no raw HTML is used where an RDS component exists
4. Translation Rules
These rules are mandatory and must not be violated:
Never copy source code from the reference page. The reference is visual inspiration only — all code must be freshly written using
@rds-vue-ui/*components.Every section uses an RDS component. If the reference has a custom HTML section, find the closest RDS component match. Use
SectionApolloorSectionContainerAtlasas generic wrappers when needed.Content is JSON-driven. All text, image URLs, links, and configuration live in the content JSON file, not hardcoded in the template.
Components are auto-imported. Never write
import { HeroStandardApollo } from '...'— the Nuxt component scanner handles this.Use Bootstrap 5 grid for custom layout needs within sections. Do not use CSS Grid or Flexbox utilities directly when Bootstrap classes suffice.
Style with SCSS using RDS theme variables. Do not use inline styles or Tailwind-like utility classes.
Respect the user's modifications. The final page must reflect ALL changes the user requested, not just a copy of the reference.
Preserve accessibility. Use semantic HTML, proper heading hierarchy (h1 → h2 → h3), alt text on images, and ARIA labels where needed.
5. Examples
Example 1: Content Swap
User prompt: "Take mlb.asu.edu and make a version for the ASU basketball program"
Process:
- Capture mlb.asu.edu — hero, program highlights, faculty cards, testimonials, CTA
- Structure: Hero → Intro → Card Grid (programs) → Testimonials → CTA → Footer
- Modifications: Replace all "MLB" with "Basketball", swap images, update stats/facts
- Map: HeroStandardApollo, SectionApollo (intro), SectionCardApollo + CardImageArticle, SectionTestimonialFalcon, SectionParallaxApollo (CTA), FooterDefault
- Generate
pages/basketball.vue+assets/content/basketball.json
Example 2: Structural Modification
User prompt: "Use example.com as inspiration but add a video hero and remove the contact form"
Process:
- Capture example.com — identify all sections including the contact form
- Modifications: Replace standard hero with video hero, delete form section
- Map: HeroVideoApollo (instead of HeroStandardApollo), remove form section entirely
- All other sections mapped normally via rds-component-mapper
- Generate page files
Example 3: Complete Redesign with Same Content
User prompt: "Take our current research page and rebuild it with a modern parallax layout"
Process:
- Capture the research page — extract all content
- Modifications: Restructure as alternating content/parallax sections
- Map: HeroStandardApollo, then alternate SectionApollo ↔ SectionParallaxApollo
- Same content, completely different visual structure
- Generate page files with parallax-oriented layout
6. Error Handling
| Error | Resolution |
|---|---|
| Playwright cannot reach URL | Ask user to verify URL is accessible; check for auth walls |
| Page requires login/auth | Ask user to provide credentials or a public alternative |
| Reference page is a SPA with lazy loading | Scroll the entire page first, wait for network idle, then snapshot |
| No RDS component matches a section | Use SectionApollo or SectionContainerAtlas as a wrapper and compose with smaller RDS components |
| Content JSON structure unclear | Follow the pattern from existing pages in the project; check assets/content/ for examples |
| Dev server not available for validation | Perform code-level validation (Step 6 fallback) |
| Reference page has dynamic/interactive elements | Translate to the closest static RDS equivalent; note limitations to user |
7. Output Checklist
Before declaring the task complete, verify:
-
pages/<page-name>.vueexists and contains all sections -
assets/content/<page-name>.jsonexists with all content - Every section uses an
@rds-vue-ui/*component (no raw HTML sections) - No component import statements (auto-import only)
- Content is driven by JSON, not hardcoded
- All user-requested modifications are applied
-
<script setup>with Composition API is used - SCSS styling uses RDS theme variables
- Heading hierarchy is correct (single h1, then h2s, h3s)
- Reference screenshot and output screenshot captured (if dev server available)
- No code was copied from the reference page source