agentsclimarketplace

Stitch react components

Skill gabelul/stitch-kit/skills/stitch-react-components

Your coding agent designs terrible UI. stitch-kit wires it into Google Stitch MCP and teaches it the whole pipeline — ideation, screen generation, design systems, production components.

Install
npx -y skills add gabelul/stitch-kit --skill stitch-react-components

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Converts a Stitch screen, a local HTML file, or a URL into modular Vite + React components — TypeScript, theme-mapped Tailwind, dark mode via CSS variables, and clean component architecture. Use this for Vite/React apps without App Router. For Next.js 15 App Router, use stitch-nextjs-components instead. Only the Stitch route needs an API key.

SKILL.md

9.8 KB, as published. Nobody here has run it

Stitch → Vite / React Components

Constraint: Only use this skill when the user explicitly mentions "Stitch" and React (Vite, CRA, or just "React app" without Next.js).

You are a frontend engineer converting Stitch mobile/desktop designs into clean, modular React components using Vite + TypeScript. This skill targets plain React apps — not Next.js App Router. For Next.js, use stitch-nextjs-components instead.

When to use this skill vs. Next.js

ScenarioUse
User says "React app", "Vite", "CRA"stitch-react-components
User says "Next.js", "App Router", "SSR"stitch-nextjs-components
User wants shadcn/ui components added afterstitch-react-components → then stitch-shadcn-ui
User wants server-side rendering or file-based routingstitch-nextjs-components

Prerequisites

An HTML source. Any one of these works:

  • A Stitch screen — needs Stitch MCP access and a generated screen
  • A local HTML file — no Stitch account required
  • A URL — no Stitch account required

Also:

  • Node.js + npm/pnpm
  • Vite + React project initialized: npm create vite@latest my-app -- --template react-ts

Step 1: Resolve the source

Everything downstream reads one file: temp/source.html. Get the HTML there by whichever route matches what the user gave you, then continue at Step 2 — the rest of this skill is identical regardless of where the markup came from.

From a Stitch screen:

  1. Namespace discoverylist_tools to find the Stitch MCP prefix
  2. Fetch metadata[prefix]:get_screen with numeric projectId and screenId
  3. Download HTML — GCS URLs need the reliable downloader:
    bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" "temp/source.html"
    
  4. Visual audit — check screenshot.downloadUrl before rewriting. Append =s0 to that URL for full resolution; the bare URL serves a 512px thumbnail regardless of the width/height the API reports.

From a local HTML file:

mkdir -p temp && cp "path/to/design.html" temp/source.html

From a URL:

bash scripts/fetch-stitch.sh "https://example.com/page" "temp/source.html"

Despite the name, that script is a generic hardened downloader — follows redirects, retries transient failures, handles gzip, and fails loudly on an empty result. It does not care whether the URL points at Stitch.

From a screenshot: there's no upload route — the Stitch MCP API has no image-upload tool. Either recreate the design from a text prompt via stitch-mcp-generate-screen-from-text, or hand-write the HTML and use the local-file route above.

Only the Stitch route needs an API key. Converting a local file or a URL works with no Google account at all.

Step 2: Project structure

src/
├── components/           ← One file per component
│   └── [Name].tsx
├── data/
│   └── mockData.ts       ← Static content (never in components)
├── theme/
│   ├── tokens.ts         ← Design token constants
│   └── useTheme.ts       ← Dark mode hook
├── types/
│   └── index.ts          ← Shared TypeScript types
├── App.tsx               ← Root component
└── main.tsx              ← Entry point

Step 3: Extract design tokens

Resolve tokens from whatever the HTML actually gives you, in this order:

  1. Inline tailwind.config in <head> (what Stitch emits) — use it directly if present.
  2. CSS custom properties (:root { --color-primary: ... }) — common in hand-written and templated HTML.
  3. A linked or inline stylesheet — parse declared colors, font-families, radii, spacing.
  4. Last resort — derive tokens from the most frequent computed values in the markup (dominant background, text color, accent, heading/body font, border radius), and tell the user what you inferred so they can correct it.

The URL route only downloads the single HTML response — externally-linked stylesheets may not come along for the ride. If none of the above resolves a token, say so instead of inventing a palette.

// src/theme/tokens.ts
export const lightTokens = {
  background: '#FFFFFF',
  surface:    '#F4F4F5',
  primary:    '#6366F1',
  primaryFg:  '#FFFFFF',
  text:       '#09090B',
  textMuted:  '#71717A',
  border:     '#E4E4E7',
} as const

export const darkTokens = {
  background: '#09090B',
  surface:    '#18181B',
  primary:    '#818CF8',
  primaryFg:  '#09090B',
  text:       '#FAFAFA',
  textMuted:  '#A1A1AA',
  border:     '#27272A',
} as const

