A stack-agnostic developer guide for transforming reference images into production-ready, whitelabel-capable UI components using Google Stitch and the Antigravity platform. Works with any language or framework — React, .NET Blazor, Vue, Angular, SwiftUI, Jetpack Compose, and others.
Reference Image → Stitch Vision → Raw Layout/Style JSON
→ Token Normalizer → Component Generator → Theme Resolver → Production Component
Every stage is a discrete, swappable step. The inputs and outputs are plain JSON; no stage assumes a specific runtime, language, or framework.
| Principle | What it means |
|---|---|
| Token-first | Every UI value resolves from a named token — never a hardcoded literal. |
| Semantic naming | Code references role-based tokens (text.primary), not primitives (neutral.900). |
| Single-swap theming | Replacing one brandPack.overrides object re-themes all components. |
| Deterministic generation | Same image + same tokens = same output. Ambiguity produces TODO markers, not silent guesses. |
| Motion with intent | Animation serves hierarchy. It unconditionally respects prefers-reduced-motion (or the platform equivalent). |
| Stack-agnostic | Token contracts, API shapes, and quality gates are defined in JSON and HTTP. Implementation language is your choice. |
Extensions shown are illustrative. Use the conventions of your target stack.
vision-ui/
├── input/
│ ├── reference.png # Source UI screenshot
│ └── meta.json # Viewport + platform hints
├── extracted/
│ ├── stitch-layout.raw.json
│ └── stitch-styles.raw.json
├── normalized/
│ ├── layout.normalized.json
│ └── tokens.resolved.json
├── themes/
│ ├── brand-default.json
│ └── brand-client-a.json
├── generated/
│ └── <ComponentName>.<ext> # Output in your stack's format
└── src/
├── design/
│ ├── tokens.<ext> # Core + semantic + component tokens
│ ├── theme-resolver.<ext> # Token → CSS variable resolver
│ └── motion-tokens.<ext> # Spring/duration/float values
└── components/
├── floating-badge.<ext>
└── hero-card.<ext>
Video walkthrough: Google Stitch to Antigravity - Full Workflow
Google Stitch uses Gemini 2.5 Pro to generate full website front-ends — including multiple page sections, design systems with light/dark mode, color palettes, and font customization — from a single reference image. Always choose the Pro model for best fidelity.
| File | Required | Purpose |
|---|---|---|
input/reference.png |
Yes | UI screenshot (desktop: 1440×1024, mobile: 390×844) |
input/meta.json |
No | Viewport dimensions, platform target, density |
Finding references: Pinterest (web design inspiration portfolio, SaaS web design), Dribbble, Behance, or Awwwards.
Image checklist:
- Normalize to a known viewport
- Preserve alpha channels where shadows carry meaning
- Remove watermarks, cursors, and non-UI chrome
- Ensure text is legible at 1× scale
Stitch prompt formula:
"Replicate this web design as an image. Also insert design system."
This generates both the visual layout and a design.md with customizable colors, fonts, and modes.
meta.json schema:
{
"viewport": { "width": 1440, "height": 1024 },
"platform": "web",
"density": 2,
"expectedComponents": ["HeroCard", "TopNav", "FloatingBadge"]
}Iterative refinement: Use Regenerate for a different take, or Variations → Refine to explore alternatives. After finalizing desktop, click the design and say "Create a mobile version for it."
Request structured output across four layers:
| Layer | What to extract |
|---|---|
| Structure | Regions, containers, parent-child hierarchy, layout type (stack/grid/freeform) |
| Spacing | Margins, paddings, gaps, alignment anchors, baseline rhythm |
| Typography | Font family, size, line-height, weight, letter spacing |
| Effects | Radii, borders, shadows, blur, transparency, depth ordering |
Recommended extraction prompt:
"Return a normalized layout tree with absolute bounds, inferred auto-layout constraints, text styles, and depth layers. Include confidence scores per element and ambiguity notes for uncertain regions. Output format:
layout-tree-v1."
Region classification:
| Visual pattern | Mapped component |
|---|---|
| Horizontal bar at top edge | TopNav |
| Repeating card blocks in grid | CardGrid / List |
| Badge + heading + action cluster | Hero variant |
| Overlapping decorative shapes | Antigravity floating layer |
Token snapping — snap every raw pixel to the nearest token step and preserve the audit trail:
{ "raw": 13, "snapped": "spacing.3", "resolved": 12, "delta": 1 }Constraint conversion:
| Source pattern | Responsive constraint |
|---|---|
| Left + right pinned | Fluid width with max-width |
| Centered in parent | Centering via flexbox or margin auto |
| Floating free position | Absolute within bounded relative parent |
| Card with fixed width | Min-width / max-width clamp |
Confidence fallback: If extraction confidence < 0.82, emit a TODO marker — never silently generate from low-confidence data. Example comment pattern (language-agnostic):
// AUTO-GENERATED — confidence: 0.71
// TODO: Verify component boundary. Stitch was uncertain about this region.
// Render a placeholder with attribute: data-todo="verify-boundary"
"Antigravity" in the design sense means intentional floating composition — UI elements that appear detached from rigid grid flow while remaining accessible and predictable.
Floating element rules:
- Use absolute positioning within a bounded relative parent
- Maintain at least one anchor relation (center, edge offset) so items stay stable on resize
- Define exclusion zones around primary actions to prevent overlap collisions
- No floating element may hide or block a primary action at any viewport size
Z-index / depth — always use tokens, never raw numbers. Define depth as named constants in your stack:
Depth layers (token names → values):
base: 0
raised: 10
floating: 40
overlay: 70
modal: 100
toast: 120
Physics-based motion (spring defaults):
| Parameter | Range | Default |
|---|---|---|
stiffness |
180–260 | 220 |
damping |
18–26 | 22 |
mass |
0.7–1.1 | 0.9 |
Motion recipes:
| Recipe | Behaviour |
|---|---|
| Float idle | Translate Y between 0 and −4px, period 4 s, ease in-out, repeat indefinitely |
| Entrance | Y: 12→0, opacity: 0→1, spring transition, stagger by depth layer |
| Lift-on-hover | Y: −4px, scale: 1.01, shadow increase, duration ≤ 120 ms |
Always respect
prefers-reduced-motion(web) or the equivalent accessibility preference on your platform. Disable perpetual oscillation and collapse entrance stagger when the user has requested reduced motion.
Core Tokens — primitives: raw colors, spacing scale, radii
↓
Semantic Tokens — roles: bg.canvas, text.primary, action.primary
↓
Component Tokens — scoped: heroCard.bg, floatingBadge.radius
Each tier references only the tier above it. Component code references semantic tokens only — never core primitives directly. This is what makes single-swap theming possible.
{
"core": {
"color": {
"neutral": {
"0": "#FFFFFF",
"50": "#F7F9FC",
"100": "#EDF1F7",
"300": "#C9D3E0",
"500": "#6B7A90",
"700": "#2C3A4B",
"900": "#111827"
},
"brand": {
"500": "#0A7A5C",
"600": "#08664C",
"700": "#064E3B"
},
"accent": { "500": "#E88A1A" },
"success": { "500": "#16A34A" },
"warning": { "500": "#EAB308" },
"error": { "500": "#DC2626" }
},
"spacing": {
"0": 0, "1": 4, "2": 8, "3": 12, "4": 16,
"5": 20, "6": 24, "8": 32, "10": 40, "12": 48, "16": 64
},
"radius": { "sm": 6, "md": 10, "lg": 16, "xl": 24, "pill": 999 },
"shadow": {
"sm": "0 1px 2px rgba(17,24,39,0.08)",
"md": "0 6px 16px rgba(17,24,39,0.12)",
"lg": "0 14px 30px rgba(17,24,39,0.16)"
},
"z": {
"base": 0, "raised": 10, "floating": 40,
"overlay": 70, "modal": 100, "toast": 120
}
}
}{
"semantic": {
"color": {
"bg.canvas": "{core.color.neutral.50}",
"bg.surface": "{core.color.neutral.0}",
"bg.muted": "{core.color.neutral.100}",
"text.primary": "{core.color.neutral.900}",
"text.secondary": "{core.color.neutral.700}",
"text.muted": "{core.color.neutral.500}",
"text.inverse": "{core.color.neutral.0}",
"border.default": "{core.color.neutral.300}",
"action.primary.bg": "{core.color.brand.500}",
"action.primary.bg.hover": "{core.color.brand.600}",
"action.primary.text": "{core.color.neutral.0}",
"accent.emphasis": "{core.color.accent.500}"
},
"spacing": {
"section.y": "{core.spacing.12}",
"section.x": "{core.spacing.8}",
"card.padding": "{core.spacing.6}",
"control.gap": "{core.spacing.3}",
"inline.gap": "{core.spacing.2}"
},
"radius": {
"card": "{core.radius.lg}",
"control": "{core.radius.md}",
"chip": "{core.radius.pill}"
},
"motion": {
"duration.fast": 120,
"duration.base": 180,
"duration.slow": 260,
"spring.stiffness": 220,
"spring.damping": 22,
"spring.mass": 0.9
}
}
}{
"component": {
"heroCard": {
"bg": "{semantic.color.bg.surface}",
"text": "{semantic.color.text.primary}",
"radius": "{semantic.radius.card}",
"padding": "{semantic.spacing.card.padding}",
"shadow": "{core.shadow.md}",
"z": "{core.z.raised}"
},
"floatingBadge": {
"bg": "{semantic.color.accent.emphasis}",
"text": "{semantic.color.text.inverse}",
"radius": "{semantic.radius.chip}",
"z": "{core.z.floating}"
},
"topNav": {
"bg": "{semantic.color.bg.surface}",
"border": "{semantic.color.border.default}",
"height": "{core.spacing.16}",
"z": "{core.z.raised}"
}
}
}A brand pack is a flat JSON override object. Swapping it re-themes every component that aliases semantic or core tokens — no component source changes required.
{
"id": "default",
"name": "Stitch Green",
"overrides": {
"core.color.brand.500": "#0A7A5C",
"core.color.brand.600": "#08664C",
"core.color.accent.500": "#E88A1A"
}
}{
"id": "client-a",
"name": "Client A Blue",
"overrides": {
"core.color.brand.500": "#0049B7",
"core.color.brand.600": "#003A91",
"core.color.accent.500": "#FF6A3D"
}
}Implement this logic in any language. The steps are language-agnostic:
1. Flatten the three token tiers into a single dot-path map:
"core.color.brand.500" → "#0A7A5C"
"semantic.color.action.primary.bg" → "{core.color.brand.500}"
2. Merge brand pack overrides (overrides win on key collision).
3. Resolve aliases: for any value matching "{some.token.path}",
replace it with the value at that path. Repeat until stable.
Cap at 10 passes to prevent circular references.
4. Emit as CSS custom properties (web) or platform theme objects (native):
dot-path → CSS variable: "core.color.brand.500" → "--ds-core-color-brand-500"
number → append "px" (except 0, which stays "0")
string → emit as-is
CSS output example:
:root[data-theme="default"] {
--ds-core-color-brand-500: #0A7A5C;
--ds-semantic-color-bg-canvas: #F7F9FC;
--ds-semantic-radius-card: 16px;
--ds-semantic-spacing-card-padding: 24px;
--ds-component-heroCard-shadow: 0 6px 16px rgba(17,24,39,0.12);
}
:root[data-theme="client-a"] {
--ds-core-color-brand-500: #0049B7;
--ds-core-color-brand-600: #003A91;
--ds-core-color-accent-500: #FF6A3D;
/* All semantic + component tokens that alias brand colors update automatically */
}CSS custom properties are the universal delivery format for web. On platforms that do not support CSS variables (native mobile, desktop apps), emit the resolved token map as a platform theme object or generated constants instead — keeping the same three-tier key structure.
| Domain | Rule |
|---|---|
| Colors | Must resolve through semantic.color.* or component.*.{color-prop} — no raw hex in component source |
| Spacing | Must resolve through semantic.spacing.* — no hardcoded literals |
| Radius | Must resolve through semantic.radius.* |
| Z-index / depth | Must come from core.z.* depth tokens only — no arbitrary numbers |
| Motion | Timing, spring params, and idle-float values must resolve through semantic.motion.* |
| Shadows | Must reference core.shadow.* |
- Contrast: Minimum WCAG AA (4.5:1 normal text, 3:1 large text)
- Focus: Visible focus indicators on all backgrounds and depth layers
- Motion: Reduced or disabled when the platform's reduced-motion preference is set
- Semantics: Native elements and controls before custom ARIA overrides
- Heading order: Logical hierarchy (h1 → h2 → h3) and landmark structure
Using Antigravity + Stitch?
├── Yes → Section 4.3 (MCP Workflow — automated)
└── No → Section 4.2 (CLI / API Workflow — manual steps)
These steps apply regardless of language or OS. All four steps use the same JSON contracts; only the shell syntax differs per OS.
Step 1 — Extract layout from the reference image
CLI:
macOS / Linux:
vision-ui analyze \
--input input/reference.png \
--meta input/meta.json \
--output-layout extracted/stitch-layout.raw.json \
--output-styles extracted/stitch-styles.raw.jsonWindows (PowerShell):
vision-ui analyze `
--input input\reference.png `
--meta input\meta.json `
--output-layout extracted\stitch-layout.raw.json `
--output-styles extracted\stitch-styles.raw.jsonOr call the HTTP API directly in any language (see §9.2):
POST {STITCH_API_BASE_URL}/stitch/vision/analyze
Step 2 — Normalize raw extraction to token-snapped layout
macOS / Linux:
vision-ui normalize \
--layout extracted/stitch-layout.raw.json \
--tokens themes/brand-default.json \
--output normalized/layout.normalized.jsonWindows (PowerShell):
vision-ui normalize `
--layout extracted\stitch-layout.raw.json `
--tokens themes\brand-default.json `
--output normalized\layout.normalized.jsonReview snap deltas in the output. Any delta > 4 px warrants manual verification.
Step 3 — Generate component
macOS / Linux:
vision-ui generate \
--input normalized/layout.normalized.json \
--template <your-stack-template> \
--theme themes/brand-default.json \
--output generated/HeroCard.<ext>Windows (PowerShell):
vision-ui generate `
--input normalized\layout.normalized.json `
--template <your-stack-template> `
--theme themes\brand-default.json `
--output generated\HeroCard.<ext>Step 4 — Validate
Run your stack's equivalent of the following checks. All must pass before the component is considered done.
| Check | Purpose |
|---|---|
| Static analysis / type check | No type errors |
| Linter | No style violations |
| Unit + integration tests | All specs green |
| Accessibility audit | No critical or serious violations |
| Token compliance | No raw hex or hardcoded brand values in component source |
Then verify manually:
- Keyboard / platform accessibility navigation works end-to-end
- Reduced-motion preference stops all oscillation
- Theme swap produces correct brand colors
This path automates the full pipeline. Stitch generates the design; Antigravity writes the code.
Video walkthrough: Google Stitch to Antigravity
Export options from Stitch:
| Method | Best for |
|---|---|
| MCP Server (recommended) | Automated end-to-end, no manual code handling |
| Code to clipboard | Quick paste for single-page designs |
| Copy prompt | Exports Stitch instructions for a landing page |
| AI Studio preview | Testing before committing |
| Figma zip | Design handoff |
| Requirement | macOS / Linux | Windows |
|---|---|---|
| Git | Pre-installed or via package manager | git-scm.com |
| Chromium browser | Via package manager or chromium.org | chromium.org |
| Antigravity access | antigravity.google | Same |
The Stitch MCP CLI is installed as part of the MCP server connection — no separate runtime installation is needed before connecting.
- Open stitch.withgoogle.com in Chromium
- Go to Settings → API Key → Create API Key
- Copy the key — you will need it in the next step
Never commit your API key to source control. Store it using your platform's secret management (see §9.1).
Open antigravity.google and configure the Stitch MCP server via Agent Panel → (⋯) → MCP Servers → View Raw Config:
{
"mcpServers": {
"stitch": {
"serverUrl": "https://stitch.googleapis.com/mcp",
"headers": { "X-Goog-Api-Key": "YOUR-API-KEY" }
}
}
}Restart Antigravity, then verify: ⋯ → MCP Servers → Manage MCP Servers — confirm Stitch appears with tools: get_projects, list_projects, get_screens, get_design_system.
Tip: Enable User Settings → Permissions → Always proceed to avoid repeated permission prompts during code generation.
# List your projects
MCP Stitch list projects
# Get screens for your project
MCP Stitch get screens for project <project-id>
# Generate all screens as a website
list all screens and make a website
# Target a specific stack
Take screen 1 from project <project-id> and generate a component for <stack>.
Use my project's conventions and --ds-* CSS variables for theming.
# Generate the design system bridge
Also generate a theme bridge that maps the Stitch design system
to my runtime theme layer and --ds-* CSS variables.
When integrating into an existing project, prime Antigravity with explicit context before generating anything:
I have an existing <stack> project.
Generate components in <component-format> using <ui-library or native primitives>.
Use CSS variables in the --ds-* namespace for all design tokens.
Do not generate code outside this stack.
Place business logic in the project's standard architecture pattern.
Then:
- Copy generated components into your target folders
- Add/update the theme bridge so
--ds-*tokens (or platform equivalents) resolve at runtime - Reconcile naming, namespaces, and import paths to your architecture
- Run your build/test/lint pipeline
Deploy (optional):
connect Netlify through MCP
host the website on Netlify
| Issue | Cause | Fix |
|---|---|---|
| Wrong framework generated | Stack context prompt too vague | Re-run with explicit stack and file format |
| Components render without theme | Theme bridge missing | Map runtime theme values to --ds-* tokens |
--ds-* vars missing at runtime |
Token emission not mounted early enough | Emit token vars at app root / document head |
| Layout drift on small screens | Constraints converted too literally | Re-map to responsive constraints |
| Motion feels janky | Animating layout-thrashing properties | Animate transform and opacity only |
| Wrong background color generated | AI misread the design | Prompt: "The background color needs to be black." |
| MCP calls fail intermittently | Permissions or network | Enable persistent permissions; check proxy/firewall |
This section defines the behaviour contract for motion. Implement it using the animation library or system available in your stack. All values are defined as tokens; implementation is stack-specific (see Appendix B).
Store these as named constants in your stack's token layer. Reference them in all animation code — never hardcode durations or spring values.
motion.duration.fast = 120ms (hover, press)
motion.duration.base = 180ms (standard transitions)
motion.duration.slow = 260ms (deliberate entrances)
motion.spring.stiffness = 220
motion.spring.damping = 22
motion.spring.mass = 0.9
motion.float.y.from = 0px
motion.float.y.to = -4px
motion.float.period = 4s
Define depth as named constants — never use raw numbers in component code.
depth.base = 0
depth.raised = 10
depth.floating = 40
depth.overlay = 70
depth.modal = 100
depth.toast = 120
Every floating component must implement these three states:
| State | Behaviour |
|---|---|
| Initial | opacity: 0, translateY: 12px, scale: 0.99 |
| Enter | opacity: 1, translateY: 0, scale: 1 — spring transition using token values |
| Hover / focus | translateY: −4px, scale: 1.01 — duration: motion.duration.fast |
Floating decorative elements additionally animate continuously between float.y.from and float.y.to on a float.period cycle with ease-in-out.
This animation must be gated behind the platform's reduced-motion preference. When reduced motion is requested, the element must remain static.
Every component containing animation must check the platform's reduced-motion preference:
IF platform indicates reduced motion is preferred:
disable idle float entirely
show element at final state immediately (no entrance animation)
retain interaction feedback only if duration ≤ 120ms
ELSE:
run full animation suite
Platform equivalents:
| Platform / Stack | Reduced-motion API |
|---|---|
| Web (CSS) | @media (prefers-reduced-motion: reduce) |
| Web (JS) | window.matchMedia("(prefers-reduced-motion: reduce)") |
| .NET MAUI / WPF | OS accessibility flags via SystemParameters |
| iOS / SwiftUI | UIAccessibility.isReduceMotionEnabled |
| Android | Settings.Global.ANIMATOR_DURATION_SCALE == 0 |
- Only animate
transform(translate, scale) andopacity. Never animatewidth,height,top,left, or any property that triggers layout recalculation. - All perpetual animations must be gated on the reduced-motion preference.
- Interaction feedback must complete within 250 ms for critical controls.
- Floating elements with interactive behaviour must have an accessible label.
- Depth must be assigned from depth token constants — never a raw number.
- Document layer assignment in a component comment.
Use this spec when implementing FloatingBadge in any stack:
Positioning:
- Absolute, anchored to top-right of parent
- Parent must have relative / bounded positioning
- Depth: depth.floating
Styling (resolve from tokens):
- Background: component.floatingBadge.bg
- Text color: component.floatingBadge.text
- Border radius: semantic.radius.chip (pill)
- Accessible label required
Motion:
- Applies: Initial → Enter → Hover states (§5.3)
- Idle float on inner content element (§5.4)
- Reduced-motion: static position, no oscillation
Positioning:
- In document flow (not floating), depth: depth.raised
- Contains FloatingBadge as an absolute child
Styling (resolve from tokens):
- Background: component.heroCard.bg
- Padding: semantic.spacing.card.padding
- Border radius: semantic.radius.card
- Shadow: component.heroCard.shadow
- Heading color: semantic.color.text.primary
- Body color: semantic.color.text.secondary
Motion:
- Entrance only (Initial → Enter)
- No idle float on the card itself
Copy these into
SKILLS.mdandRULES.mdin your project to prime your AI agent.
You are a Senior Full-Stack Architect and Design Systems Lead who: translates visual references into semantic UI architecture; builds tokenized, whitelabel-capable component systems; implements interaction depth with predictable floating motion; and generates production-ready code that passes lint, type, and accessibility gates — in the target stack, whatever that stack may be.
Generation is incomplete until every gate passes:
- Static analysis / type checks pass (exit code 0 or equivalent)
- Linter reports 0 blocking violations
- Accessibility audit reports 0 critical or serious violations
- Token compliance confirms 0 raw hardcoded brand values in component source
- Theme swap with an alternate brand pack produces correct overrides
- No unresolved
TODOmarkers in final output (unless explicitly deferred)
| Do Not | Do Instead |
|---|---|
| Hardcode hex colors in component source | Reference design token (CSS var or platform equivalent) |
| Hardcode spacing or radius literals | Reference spacing / radius tokens |
| Use arbitrary z-index or depth values | Reference named depth token constants |
Animate width, height, top, or left |
Animate transform and opacity only |
| Use non-semantic elements as controls without full keyboard / accessibility support | Use native interactive elements or add full semantics |
| Use default / unnamed exports for components | Use named exports (or platform equivalent) |
| Ship code that fails any quality gate | Fix all violations first |
| Run perpetual animations without a reduced-motion check | Gate all infinite animations on platform reduced-motion preference |
Every generation run must deliver:
| # | Artifact |
|---|---|
| 1 | Component source (in the target stack's format) |
| 2 | Token mapping summary (JSON) |
| 3 | Motion behaviour summary (Markdown) |
| 4 | Accessibility notes (Markdown) |
| 5 | Validation status (JSON — pass/fail per gate) |
The stack can change; the contracts cannot.
| Contract | Must remain stable across all stacks |
|---|---|
| Token namespace | core.*, semantic.*, component.* key structure |
| CSS variable names | --ds-{token-path} format (web); equivalent on native |
| Depth token values | Same numeric values, named constants |
| Motion token values | Same durations and spring params |
| Quality gates | Same pass criteria, regardless of tooling |
| Output artifacts | Same five deliverables |
Replace {{...}} placeholders with real values.
Analyze this high-fidelity UI screenshot. Return a structured layout tree in "layout-tree-v1" format.
Extract:
- Layout regions with parent-child hierarchy (stack-vertical | stack-horizontal | grid | freeform)
- Typography: font family class, size, weight, line-height
- Visual effects: border-radius, box-shadow tier (sm/md/lg), opacity
- Depth ordering: base | raised | floating | overlay
- Absolute bounds {x, y, width, height} per node
- Confidence score (0.0–1.0) per node — flag nodes below 0.82 as "uncertain: true"
Viewport: {{width}}×{{height}} at {{density}}x. Platform: {{platform}}.
Expected components (hints): {{expectedComponents}}.
Output: layout-tree-v1 JSON only. No prose outside the JSON.
Normalize this raw Stitch layout tree. Map every pixel value to the nearest token in the provided scale.
Scales: spacing {{spacingScale}}, radius {{radiusScale}}, font sizes {{fontSizeScale}}, shadows {{shadowTiers}}.
Rules:
1. Snap each raw value to the nearest token step.
2. Record: raw value, snapped token name, resolved px value, snap delta.
3. If delta > 4px, add a warning to the "warnings" array.
4. Every dimension must appear — no silent discards.
5. Preserve the original confidence score.
Return: normalized layout JSON + snapLog array + warnings array.
Generate a production-ready whitelabel UI component.
Source: normalized layout tree (below).
Target stack: {{stackName}}
Language / runtime: {{runtimeLanguageAndVersion}}
UI layer: {{uiLibraryOrNativePrimitives}}
Motion approach: {{motionApproach}}
Token delivery: CSS variables in the --ds-* namespace (token map below).
If CSS variables are not supported on this platform, use: {{platformTokenAlternative}}
Requirements:
1. All colors, spacing, radius, z-index, and shadows resolve from design tokens — no hardcoded hex.
2. Use native / semantic elements before custom accessibility overrides.
3. All interactive elements must be keyboard or platform-equivalent accessible.
4. Implement entrance (initial → enter), hover / focus lift, and idle float (floating layer only).
5. Gate idle float behind the platform's reduced-motion preference.
6. Named exports only (or platform equivalent).
7. Apply strict typing / static analysis rules for the target stack.
Normalized layout: {{normalizedLayout}}
Token map: {{tokenMap}}
Deliver: component source, token mapping JSON, motion behaviour .md, a11y notes .md, validation status JSON.
Derive a semantic-token brand pack from these inputs:
- Primary brand color: {{primaryHex}}
- Accent color: {{accentHex}}
- Brand name: {{brandName}}
Rules:
1. Generate a 10-stop ramp (index 0–9) where index 5 = primaryHex.
2. Ensure WCAG AA contrast (4.5:1) between index 6 and #FFFFFF.
3. Map ramp to core.color.brand.* token keys.
4. Map accent to core.color.accent.500.
5. Include only keys that differ from the default brand pack.
Default brand pack: {{defaultBrandPack}}
Output:
1. brandPack JSON: { "id": "{{brandId}}", "name": "{{brandName}}", "overrides": { ... } }
2. Optional theme bridge adapter for {{stackName}}
Review this component against the vision-ui quality gates.
HARD BLOCKERS — fail if any are present:
1. Raw hex color values in component source
2. Hardcoded spacing or radius literals when a token equivalent exists
3. Depth / z-index values outside the named depth token set
4. Animation of width, height, top, or left
5. Default / unnamed exports for components
6. Unsafe / untyped values without justification
7. Interactive elements not reachable via keyboard or platform accessibility
8. Perpetual animations without a reduced-motion preference check
For each violation: state the rule number, quote the offending code, provide the corrected version.
If none: respond "PASS — all quality gates satisfied."
Component source: {{componentSource}}
Convert this component to the target stack.
Stack: {{stackName}} | Files: {{fileFormats}}
UI layer: {{uiLayer}} | State pattern: {{statePattern}}
Token delivery: resolve all design values via --ds-* CSS variables,
or via {{platformTokenAlternative}} if CSS variables are unavailable.
Validation commands: {{validationCommands}}
Preserve: component semantics, hierarchy, token mapping, reduced-motion behaviour,
depth layering, and accessibility (labels, keyboard / platform nav, focus visibility).
Do not introduce stack-specific anti-patterns.
Return migration notes for any intentional adaptation.
| # | Gate | Pass Criteria |
|---|---|---|
| 1 | Static analysis | Type checker exits cleanly |
| 2 | Lint | 0 blocking violations |
| 3 | Tests | All specs green |
| 4 | Keyboard / platform nav | Full navigation, no traps |
| 5 | Color contrast | WCAG AA (4.5:1 normal / 3:1 large) |
| 6 | Reduced motion | Perpetual animations disabled; entrances collapse |
| 7 | Token compliance | 0 raw hex or hardcoded brand values in component source |
| 8 | Theme swap | Visual verification with 2+ brand packs |
| Failure | Symptom | Recovery |
|---|---|---|
| Ambiguous component boundary | Confidence < 0.82 | Emit placeholder + TODO annotation |
| Typography mismatch | Size doesn't match token tier | Snap to nearest tier; store source value in metadata |
| Floating collisions on resize | Element overlaps primary action at narrow viewport | Enforce collision constraints; hide ornaments below 640px |
| Theme drift | Raw hex found in component source | Replace with design token reference |
| Motion jank | Animating layout-thrashing properties | Switch to transform / opacity only |
Setup
- API keys stored securely — not committed to source control
- Secret storage added to repository ignore file
- Stitch MCP connected and tools verified (if using Antigravity)
Tokens & Theming
- All token references validated (no unresolved aliases)
- No hardcoded brand values in component source
- Theme bridge resolves tokens at runtime (CSS vars on web; platform theme on native)
- Theme swap verified with 2+ brand packs
Component Quality
- Responsive: desktop → tablet → mobile
- Keyboard / platform accessibility navigation works end-to-end
- Reduced-motion preference stops all oscillation
- All quality gates pass (static analysis, lint, tests, a11y, contrast)
- Floating depth layers reference named depth token constants
Documentation
- Token mapping summary committed alongside component
- Motion behaviour summary committed alongside component
The Stitch and Antigravity APIs are plain HTTP with JSON bodies. Implement the client in whichever language your stack uses. This section documents the HTTP contracts and configuration patterns only — not a specific language implementation.
Never commit API keys to source control. Use your platform's standard secret management:
| Platform | Recommended approach |
|---|---|
| .NET | dotnet user-secrets, Azure Key Vault, or appsettings.json environment overrides |
| Java / Kotlin | .env + dotenv library, or environment variables passed at startup |
| Python | .env + python-dotenv, or OS environment variables |
| JavaScript / Node.js | .env (git-ignored) loaded by your runtime or build tool |
| Any CI/CD | Repository secrets (GitHub Actions, Azure DevOps, GitLab CI, etc.) |
Setting environment variables — cross-platform:
macOS / Linux:
export STITCH_API_KEY=your_key_here
export STITCH_API_BASE_URL=https://stitch.googleapis.com/v1
export ANTIGRAVITY_API_BASE_URL=http://localhost:3100
export ANTIGRAVITY_API_KEY=your_key_hereWindows (PowerShell):
$env:STITCH_API_KEY = "your_key_here"
$env:STITCH_API_BASE_URL = "https://stitch.googleapis.com/v1"
$env:ANTIGRAVITY_API_BASE_URL = "http://localhost:3100"
$env:ANTIGRAVITY_API_KEY = "your_key_here"Windows (Command Prompt):
set STITCH_API_KEY=your_key_here
set STITCH_API_BASE_URL=https://stitch.googleapis.com/v1Request:
POST {STITCH_API_BASE_URL}/stitch/vision/analyze
Authorization: Bearer {STITCH_API_KEY}
Content-Type: application/json
{
"image": "<base64-encoded PNG or public HTTPS image URL>",
"viewport": { "width": 1440, "height": 1024 },
"extract": ["structure", "spacing", "typography", "effects", "depth"],
"outputFormat": "layout-tree-v1",
"options": {
"confidenceThreshold": 0.82,
"snapToGrid": true,
"gridBase": 4
}
}
Response shape:
{
"version": "layout-tree-v1",
"nodes": [
{
"id": "node-001",
"type": "container",
"bounds": { "x": 0, "y": 0, "width": 1440, "height": 600 },
"layout": "stack-vertical",
"children": ["node-002", "node-003"],
"confidence": 0.94
}
],
"styles": [
{
"nodeId": "node-002",
"typography": { "family": "Inter", "size": 32, "weight": 700, "lineHeight": 1.2 },
"effects": { "radius": 16, "shadow": "md" },
"confidence": 0.89
}
],
"depth": [
{ "nodeId": "node-003", "layer": "floating", "zIndex": 40 }
]
}Validate the response before processing: confirm version === "layout-tree-v1", all nodes have confidence, all nodes have bounds.
Request:
POST {ANTIGRAVITY_API_BASE_URL}/normalize
X-Antigravity-Key: {ANTIGRAVITY_API_KEY}
Content-Type: application/json
{
"layout": { <raw Stitch layout-tree-v1 response> },
"tokens": { <brand-default.json content> }
}
Response shape:
{
"normalized": { },
"snapLog": [
{ "path": "heroCard.padding", "raw": 23, "snapped": "spacing.6", "resolved": 24, "delta": 1 }
],
"warnings": ["Snap delta > 4px at heroCard.cornerRadius (raw: 20, snapped: 16)"]
}Log all warnings. Review any snap delta > 4 px manually before continuing.
Request:
POST {ANTIGRAVITY_API_BASE_URL}/generate
X-Antigravity-Key: {ANTIGRAVITY_API_KEY}
Content-Type: application/json
{
"layout": { <normalized layout from /normalize> },
"template": "<your-stack-template-name>",
"theme": { <brand-default.json content> }
}
Response shape:
{
"componentSource": "<generated source code as a string>",
"tokenMapping": { "heroCard.bg": "#FFFFFF", "heroCard.radius": "16px" },
"motionSummary": "Entrance: spring y 12→0, opacity 0→1 ...",
"a11yNotes": "FloatingBadge requires accessible label ...",
"validationStatus": {
"lint": "pass",
"types": "pass",
"tests": "pass",
"a11y": "pass",
"tokens": "pass"
}
}Check validationStatus before consuming the output. If any gate is "fail", fix the reported issues and regenerate — do not ship.
// 1. Read image and encode to base64
imageData = base64_encode(read_file("input/reference.png"))
// 2. Call Stitch analyze
extracted = http_post(
url = STITCH_API_BASE_URL + "/stitch/vision/analyze",
headers = { "Authorization": "Bearer " + STITCH_API_KEY },
body = {
image: imageData, viewport: { width: 1440, height: 1024 },
extract: ["structure","spacing","typography","effects","depth"],
outputFormat: "layout-tree-v1",
options: { confidenceThreshold: 0.82, snapToGrid: true, gridBase: 4 }
}
)
// 3. Normalize
brandTokens = read_json("themes/brand-default.json")
normalized = http_post(
url = ANTIGRAVITY_API_BASE_URL + "/normalize",
headers = { "X-Antigravity-Key": ANTIGRAVITY_API_KEY },
body = { layout: extracted, tokens: brandTokens }
)
if normalized.warnings is not empty:
log_warnings(normalized.warnings) // review deltas > 4px
// 4. Generate
generated = http_post(
url = ANTIGRAVITY_API_BASE_URL + "/generate",
headers = { "X-Antigravity-Key": ANTIGRAVITY_API_KEY },
body = { layout: normalized.normalized, template: YOUR_TEMPLATE, theme: brandTokens }
)
// 5. Gate check — halt if any gate failed
if any value in generated.validationStatus == "fail":
log_error(generated.validationStatus)
exit(1)
// 6. Write output
write_file("generated/HeroCard.<ext>", generated.componentSource)
write_file("generated/HeroCard.token-map.json", generated.tokenMapping)
write_file("generated/HeroCard.motion.md", generated.motionSummary)
write_file("generated/HeroCard.a11y.md", generated.a11yNotes)
| HTTP Status | Meaning | Action |
|---|---|---|
| 429 | Rate limited | Retry with exponential backoff (start at 1 s) |
| 503 | Service unavailable | Retry up to 3 times, then surface error to user |
| 401 / 403 | Authentication failure | Check API key; do not retry |
| 400 | Bad request | Fix request body against schema; do not retry |
200 with any gate "fail" |
Generated but invalid | Fix gate violations; regenerate; do not ship |
| Term | Definition |
|---|---|
| Token | A named design value (e.g., spacing.4 = 16px) used instead of hardcoded numbers. |
| Semantic token | A role-based token (e.g., text.primary) aliasing a core primitive to convey intent. |
| Whitelabel | Same product UI, different brand per client — achieved by swapping a token override set. |
| Antigravity | Google's AI-powered IDE/platform (antigravity.google). Also used here as a design concept for floating UI composition. |
| Stitch extraction | Converting an image's visual structure into machine-readable layout and style data. |
| MCP | Model Context Protocol — connects tools (Stitch, Netlify) to Antigravity for automated workflows. |
| Confidence score | Model certainty for an inferred element (0.0–1.0). Below 0.82 triggers a fallback. |
| Brand pack | JSON file of token overrides for a specific client brand. |
| Token snapping | Rounding a raw pixel value to the nearest step in the token scale. |
| Theme bridge | Stack-specific code that maps the resolved token JSON to CSS custom properties or a platform theme object. |
The token contracts and quality gates are identical across all stacks. What changes is how you implement the theme bridge, motion system, and reduced-motion check. Quick reference:
| Stack | Token delivery | Motion approach | Reduced-motion check |
|---|---|---|---|
| React / Next.js | CSS custom properties on :root |
Motion (Framer Motion), CSS transitions, or GSAP | window.matchMedia("(prefers-reduced-motion: reduce)") |
| Vue / Nuxt | CSS custom properties on :root |
CSS transitions, GSAP, or Motion Vue | Same CSS media query via composable |
| Angular | CSS custom properties, or Angular CDK theme | Angular Animations API or GSAP | CSS media query via service or CDK |
| .NET Blazor (WASM / Server) | CSS custom properties injected via JS interop, or MudBlazor theme object | Blazor.Animate or CSS transitions | CSS media query evaluated on render |
| ASP.NET MVC / Razor Pages | CSS custom properties in shared layout | CSS transitions or JS animation library via CDN | CSS @media query |
| .NET MAUI | Resource dictionaries merged at app level | Platform animation APIs | UIAccessibility.isReduceMotionEnabled (iOS) / OS accessibility flags (Android, Windows) |
| WPF / WinUI | Resource dictionaries | Storyboard / VisualStateManager | SystemParameters.MessageFontSize or OS accessibility flag |
| SwiftUI | @Environment key or custom EnvironmentKey |
.animation() modifier, withAnimation |
UIAccessibility.isReduceMotionEnabled |
| Jetpack Compose | MaterialTheme or custom CompositionLocal |
animate*AsState, Transition APIs |
LocalAccessibilityManager / ANIMATOR_DURATION_SCALE |
For any stack: the --ds-* CSS variable naming convention is canonical for web. For non-web platforms, emit the resolved token map as a strongly-typed theme object in your language, preserving the three-tier hierarchy (core, semantic, component).
| Resource | URL |
|---|---|
| Google Antigravity | https://antigravity.google/ |
| Google Stitch | https://stitch.withgoogle.com/ |
| Video: Stitch → Antigravity Walkthrough | https://www.youtube.com/watch?v=FLQWXRzKMS4 |
| WCAG AA contrast requirements | https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum |
| axe-core accessibility engine (web) | https://github.com/dequelabs/axe-core |
| Motion / Framer Motion docs | https://motion.dev/docs/react |
| MudBlazor (.NET component library) | https://mudblazor.com/ |
| Blazor.Animate | https://github.com/mikoskinen/Blazor.Animate |
| bUnit (Blazor testing) | https://bunit.dev/ |
| Netlify (free hosting) | https://www.netlify.com/ |
| Pinterest (design inspiration) | https://www.pinterest.com/ |