export type ThemeTokens = typeof lightTokens
// src/theme/useTheme.ts
import { useEffect, useState } from 'react'
import { lightTokens, darkTokens, type ThemeTokens } from './tokens'

/**
 * Returns current theme tokens based on system color scheme.
 * Listens for system-level dark/light mode changes.
 */
export function useTheme(): ThemeTokens {
  const [isDark, setIsDark] = useState(
    () => window.matchMedia('(prefers-color-scheme: dark)').matches
  )

  useEffect(() => {
    const mq = window.matchMedia('(prefers-color-scheme: dark)')
    const handler = (e: MediaQueryListEvent) => setIsDark(e.matches)
    mq.addEventListener('change', handler)
    return () => mq.removeEventListener('change', handler)
  }, [])

  return isDark ? darkTokens : lightTokens
}

Step 4: Component conversion rules

Layout mapping

HTML/CSS→ React / Tailwind
display:flex; flex-direction:column<div className="flex flex-col gap-4">
display:flex; flex-direction:row<div className="flex items-center gap-2">
justify-content:space-between<div className="flex justify-between">
display:grid; grid-template-columns:1fr 1fr<div className="grid grid-cols-2 gap-4">
overflow-y:scroll<div className="overflow-y-auto">
Long listitems.map(item => <Card key={item.id} {...item} />)
<img><img src="..." alt="..." className="object-cover">

Tailwind class mapping

Use the source HTML's Tailwind classes directly in JSX where they don't reference custom tokens. Map custom tokens to CSS variables:

// Source HTML: bg-primary → CSS variable → Tailwind arbitrary value
// OR: use inline style with token value

// Option A — Tailwind arbitrary value (if custom tokens in tailwind.config)
<div className="bg-[--color-primary] text-[--color-primaryFg]">

// Option B — inline style with useTheme()
const theme = useTheme()
<div style={{ backgroundColor: theme.primary, color: theme.primaryFg }}>

Component template

// src/components/StitchComponent.tsx

/**
 * Props for StitchComponent — all data via props, never fetched inside.
 */
interface StitchComponentProps {
  /** Primary heading text */
  title: string
  /** Supporting description — optional */
  description?: string
  /** Primary action callback */
  onAction?: () => void
}

/**
 * StitchComponent — [describe purpose in one sentence]
 */
export function StitchComponent({
  title,
  description,
  onAction,
}: Readonly<StitchComponentProps>) {
  const theme = useTheme()

  return (
    <div
      className="rounded-xl border p-4 gap-2 flex flex-col"
      style={{
        backgroundColor: theme.surface,
        borderColor: theme.border,
      }}
    >
      <h3 className="text-base font-semibold" style={{ color: theme.text }}>
        {title}
      </h3>

      {description ? (
        <p className="text-sm" style={{ color: theme.textMuted }}>
          {description}
        </p>
      ) : null}

      {onAction ? (
        <button
          onClick={onAction}
          className="rounded-lg px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90"
          style={{ backgroundColor: theme.primary, color: theme.primaryFg }}
          type="button"
        >
          Action
        </button>
      ) : null}
    </div>
  )
}

Step 5: Architectural rules

  • One component per file — no single-file spaghetti
  • Static data in src/data/mockData.ts — never hardcoded in JSX
  • Shared types in src/types/index.ts
  • Every component has Readonly<ComponentNameProps> interface
  • No hardcoded hex colors — use useTheme() or CSS variables
  • No any types

Step 6: Integration with shadcn/ui

After converting the design to base React components, you can layer in shadcn/ui:

npx shadcn@latest init    # Set up shadcn in your Vite project
npx shadcn@latest add button card input dialog

Then use stitch-shadcn-ui skill to replace raw HTML elements with shadcn components while preserving the design tokens.

Troubleshooting

IssueFix
Tailwind classes not applyingCheck tailwind.config.js includes ./src/**/*.{ts,tsx} in content
Dark mode not togglingVerify useTheme() is called at component level, not hoisted
Images not showingAdd explicit width and height or use className="w-full h-auto"
Type error on propsEnsure Readonly<> wrapper and all required props are provided

References

  • resources/component-template.tsx — Boilerplate component
  • resources/architecture-checklist.md — Pre-ship checklist
  • references/tailwind-to-react.md — Token + class mapping guide (source HTML → React/Tailwind)
  • scripts/fetch-stitch.sh — Reliable GCS HTML downloader
  • stitch-shadcn-ui — Add shadcn/ui components after base conversion
  • docs/tailwind-reference.md — Tailwind utility class lookup

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.