mirror of
https://github.com/google-labs-code/stitch-skills.git
synced 2026-09-14 17:29:37 +08:00
Merge pull request #59 from google-labs-code/stitch-skills-plugins
Launch Stitch Skills plugins
This commit is contained in:
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: skills/react-components
|
||||
working-directory: plugins/stitch-build/skills/react-components
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
with:
|
||||
node-version: '18.x'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: skills/react-components/package-lock.json
|
||||
cache-dependency-path: plugins/stitch-build/skills/react-components/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -1,76 +1,111 @@
|
||||
# Stitch Agent Skills
|
||||
# Stitch Design Skills
|
||||
|
||||
A library of Agent Skills designed to work with the Stitch MCP server. Each skill follows the Agent Skills open standard, for compatibility with coding agents such as Antigravity, Gemini CLI, Claude Code, Cursor.
|
||||
A collection of agent skills and plugins for [Google Stitch](https://stitch.withgoogle.com), following the [Agent Skills](https://agentskills.io) open standard. Compatible with coding agents such as Antigravity, Gemini CLI, Claude Code, and Cursor.
|
||||
|
||||
## Installation & Discovery
|
||||
## Quick Start
|
||||
|
||||
Install any skill from this repository using the `skills` CLI. This command will automatically detect your active coding agents and place the skill in the appropriate directory.
|
||||
### 1. Install Plugins (Recommended)
|
||||
The fastest way to set up the full Stitch plugin suite globally.
|
||||
|
||||
```bash
|
||||
# List all available skills in this repository
|
||||
npx skills add google-labs-code/stitch-skills --list
|
||||
npx plugins add google-labs-code/stitch-skills --scope project --target claude-code
|
||||
|
||||
# Install a specific skill
|
||||
npx skills add google-labs-code/stitch-skills --skill react:components --global
|
||||
```
|
||||
|
||||
## Available Skills
|
||||
### 2. Install Skills Selectively
|
||||
Choose only the specific skills you need.
|
||||
|
||||
### stitch-design
|
||||
Unified entry point for Stitch design work. Handles prompt enhancement (UI/UX keywords, atmosphere), design system synthesis (.stitch/DESIGN.md), and high-fidelity screen generation/editing via Stitch MCP.
|
||||
> [!IMPORTANT]
|
||||
> Stitch Design Skills often have inter-dependencies. If you choose to install skills selectively, ensure you include all required dependencies.
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill stitch-design --global
|
||||
npx skills add google-labs-code/stitch-skills
|
||||
```
|
||||
|
||||
### stitch-loop
|
||||
Generates a complete multi-page website from a single prompt using Stitch, with automated file organization and validation.
|
||||
You can run the following commands to see the help documentation for plugins and skills:
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill stitch-loop --global
|
||||
npx plugins --help
|
||||
|
||||
npx skills --help
|
||||
```
|
||||
|
||||
### design-md
|
||||
Analyzes Stitch projects and generates comprehensive DESIGN.md files documenting design systems in natural, semantic language optimized for Stitch screen generation.
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill design-md --global
|
||||
```
|
||||
These skills require the **Stitch MCP** server to be configured and running in your agent's environment. Make sure you have followed the [Stitch MCP Setup Instructions](https://stitch.withgoogle.com/docs/mcp/setup/) to register the server and set up appropriate environment variables and credentials.
|
||||
|
||||
### enhance-prompt
|
||||
Transforms vague UI ideas into polished, Stitch-optimized prompts. Enhances specificity, adds UI/UX keywords, injects design system context, and structures output for better generation results.
|
||||
## Available Plugins
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill enhance-prompt --global
|
||||
```
|
||||
### Design (`stitch-design`)
|
||||
|
||||
### react-components
|
||||
Converts Stitch screens to React component systems with automated validation and design token consistency.
|
||||
Core design workflows for creating, managing, and optimizing designs within Stitch.
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill react:components --global
|
||||
```
|
||||
| Skill | Description | Prompt Example |
|
||||
|---|---|---|
|
||||
| [stitch::code-to-design](plugins/stitch-design/skills/code-to-design/) | Convert frontend code (React, Vue, etc.) to a Stitch Design via HTML extraction + design system + upload | *"Upload the frontend code at `/path/to/dashboard` into a Stitch project named 'Dashboard-Migration-2026'."* |
|
||||
| [stitch::generate-design](plugins/stitch-design/skills/generate-design/) | Generate new screens from text or images, edit existing screens, and create design variants | · *"Make a browse tab for a mobile app for romance and date night ideas."*<br>· *"Edit the login screen to add a 'Remember Me' checkbox and change the button color to blue."*<br>· *"Generate 3 design variants of the home screen with dark mode and high-density layouts."* |
|
||||
| [stitch::manage-design-system](plugins/stitch-design/skills/manage-design-system/) | Manage design systems in Stitch — upload DESIGN.md and apply themes to screens | *"Upload our design system from `.stitch/DESIGN.md` and apply it to all screens."* |
|
||||
| [stitch::extract-design-md](plugins/stitch-design/skills/extract-design-md/) | Extract a comprehensive DESIGN.md directly from frontend source code | *"Scan `/src` and extract the design system into `.stitch/DESIGN.md`."* |
|
||||
| [stitch::extract-static-html](plugins/stitch-design/skills/extract-static-html/) | Extract self-contained static HTML from running web apps, inlining CSS and images | *"Extract a static HTML snapshot of `http://localhost:3000/profile`."* |
|
||||
| [stitch::upload-to-stitch](plugins/stitch-design/skills/upload-to-stitch/) | Upload local assets (images, mockups, HTML) to a Stitch project | *"Upload `.stitch/landing_page.html` to Stitch project `projects/987654321`."* |
|
||||
|
||||
### remotion
|
||||
Generates walkthrough videos from Stitch projects using Remotion with smooth transitions, zooming, and text overlays to showcase app screens professionally.
|
||||
---
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill remotion --global
|
||||
```
|
||||
### Build (`stitch-build`)
|
||||
|
||||
### shadcn-ui
|
||||
Expert guidance for integrating and building applications with shadcn/ui components. Helps discover, install, customize, and optimize shadcn/ui components with best practices for React applications.
|
||||
Code generation, framework integration, and asset compilation from Stitch designs.
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill shadcn-ui --global
|
||||
```
|
||||
| Skill | Description | Prompt Example |
|
||||
|---|---|---|
|
||||
| [react-components](plugins/stitch-build/skills/react-components/) | Convert Stitch screens to React component systems with automated validation and design token consistency | *"Convert all screens in Stitch project `projects/123` to React components."* |
|
||||
| [remotion](plugins/stitch-build/skills/remotion/) | Generate walkthrough videos from Stitch projects using Remotion with smooth transitions and zooming | *"Generate a walkthrough video of the Stitch project `projects/456`."* |
|
||||
| [shadcn-ui](plugins/stitch-build/skills/shadcn-ui/) | Expert guidance for integrating and building applications with shadcn/ui components | *"Set up shadcn/ui and build a data table with sorting and filtering."* |
|
||||
|
||||
---
|
||||
|
||||
### Utilities (`stitch-utilities`)
|
||||
|
||||
Supporting tools for enhancing prompts, generating design specs, and enforcing design standards.
|
||||
|
||||
| Skill | Description | Prompt Example |
|
||||
|---|---|---|
|
||||
| [design-md](plugins/stitch-utilities/skills/design-md/) | Analyze Stitch projects and generate comprehensive DESIGN.md files in semantic language | *"Analyze Stitch project `projects/123` and generate a DESIGN.md."* |
|
||||
| [enhance-prompt](plugins/stitch-utilities/skills/enhance-prompt/) | Transform vague UI ideas into polished, Stitch-optimized prompts with UI/UX keywords | *"Enhance this prompt: 'make a settings page'."* |
|
||||
| [stitch-loop](plugins/stitch-utilities/skills/stitch-loop/) | Generate complete multi-page websites from a single prompt with automated validation | *"Build a 5-page portfolio website with Stitch."* |
|
||||
| [taste-design](plugins/stitch-utilities/skills/taste-design/) | Generate DESIGN.md files enforcing premium, anti-generic UI standards | *"Generate a premium DESIGN.md with strict typography and calibrated colors."* |
|
||||
|
||||
## Repository Structure
|
||||
|
||||
Every directory within `skills/` or at the root level follows a standardized structure to ensure the AI agent has everything it needs to perform "few-shot" learning and automated quality checks.
|
||||
```text
|
||||
plugins/
|
||||
├── stitch-design/ — Core design workflow plugin
|
||||
│ ├── plugin.json
|
||||
│ └── skills/
|
||||
│ ├── code-to-design/
|
||||
│ ├── generate-design/
|
||||
│ ├── manage-design-system/
|
||||
│ ├── extract-design-md/
|
||||
│ ├── extract-static-html/
|
||||
│ └── upload-to-stitch/
|
||||
├── stitch-build/ — Code generation & build plugin
|
||||
│ ├── plugin.json
|
||||
│ └── skills/
|
||||
│ ├── react-components/
|
||||
│ ├── remotion/
|
||||
│ └── shadcn-ui/
|
||||
└── stitch-utilities/ — Design utilities & assistants plugin
|
||||
├── plugin.json
|
||||
└── skills/
|
||||
├── design-md/
|
||||
├── enhance-prompt/
|
||||
├── stitch-loop/
|
||||
└── taste-design/
|
||||
```
|
||||
|
||||
Each skill follows the Agent Skills standard:
|
||||
|
||||
```text
|
||||
skills/[category]/
|
||||
skills/<skill-name>/
|
||||
├── SKILL.md — The "Mission Control" for the agent
|
||||
├── scripts/ — Executable enforcers (Validation & Networking)
|
||||
├── resources/ — The knowledge base (Checklists & Style Guides)
|
||||
@@ -78,6 +113,7 @@ skills/[category]/
|
||||
```
|
||||
|
||||
## Adding New Skills
|
||||
|
||||
All new skills need to follow the file structure above to implement the Agent Skills open standard.
|
||||
|
||||
### Great candidates for new skills
|
||||
|
||||
+1
-1
@@ -9,6 +9,6 @@
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report a security vulnerability, please use the [GitHub Security Advisory "Report a Vulnerability" tab](https://github.com/google-labs-code/jules-sdk/security/advisories/new).
|
||||
To report a security vulnerability, please use the [GitHub Security Advisory "Report a Vulnerability" tab](https://github.com/google-labs-code/stitch-skills/security/advisories/new).
|
||||
|
||||
> **Note:** This is not an officially supported Google product. This project is not eligible for the [Google Open Source Software Vulnerability Rewards Program](https://bughunters.google.com/open-source-security).
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "stitch-build",
|
||||
"description": "Code generation and build tools for Google Stitch — convert designs to React components, generate walkthrough videos with Remotion, and integrate shadcn/ui.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "google-labs-code"
|
||||
},
|
||||
"homepage": "https://github.com/google-labs-code/stitch-skills",
|
||||
"repository": "https://github.com/google-labs-code/stitch-skills",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"stitch",
|
||||
"react",
|
||||
"components",
|
||||
"remotion",
|
||||
"shadcn-ui",
|
||||
"code-generation",
|
||||
"video"
|
||||
]
|
||||
}
|
||||
-15
@@ -1,18 +1,3 @@
|
||||
/**
|
||||
* Copyright 2026 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
// Note for Agent: The '@' alias refers to the target project's src directory.
|
||||
@@ -190,7 +190,7 @@ Create the video components following Remotion best practices:
|
||||
**Reference Resources:**
|
||||
- Use `resources/screen-slide-template.tsx` as starting point
|
||||
- Follow `resources/composition-checklist.md` for completeness
|
||||
- Review examples in `examples/walkthrough/` directory
|
||||
- Review examples in `examples/` directory
|
||||
|
||||
### Step 3: Preview and Refine
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "stitch-design",
|
||||
"description": "Design workflows for Google Stitch — convert code to designs, generate screens from text/images, manage design systems, extract static HTML, and upload assets.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "google-labs-code"
|
||||
},
|
||||
"homepage": "https://github.com/google-labs-code/stitch-skills",
|
||||
"repository": "https://github.com/google-labs-code/stitch-skills",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"stitch",
|
||||
"design",
|
||||
"code-to-design",
|
||||
"design-system",
|
||||
"html-extraction",
|
||||
"mcp",
|
||||
"ui-generation"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
name: stitch::code-to-design
|
||||
description: >-
|
||||
Convert frontend code (Vite, React, etc.) to a Stitch Design by chaining
|
||||
static HTML extraction, design system extraction, and file upload. **ALWAYS** use this skill when the user's intent is to move existing web apps or React components into Stitch (e.g., requests to "save", "migrate", or "upload"). You must use this skill even for simple "save" operations, as it is the only way to ensure the design system is extracted and assets are properly linked.
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Code to Design
|
||||
|
||||
Transform your existing frontend code into a Stitch Design so you can iterate and improve it using Stitch.
|
||||
|
||||
This skill orchestrates three other skills in sequence:
|
||||
1. `extract-static-html`: Extract a single self-contained HTML file from your build output.
|
||||
2. `extract-design-md`: Analyze the source code to create a design system (DESIGN.md).
|
||||
3. `upload-to-stitch`: Upload that HTML file and the design system to your Stitch project.
|
||||
|
||||
## Workflow
|
||||
|
||||
Follow these steps to convert your existing code.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A built web application directory containing `index.html` and assets.
|
||||
- Target Stitch `projectId` (use `list_projects` if unknown).
|
||||
|
||||
### Steps
|
||||
|
||||
#### 1. Extract Self-Contained HTML
|
||||
|
||||
Delegate to the `extract-static-html` skill to generate a standalone HTML file.
|
||||
Read [skills/extract-static-html/SKILL.md](../extract-static-html/SKILL.md) for detailed instructions and script usage.
|
||||
|
||||
Expected output: A single file like `/path/to/extracted/standalone.html`.
|
||||
|
||||
#### 2. Verify HTML (Optional — User-Driven)
|
||||
|
||||
After extraction, inform the user of the output file path so they can manually
|
||||
verify in a browser if desired. **Do not block on verification** — proceed
|
||||
directly to Step 3.
|
||||
|
||||
If the user reports issues after reviewing, fix them before continuing.
|
||||
|
||||
#### 3. Extract Design System (File)
|
||||
|
||||
Delegate to the `extract-design-md` skill to analyze the project's source files
|
||||
(components, stylesheets, theme configs) and produce a design system. Read
|
||||
[skills/extract-design-md/SKILL.md](../extract-design-md/SKILL.md) for the
|
||||
full analysis workflow.
|
||||
|
||||
Write `.stitch/DESIGN.md` following the `extract-design-md` skill's output
|
||||
structure.
|
||||
|
||||
#### 4. Upload DESIGN.md and Create Design System in Stitch
|
||||
|
||||
Delegate to the `manage-design-system` skill to upload the `DESIGN.md` and
|
||||
create the design system in Stitch. Read
|
||||
[skills/manage-design-system/SKILL.md](../manage-design-system/SKILL.md) for
|
||||
the full workflow (upload script usage, `create_design_system_from_design_md`
|
||||
call, and required schemas).
|
||||
|
||||
#### 5. Upload HTML to Stitch
|
||||
|
||||
Use the same `upload-to-stitch` skill's script to upload the extracted HTML file.
|
||||
Read [skills/upload-to-stitch/SKILL.md](../upload-to-stitch/SKILL.md) for detailed instructions and script usage.
|
||||
|
||||
You will need:
|
||||
- The path to the standalone HTML file generated in Step 1.
|
||||
- Your Stitch API Key (same key used in Step 4).
|
||||
- The target `projectId`.
|
||||
@@ -0,0 +1,365 @@
|
||||
---
|
||||
name: stitch::extract-design-md
|
||||
description: >-
|
||||
Extract a comprehensive design system (DESIGN.md) directly from frontend source
|
||||
code — React, Vue, Svelte, Angular, plain HTML/CSS, or any web framework. Analyzes
|
||||
component files, stylesheets, Tailwind configs, theme definitions, and design tokens
|
||||
to produce a rich, Stitch-compatible design system document. Use this skill whenever
|
||||
the user wants to reverse-engineer a design system from an existing codebase, audit
|
||||
the visual language of a project, extract design tokens from source files, or
|
||||
understand the styling patterns in a frontend repo — even if they just say "what
|
||||
does this app look like?" or "pull out the design from this code."
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Extract Design System from Frontend Code
|
||||
|
||||
Analyze frontend source code to extract a comprehensive design system document
|
||||
(DESIGN.md) that captures the project's visual language — colors, typography,
|
||||
spacing, component patterns, and layout principles — directly from the source
|
||||
files, without needing to build or render the application.
|
||||
|
||||
## Why This Exists
|
||||
|
||||
The `design-md` skill works from rendered HTML. But often you have a codebase
|
||||
and want to understand its design system before you can even run the app —
|
||||
maybe dependencies are missing, the build is broken, or you just want a quick
|
||||
audit. This skill reads the source files themselves: stylesheets, component
|
||||
files, theme configs, and Tailwind setups. It's faster and works anywhere.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User has a frontend codebase and wants to extract or document its design system
|
||||
- User wants to migrate a project's visual identity into Stitch
|
||||
- User asks to "audit the styling" or "understand the design language" of a repo
|
||||
- User wants to create a DESIGN.md from existing source code
|
||||
- The app can't be built/rendered but the source is available
|
||||
- User wants to unify or reconcile inconsistent styles across a codebase
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Access to the frontend project's source directory
|
||||
- No build or runtime dependencies needed — this skill reads source files only
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Phase 1: Project Discovery
|
||||
|
||||
Start by understanding what you're working with. This determines which
|
||||
extraction patterns to use.
|
||||
|
||||
#### 1. Detect the Framework and Stack
|
||||
|
||||
Scan the project root for telltale files:
|
||||
|
||||
| Signal File | Framework / Tool |
|
||||
|:---|:---|
|
||||
| `package.json` with `react` | React / Next.js |
|
||||
| `package.json` with `vue` | Vue / Nuxt |
|
||||
| `package.json` with `svelte` | Svelte / SvelteKit |
|
||||
| `package.json` with `@angular/core` | Angular |
|
||||
| `tailwind.config.js/ts` | Tailwind CSS |
|
||||
| `postcss.config.js` | PostCSS pipeline |
|
||||
| `styled-components` or `@emotion` in deps | CSS-in-JS |
|
||||
| `.css` / `.scss` / `.less` files only | Plain CSS / SASS |
|
||||
| `theme.js` / `theme.ts` / `tokens.js` | Design token files |
|
||||
|
||||
Read `package.json` first — it reveals the framework, CSS tooling, and any
|
||||
design-token libraries (e.g., `style-dictionary`, `@chakra-ui/react`,
|
||||
`@mui/material`, `ant-design`). This context tells you *where* to look for
|
||||
styling information.
|
||||
|
||||
#### 2. Map the Source Tree
|
||||
|
||||
Identify the key directories and files you'll analyze:
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ ← Component-level styles
|
||||
├── styles/ ← Global stylesheets
|
||||
├── theme/ ← Theme definitions, tokens
|
||||
├── assets/ ← Fonts, images
|
||||
├── app.css ← Root styles
|
||||
└── index.css ← Entry CSS
|
||||
```
|
||||
|
||||
Also check for:
|
||||
- `tailwind.config.js` / `tailwind.config.ts` — Custom colors, fonts, spacing
|
||||
- `globals.css` / `global.css` — CSS custom properties (variables)
|
||||
- Any `theme.*` or `tokens.*` files
|
||||
- Component library config (e.g., `chakra-theme.ts`, `vuetify.config.ts`)
|
||||
|
||||
#### 3. Read Framework-Specific Guidance
|
||||
|
||||
Consult the appropriate reference for extraction patterns:
|
||||
|
||||
- **React / Next.js / Tailwind** → [references/react-tailwind.md](references/react-tailwind.md)
|
||||
- **Vue / Nuxt** → [references/vue.md](references/vue.md)
|
||||
- **Svelte / SvelteKit** → [references/svelte.md](references/svelte.md)
|
||||
- **Angular** → [references/angular.md](references/angular.md)
|
||||
- **Plain CSS / SASS / Less** → [references/plain-css.md](references/plain-css.md)
|
||||
|
||||
These references contain framework-specific patterns for locating colors,
|
||||
typography, spacing, and component styles. Read the one that matches before
|
||||
proceeding.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Deep Extraction
|
||||
|
||||
Work through each design dimension systematically. For each one, gather raw
|
||||
data from the source files, then synthesize it into descriptive language.
|
||||
|
||||
The goal isn't to dump every CSS property — it's to understand the *intent*
|
||||
behind the styling choices and describe them in human, editorial language that
|
||||
another designer (or Stitch) can use to recreate the same visual feel.
|
||||
|
||||
#### 1. Visual Theme & Atmosphere
|
||||
|
||||
Read the broadest styling first to understand the overall mood:
|
||||
|
||||
- **Root background**: What's the `body` or root element background? Light
|
||||
cream (#f-range) signals airy/clean; dark (#0-#2 range) signals moody/dramatic.
|
||||
- **Whitespace philosophy**: Are spacing values generous (32px+) or tight?
|
||||
Check padding/margin values on root containers, section wrappers, and card components.
|
||||
- **Density**: Count the components per page/section. Few with space = minimal;
|
||||
many packed tight = information-dense.
|
||||
- **Color temperature**: Are the neutrals warm (creams, tans) or cool (blue-grays, slates)?
|
||||
- **Overall feel**: Synthesize into 1-2 rich sentences that capture the mood.
|
||||
|
||||
Look for these signals in the source:
|
||||
|
||||
| Source Location | What It Tells You |
|
||||
|:---|:---|
|
||||
| Root `background-color` or Tailwind `bg-*` on layouts | Overall lightness/darkness |
|
||||
| Spacing scale in Tailwind config or CSS vars | Whitespace philosophy |
|
||||
| Number of components vs. wrapper padding | Density |
|
||||
| Custom property naming (`--warm-*` vs `--cool-*`) | Color temperature intent |
|
||||
| Comments in theme files | Design intent in the developer's own words |
|
||||
|
||||
#### 2. Color Palette & Roles
|
||||
|
||||
Extract every unique color from the codebase and assign functional roles.
|
||||
Search across all layers:
|
||||
|
||||
**Where to find colors:**
|
||||
|
||||
| Layer | What to Search |
|
||||
|:---|:---|
|
||||
| CSS custom properties | `--color-*`, `--primary`, `--bg-*` |
|
||||
| Tailwind config | `theme.extend.colors` |
|
||||
| Theme/token files | Color objects, palettes |
|
||||
| Component styles | `background-color`, `color`, `border-color` |
|
||||
| Inline/scoped styles | `bg-*`, `text-*` classes in templates |
|
||||
| CSS-in-JS theme objects | `colors`, `palette` keys |
|
||||
|
||||
**How to organize:** Group colors by function, not by hue:
|
||||
|
||||
1. **Primary Foundation** — Background and surface colors
|
||||
2. **Accent & Interactive** — CTA buttons, active states, links
|
||||
3. **Typography & Text Hierarchy** — Primary, secondary, tertiary text
|
||||
4. **Functional States** — Success, error, warning, info
|
||||
|
||||
For each color, create a descriptive name that evokes the color's character
|
||||
rather than its raw hex value:
|
||||
|
||||
- ❌ `#294056` → "Blue"
|
||||
- ✅ `#294056` → **"Deep Muted Teal-Navy"** — Primary CTA, active navigation
|
||||
|
||||
**Deduplication matters.** Codebases often have near-duplicate colors (e.g.,
|
||||
`#333` and `#2C2C2C`). Consolidate them under one name that best represents
|
||||
the intended color.
|
||||
|
||||
#### 3. Typography Rules
|
||||
|
||||
Extract the complete typographic system:
|
||||
|
||||
**Font families:**
|
||||
- Check CSS `font-family`, Tailwind `fontFamily`, Google Fonts links, or
|
||||
local `@font-face` declarations.
|
||||
- Note the **character** of each font: geometric vs humanist, serif vs sans,
|
||||
the feeling it evokes.
|
||||
|
||||
**Type scale (hierarchy):**
|
||||
- Find every heading level (H1-H6) and body text, noting:
|
||||
- `font-size` (in rem or px)
|
||||
- `font-weight` (numeric value + descriptive name)
|
||||
- `letter-spacing` (and why — elegance? compactness?)
|
||||
- `line-height` (generous for readability? tight for display?)
|
||||
- Map component usage: Which heading level do product cards use? What about
|
||||
hero sections?
|
||||
|
||||
**Spacing principles:**
|
||||
- How does text spacing relate to the overall spacing scale?
|
||||
- Letter-spacing patterns on headings vs body
|
||||
- Line-height philosophy (generous/relaxed for body, tighter for display)
|
||||
|
||||
#### 4. Component Stylings
|
||||
|
||||
Analyze the 4-5 most important UI primitives:
|
||||
|
||||
**Buttons:**
|
||||
- Corner radius (and what it communicates — playful? professional? minimal?)
|
||||
- Color scheme for primary, secondary, and ghost variants
|
||||
- Hover/focus/active states and transition timing
|
||||
- Padding ratios (horizontal vs vertical)
|
||||
|
||||
**Cards / Containers:**
|
||||
- Corner radius (often different from buttons — slightly rounder)
|
||||
- Shadow strategy: flat, subtle hover shadows, or always elevated?
|
||||
- Border treatment: hairline borders, colored accents, or none?
|
||||
- Internal padding (generous or compact?)
|
||||
- Image treatment within cards (full-bleed, padded, rounded?)
|
||||
|
||||
**Navigation:**
|
||||
- Layout pattern (horizontal bar, vertical sidebar, drawer)
|
||||
- Typography treatment (uppercase, letter-spacing, weight)
|
||||
- Active/hover state indicators (underline, color, background)
|
||||
- Mobile behavior (hamburger, bottom nav, drawer)
|
||||
|
||||
**Inputs & Forms:**
|
||||
- Border style and focus state behavior
|
||||
- Corner style consistency with buttons
|
||||
- Padding and touch-target sizing
|
||||
|
||||
**Domain-Specific Components:**
|
||||
- Identify 1-2 components unique to this project (e.g., product cards,
|
||||
dashboard widgets, chat bubbles) and describe their styling patterns.
|
||||
|
||||
#### 5. Layout Principles
|
||||
|
||||
Extract the structural system:
|
||||
|
||||
**Grid & Structure:**
|
||||
- Max content width (from `max-width` on containers)
|
||||
- Column system (CSS Grid, Flexbox patterns, defined breakpoints)
|
||||
- Responsive breakpoints (from media queries or Tailwind config)
|
||||
|
||||
**Whitespace Strategy:**
|
||||
- Base spacing unit (8px grid? 4px? custom?)
|
||||
- Section margins (how much space between major sections)
|
||||
- Edge padding (page margins at different breakpoints)
|
||||
|
||||
**Alignment & Visual Balance:**
|
||||
- Text alignment patterns (centered heroes, left-aligned body)
|
||||
- Image-to-text ratios
|
||||
- Visual weight distribution
|
||||
|
||||
**Responsive Behavior:**
|
||||
- Mobile-first or desktop-first?
|
||||
- How do grids collapse? Padding scale?
|
||||
- Touch target sizing
|
||||
|
||||
#### 6. Stitch Generation Notes
|
||||
|
||||
Synthesize the extraction into actionable prompts for Stitch:
|
||||
|
||||
- **Atmosphere language**: Translate the mood into natural descriptors
|
||||
- **Color references**: List colors by descriptive name + hex
|
||||
- **Component prompts**: Write 2-3 example prompts that would recreate
|
||||
key components in Stitch
|
||||
- **Iteration guidance**: Tips for refining screens in this design system
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Write the DESIGN.md
|
||||
|
||||
Assemble everything into the standard DESIGN.md format. Place it at
|
||||
`.stitch/DESIGN.md` in the project directory (create the `.stitch/` directory
|
||||
if it doesn't exist).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You **MUST** include the YAML frontmatter at the top of the file with `name` and `colors` mapping, exactly as shown in the example at [examples/DESIGN.md](examples/DESIGN.md). This structured data is required for other skills to parse the design system.
|
||||
>
|
||||
> Failure to include this YAML block with at least the core color tokens is a failure to use this skill correctly.
|
||||
|
||||
Use the format from the example at [examples/DESIGN.md](examples/DESIGN.md) as your template. The file must start with the YAML block, followed by the markdown sections:
|
||||
|
||||
```markdown
|
||||
# Design System: [Project Name]
|
||||
**Project ID:** [If known, otherwise omit]
|
||||
|
||||
## 1. Visual Theme & Atmosphere
|
||||
[Rich 2-paragraph description of mood, philosophy, and key characteristics]
|
||||
|
||||
## 2. Color Palette & Roles
|
||||
### Primary Foundation
|
||||
### Accent & Interactive
|
||||
### Typography & Text Hierarchy
|
||||
### Functional States
|
||||
|
||||
## 3. Typography Rules
|
||||
### Hierarchy & Weights
|
||||
### Spacing Principles
|
||||
|
||||
## 4. Component Stylings
|
||||
### Buttons
|
||||
### Cards & [Domain-Specific Containers]
|
||||
### Navigation
|
||||
### Inputs & Forms
|
||||
### [Domain-Specific Components]
|
||||
|
||||
## 5. Layout Principles
|
||||
### Grid & Structure
|
||||
### Whitespace Strategy
|
||||
### Alignment & Visual Balance
|
||||
### Responsive Behavior & Touch
|
||||
|
||||
## 6. Design System Notes for Stitch Generation
|
||||
### Language to Use
|
||||
### Color References
|
||||
### Component Prompts
|
||||
### Incremental Iteration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Integration (Optional)
|
||||
|
||||
If the user wants to push the design system into Stitch:
|
||||
|
||||
1. Hand off to the `manage-design-system` skill for the MCP create/update calls
|
||||
2. The DESIGN.md you wrote is the input — the manage-design-system skill handles
|
||||
the Stitch API integration
|
||||
|
||||
If the user just wants the document, you're done after Phase 3.
|
||||
|
||||
---
|
||||
|
||||
## Quality Checklist
|
||||
|
||||
Before delivering the DESIGN.md, verify:
|
||||
|
||||
- [ ] Every color has a descriptive name, hex code, and functional role
|
||||
- [ ] Typography includes font family, character description, and full hierarchy
|
||||
- [ ] Component styles describe shape, color, states, and transitions
|
||||
- [ ] Layout includes max-width, grid, breakpoints, and spacing strategy
|
||||
- [ ] Stitch generation notes use natural language, not CSS syntax
|
||||
- [ ] The atmosphere section reads like editorial copy, not technical docs
|
||||
- [ ] Near-duplicate colors are consolidated
|
||||
- [ ] The document captures the *intent* behind styling, not just raw values
|
||||
|
||||
## Tips for Better Extraction
|
||||
|
||||
- **Read comments and commit messages.** Developers often document design
|
||||
intent in code comments (`/* hero section — breathable */`) and commit
|
||||
messages. These are gold for understanding the *why*.
|
||||
- **Check for design-token libraries.** If the project uses `style-dictionary`,
|
||||
`@tokens-studio`, or similar, these files are the most authoritative
|
||||
source of design values.
|
||||
- **Theme files are higher-signal than component styles.** A `theme.ts` that
|
||||
defines a palette tells you the intended design system; scattered inline
|
||||
styles in components tell you what actually shipped. Both matter, but
|
||||
start from the theme.
|
||||
- **Tailwind config is a design system.** If a project has a customized
|
||||
`tailwind.config.js`, that *is* the design system — extract from it first,
|
||||
then spot-check components for overrides.
|
||||
- **CSS custom properties are intentional.** If a developer defined
|
||||
`--brand-primary`, they're telling you this is a design token. Respect that.
|
||||
@@ -0,0 +1,210 @@
|
||||
---
|
||||
name: Alpine Peak
|
||||
colors:
|
||||
surface: '#fcf8fa'
|
||||
surface-dim: '#dcd9db'
|
||||
surface-bright: '#fcf8fa'
|
||||
surface-container-lowest: '#ffffff'
|
||||
surface-container-low: '#f6f3f5'
|
||||
surface-container: '#f0edef'
|
||||
surface-container-high: '#eae7e9'
|
||||
surface-container-highest: '#e4e2e4'
|
||||
on-surface: '#1b1b1d'
|
||||
on-surface-variant: '#45464d'
|
||||
inverse-surface: '#303032'
|
||||
inverse-on-surface: '#f3f0f2'
|
||||
outline: '#76777d'
|
||||
outline-variant: '#c6c6cd'
|
||||
surface-tint: '#565e74'
|
||||
primary: '#000000'
|
||||
on-primary: '#ffffff'
|
||||
primary-container: '#131b2e'
|
||||
on-primary-container: '#7c839b'
|
||||
inverse-primary: '#bec6e0'
|
||||
secondary: '#5c5f61'
|
||||
on-secondary: '#ffffff'
|
||||
secondary-container: '#e0e3e5'
|
||||
on-secondary-container: '#626567'
|
||||
tertiary: '#000000'
|
||||
on-tertiary: '#ffffff'
|
||||
tertiary-container: '#161c22'
|
||||
on-tertiary-container: '#7e848c'
|
||||
error: '#ba1a1a'
|
||||
on-error: '#ffffff'
|
||||
error-container: '#ffdad6'
|
||||
on-error-container: '#93000a'
|
||||
primary-fixed: '#dae2fd'
|
||||
primary-fixed-dim: '#bec6e0'
|
||||
on-primary-fixed: '#131b2e'
|
||||
on-primary-fixed-variant: '#3f465c'
|
||||
secondary-fixed: '#e0e3e5'
|
||||
secondary-fixed-dim: '#c4c7c9'
|
||||
on-secondary-fixed: '#191c1e'
|
||||
on-secondary-fixed-variant: '#444749'
|
||||
tertiary-fixed: '#dde3eb'
|
||||
tertiary-fixed-dim: '#c1c7cf'
|
||||
on-tertiary-fixed: '#161c22'
|
||||
on-tertiary-fixed-variant: '#41474e'
|
||||
background: '#fcf8fa'
|
||||
on-background: '#1b1b1d'
|
||||
surface-variant: '#e4e2e4'
|
||||
typography:
|
||||
display-lg:
|
||||
fontFamily: Inter
|
||||
fontSize: 48px
|
||||
fontWeight: '800'
|
||||
lineHeight: 56px
|
||||
letterSpacing: -0.02em
|
||||
headline-md:
|
||||
fontFamily: Inter
|
||||
fontSize: 24px
|
||||
fontWeight: '700'
|
||||
lineHeight: 32px
|
||||
letterSpacing: -0.01em
|
||||
body-base:
|
||||
fontFamily: Inter
|
||||
fontSize: 16px
|
||||
fontWeight: '400'
|
||||
lineHeight: 24px
|
||||
letterSpacing: '0'
|
||||
body-bold:
|
||||
fontFamily: Inter
|
||||
fontSize: 16px
|
||||
fontWeight: '600'
|
||||
lineHeight: 24px
|
||||
letterSpacing: '0'
|
||||
label-caps:
|
||||
fontFamily: Lexend
|
||||
fontSize: 12px
|
||||
fontWeight: '700'
|
||||
lineHeight: 16px
|
||||
letterSpacing: 0.05em
|
||||
stat-lg:
|
||||
fontFamily: Lexend
|
||||
fontSize: 32px
|
||||
fontWeight: '600'
|
||||
lineHeight: 40px
|
||||
letterSpacing: -0.02em
|
||||
rounded:
|
||||
sm: 0.25rem
|
||||
DEFAULT: 0.5rem
|
||||
md: 0.75rem
|
||||
lg: 1rem
|
||||
xl: 1.5rem
|
||||
full: 9999px
|
||||
spacing:
|
||||
unit: 4px
|
||||
xs: 4px
|
||||
sm: 8px
|
||||
md: 16px
|
||||
lg: 24px
|
||||
xl: 32px
|
||||
gutter: 16px
|
||||
margin-mobile: 20px
|
||||
margin-desktop: 40px
|
||||
---
|
||||
|
||||
## Brand & Style
|
||||
|
||||
The brand personality is high-performance, sophisticated, and dependable. It is
|
||||
designed for the modern skier who demands clarity and precision in harsh
|
||||
environments. The visual language evokes the crystalline beauty of a mountain
|
||||
peak while prioritizing the rugged utility required for outdoor navigation.
|
||||
|
||||
The design system utilizes **Glassmorphism** to achieve a "frozen" aesthetic,
|
||||
mimicking the properties of ice and packed snow. This is paired with a
|
||||
**High-Contrast** philosophy to ensure that critical data—like trail status and
|
||||
weather alerts—remains legible under intense mountain sunlight or the
|
||||
low-visibility conditions of a snowstorm.
|
||||
|
||||
## Colors
|
||||
|
||||
The palette is anchored by **Deep Peak Blue**, a dark navy that provides a
|
||||
grounding contrast for white text and maps. **Powder White** serves as the
|
||||
primary canvas, ensuring the interface feels airy and cold.
|
||||
|
||||
**Safety Orange** is reserved strictly for primary calls to action, hazard
|
||||
warnings, and emergency trail closures, ensuring immediate eye-tracking.
|
||||
**Electric Blue** (the info status) is used for weather updates and general
|
||||
interactivity. For trail difficulty levels, standard industry colors (Green,
|
||||
Blue, Black) are used but rendered with high-saturation values to pop against
|
||||
the white backgrounds.
|
||||
|
||||
## Typography
|
||||
|
||||
The typography system uses **Inter** for core functional text and body copy due
|
||||
to its exceptional x-height and legibility at small sizes. **Lexend** is
|
||||
introduced for labels and statistics; its hyper-readable, athletic character
|
||||
suits the "active" nature of skiing data like speed and vertical drop.
|
||||
|
||||
To combat outdoor glare, font weights are generally heavier than standard web
|
||||
applications. Display styles use tight tracking and heavy weights to create a
|
||||
sense of strength. Labels always utilize high-contrast coloring against their
|
||||
backgrounds.
|
||||
|
||||
## Layout & Spacing
|
||||
|
||||
This design system uses a **Fluid Grid** model with generous safe areas.
|
||||
Elements are spaced using a strict 4px/8px baseline rhythm to maintain a
|
||||
disciplined, professional appearance.
|
||||
|
||||
Touch targets are intentionally oversized (minimum 48x48px) to accommodate users
|
||||
wearing gloves. Padding within cards and containers is kept spacious (`lg` or
|
||||
`xl`) to prevent the interface from feeling cluttered when displaying complex
|
||||
trail data.
|
||||
|
||||
## Elevation & Depth
|
||||
|
||||
Depth is conveyed through **Glassmorphism** and backdrop filters rather than
|
||||
traditional heavy shadows. Surfaces use a hierarchy of transparency:
|
||||
|
||||
1. **Base Layer:** Solid 'Powder White' or 'Deep Peak Blue'.
|
||||
2. **Middle Layer (Cards/Modals):** 70% opacity white with a 20px backdrop blur
|
||||
and a thin 1px 'Ice' border (white at 40% opacity).
|
||||
3. **Top Layer (Floating Actions):** 90% opacity with a subtle 4px ambient
|
||||
shadow to separate the element from the blurred background.
|
||||
|
||||
This creates a "stacked ice" effect that maintains visual clarity while giving
|
||||
the UI a premium, modern feel.
|
||||
|
||||
## Shapes
|
||||
|
||||
The shape language is defined by modern, organic **rounded corners**. This
|
||||
softens the high-contrast color palette and makes the app feel approachable.
|
||||
|
||||
Interactive elements like buttons and chips utilize `rounded-lg` (1rem) or
|
||||
`rounded-xl` (1.5rem) to suggest a tactile, "pebble" feel. Large containers like
|
||||
map overlays use 1rem corners, while small status indicators for trail
|
||||
difficulty may use pill shapes for instant recognition.
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
|
||||
Primary buttons use a solid 'Safety Orange' or 'Deep Peak Blue' fill with white
|
||||
text. High-contrast outlines are used for secondary actions. All buttons must
|
||||
feature a minimum height of 52px for gloved-hand accessibility.
|
||||
|
||||
### Cards & Modals
|
||||
|
||||
Cards utilize the glassmorphism effect—semi-transparent backgrounds with a 1px
|
||||
white border. This ensures that map imagery or photos underneath remain slightly
|
||||
visible, maintaining the "frozen" aesthetic.
|
||||
|
||||
### Trail Status Chips
|
||||
|
||||
Pill-shaped indicators with high-saturation icons. Green (Easy), Blue
|
||||
(Intermediate), Black (Expert), and Double Black (Extreme). These must include
|
||||
text labels alongside icons to ensure accessibility for colorblind users.
|
||||
|
||||
### Input Fields
|
||||
|
||||
Fields feature a 'Cloud Gray' background with a thick bottom border that
|
||||
transforms into 'Electric Blue' on focus. Labels are always visible above the
|
||||
field to ensure the user doesn't lose context in bright environments.
|
||||
|
||||
### Weather Widgets
|
||||
|
||||
Bold, thick-stroke iconography (minimum 2pt stroke) to represent sun, snow, or
|
||||
wind. These icons should be large and accompanied by 'Lexend' typography for
|
||||
temperature and wind speed.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Angular Extraction Patterns
|
||||
|
||||
Angular projects use a structured, convention-heavy approach to styling.
|
||||
Design systems often live in SCSS/CSS files with clear separation between
|
||||
global themes and component-scoped styles.
|
||||
|
||||
## File Discovery Order
|
||||
|
||||
1. **`angular.json`** — Lists global style files under
|
||||
`projects.*.architect.build.options.styles`. These are the entry-point
|
||||
CSS/SCSS files.
|
||||
|
||||
2. **`src/styles.scss` / `src/styles.css`** — Global stylesheet. CSS custom
|
||||
properties, font imports, and base styles live here.
|
||||
|
||||
3. **`src/theme.scss` / `src/theme/`** — Explicit theme directory. Custom
|
||||
Material/component palettes.
|
||||
|
||||
4. **`tailwind.config.js`** (if Tailwind) — Same extraction as React.
|
||||
|
||||
5. **`src/app/app.component.scss`** — Root component styles, reveals global
|
||||
layout patterns.
|
||||
|
||||
6. **Component `.scss` / `.css` files** — Co-located styles (ViewEncapsulation
|
||||
scoped by default).
|
||||
|
||||
## Angular Material Theme Extraction
|
||||
|
||||
Angular Material is the most common component library. Themes are SCSS-based:
|
||||
|
||||
```scss
|
||||
// src/theme.scss
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
$primary-palette: mat.m2-define-palette(mat.$m2-teal-palette, 800);
|
||||
$accent-palette: mat.m2-define-palette(mat.$m2-blue-grey-palette);
|
||||
$warn-palette: mat.m2-define-palette(mat.$m2-red-palette);
|
||||
|
||||
$theme: mat.m2-define-light-theme((
|
||||
color: (
|
||||
primary: $primary-palette,
|
||||
accent: $accent-palette,
|
||||
warn: $warn-palette,
|
||||
),
|
||||
typography: mat.m2-define-typography-config(
|
||||
$font-family: 'Manrope, sans-serif',
|
||||
$headline-1: mat.m2-define-typography-level(3.5rem, 4rem, 600),
|
||||
$headline-5: mat.m2-define-typography-level(1.5rem, 2rem, 500),
|
||||
$body-1: mat.m2-define-typography-level(1rem, 1.7, 400),
|
||||
),
|
||||
));
|
||||
|
||||
@include mat.all-component-themes($theme);
|
||||
```
|
||||
|
||||
**What to extract:**
|
||||
- Palette choices → map to functional color roles
|
||||
- Typography config → maps directly to the hierarchy section
|
||||
- Light vs dark theme → atmosphere
|
||||
|
||||
For Angular Material 3 (MDC-based), look for `mat.define-theme()` using
|
||||
the new token system with `--mat-*` CSS custom properties.
|
||||
|
||||
## SCSS Variable Patterns
|
||||
|
||||
Many Angular projects use SCSS variables for tokens:
|
||||
|
||||
```scss
|
||||
// _variables.scss
|
||||
$color-primary: #294056;
|
||||
$color-background: #FCFAFA;
|
||||
$color-surface: #F5F5F5;
|
||||
$color-text: #2C2C2C;
|
||||
|
||||
$font-heading: 'Manrope', sans-serif;
|
||||
$font-body: 'Inter', sans-serif;
|
||||
|
||||
$radius-button: 8px;
|
||||
$radius-card: 12px;
|
||||
|
||||
$breakpoint-mobile: 768px;
|
||||
$breakpoint-desktop: 1024px;
|
||||
|
||||
$spacing-section: 5rem;
|
||||
$spacing-component: 2rem;
|
||||
```
|
||||
|
||||
These are explicit design tokens. Map them directly.
|
||||
|
||||
## ViewEncapsulation and Scoped Styles
|
||||
|
||||
Angular scopes styles by default (similar to Vue's `scoped`). When scanning
|
||||
component styles:
|
||||
|
||||
- Look for `:host` selectors — these style the component's root element
|
||||
- `::ng-deep` (deprecated but still used) — styles that pierce encapsulation
|
||||
- Repeated values across components indicate design system conventions
|
||||
|
||||
## PrimeNG / Nebular / NG-ZORRO
|
||||
|
||||
If component libraries are used:
|
||||
- **PrimeNG**: Theme SCSS in `node_modules/primeng/resources/themes/` —
|
||||
look for custom theme or `styles.scss` overrides.
|
||||
- **Nebular**: `nb-theme()` in `styles.scss` with custom theme object.
|
||||
- **NG-ZORRO (Ant Design for Angular)**: `ng-zorro-antd.less` variables
|
||||
or custom theme config in `angular.json`.
|
||||
|
||||
## Responsive Patterns
|
||||
|
||||
Check for:
|
||||
- `@media` queries in `styles.scss` and component styles
|
||||
- Angular CDK `BreakpointObserver` usage in components
|
||||
- Tailwind responsive prefixes if Tailwind is configured
|
||||
- Angular Flex-Layout directives (`fxLayout`, `fxFlex`) in templates
|
||||
@@ -0,0 +1,161 @@
|
||||
# Plain CSS / SASS / Less Extraction Patterns
|
||||
|
||||
For projects without a JavaScript framework — static sites, WordPress
|
||||
themes, vanilla HTML/CSS, or CSS preprocessor-heavy projects.
|
||||
|
||||
## File Discovery Order
|
||||
|
||||
1. **`index.html` / `*.html`** — Check `<link>` tags and `<style>` blocks
|
||||
for stylesheet references, inline styles, and font loading.
|
||||
|
||||
2. **Main stylesheet** (`style.css`, `main.css`, `app.css`) — The primary
|
||||
CSS file. Look for custom properties, base styles, and typography.
|
||||
|
||||
3. **`_variables.scss` / `_tokens.scss` / `variables.less`** — Preprocessor
|
||||
variable files containing design tokens.
|
||||
|
||||
4. **`_mixins.scss`** — Reusable style patterns reveal design conventions.
|
||||
|
||||
5. **Component/module stylesheets** — Individual CSS files for UI components.
|
||||
|
||||
## CSS Custom Properties (Modern CSS)
|
||||
|
||||
Modern vanilla CSS projects often define a design system via custom properties:
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Colors */
|
||||
--color-primary: #294056;
|
||||
--color-bg: #FCFAFA;
|
||||
--color-surface: #F5F5F5;
|
||||
--color-text: #2C2C2C;
|
||||
--color-text-secondary: #6B6B6B;
|
||||
--color-border: #E0E0E0;
|
||||
--color-success: #10B981;
|
||||
--color-error: #EF4444;
|
||||
|
||||
/* Typography */
|
||||
--font-heading: 'Manrope', sans-serif;
|
||||
--font-body: 'Inter', sans-serif;
|
||||
--font-size-base: 1rem;
|
||||
--line-height-body: 1.7;
|
||||
|
||||
/* Spacing */
|
||||
--spacing-xs: 0.5rem;
|
||||
--spacing-sm: 1rem;
|
||||
--spacing-md: 2rem;
|
||||
--spacing-lg: 4rem;
|
||||
--spacing-xl: 6rem;
|
||||
|
||||
/* Shapes */
|
||||
--radius-button: 8px;
|
||||
--radius-card: 12px;
|
||||
|
||||
/* Shadows */
|
||||
--shadow-card: 0 2px 8px rgba(0,0,0,0.06);
|
||||
}
|
||||
```
|
||||
|
||||
This is the cleanest source of truth. Extract directly and name each token.
|
||||
|
||||
## SASS/SCSS Token Patterns
|
||||
|
||||
```scss
|
||||
// _variables.scss
|
||||
$colors: (
|
||||
'primary': #294056,
|
||||
'background': #FCFAFA,
|
||||
'surface': #F5F5F5,
|
||||
'text': #2C2C2C,
|
||||
'text-muted': #6B6B6B,
|
||||
);
|
||||
|
||||
$font-stack-heading: 'Manrope', sans-serif;
|
||||
$font-stack-body: 'Inter', sans-serif;
|
||||
|
||||
$breakpoints: (
|
||||
'mobile': 768px,
|
||||
'tablet': 1024px,
|
||||
'desktop': 1280px,
|
||||
);
|
||||
|
||||
$spacers: (
|
||||
'section': 5rem,
|
||||
'component': 2rem,
|
||||
'element': 1rem,
|
||||
);
|
||||
```
|
||||
|
||||
SASS maps are essentially design token dictionaries. Extract all values.
|
||||
|
||||
## Less Variable Patterns
|
||||
|
||||
```less
|
||||
@primary-color: #294056;
|
||||
@bg-color: #FCFAFA;
|
||||
@text-color: #2C2C2C;
|
||||
@font-heading: 'Manrope', sans-serif;
|
||||
@border-radius-base: 8px;
|
||||
```
|
||||
|
||||
Same extraction approach — map each variable to a descriptive name and role.
|
||||
|
||||
## Static Sites and WordPress
|
||||
|
||||
For static sites or WordPress themes:
|
||||
|
||||
- **WordPress**: Check `style.css` header comment for theme metadata.
|
||||
Look for `wp-content/themes/<name>/assets/css/` for stylesheets.
|
||||
`functions.php` may enqueue Google Fonts.
|
||||
- **Jekyll/Hugo**: Check `_sass/` or `assets/css/` directories.
|
||||
- **Static HTML**: Everything is in the CSS files and `<style>` blocks.
|
||||
|
||||
## Inline Style Scanning
|
||||
|
||||
For projects heavy on inline styles (legacy codebases, email templates):
|
||||
|
||||
Search HTML files for `style="..."` attributes. Group unique values by
|
||||
property type:
|
||||
|
||||
```
|
||||
background-color: #FCFAFA, #F5F5F5, #294056
|
||||
color: #2C2C2C, #6B6B6B, white
|
||||
border-radius: 8px, 12px
|
||||
font-family: 'Manrope', 'Inter'
|
||||
```
|
||||
|
||||
Then deduplicate and assign roles.
|
||||
|
||||
## Color Extraction Strategy
|
||||
|
||||
When there's no explicit token system, you need to discover colors across
|
||||
all stylesheets. Search for:
|
||||
|
||||
```
|
||||
background-color:
|
||||
background:
|
||||
color:
|
||||
border-color:
|
||||
border:
|
||||
outline-color:
|
||||
box-shadow:
|
||||
fill:
|
||||
stroke:
|
||||
```
|
||||
|
||||
Collect all unique hex values, `rgb()`, `rgba()`, and `hsl()` values.
|
||||
Group by proximity (similar colors within a few shades) and assign roles
|
||||
based on context (which selectors use them).
|
||||
|
||||
## Responsive Patterns
|
||||
|
||||
Look for `@media` queries in all stylesheets. Common patterns:
|
||||
|
||||
```css
|
||||
@media (max-width: 768px) { ... } /* Mobile-first breakpoint */
|
||||
@media (min-width: 1024px) { ... } /* Desktop enhancement */
|
||||
@media (prefers-color-scheme: dark) { ... } /* Dark mode support */
|
||||
```
|
||||
|
||||
Document all breakpoints and the content strategy at each (column
|
||||
changes, padding adjustments, navigation transformations).
|
||||
@@ -0,0 +1,157 @@
|
||||
# React / Next.js / Tailwind Extraction Patterns
|
||||
|
||||
This reference covers the most common frontend stack: React components with
|
||||
Tailwind CSS (and optionally CSS Modules, styled-components, or Emotion).
|
||||
|
||||
## File Discovery Order
|
||||
|
||||
Read these files in priority order — higher-priority files give you the
|
||||
intended design system, lower-priority files show what actually shipped:
|
||||
|
||||
1. **`tailwind.config.js` / `tailwind.config.ts`** — The single most
|
||||
important file. Custom `theme.extend.colors`, `fontFamily`, `spacing`,
|
||||
`borderRadius`, and `screens` are the design system definition.
|
||||
|
||||
2. **`globals.css` / `global.css` / `index.css`** — CSS custom properties
|
||||
(`--*`), `@layer` directives, `@font-face` declarations, and base styles.
|
||||
|
||||
3. **`theme.ts` / `theme.js` / `tokens.ts`** — Explicit design token files.
|
||||
May export objects consumed by Tailwind config or CSS-in-JS providers.
|
||||
|
||||
4. **`src/app/layout.tsx` or `src/App.tsx`** — Root layout. Shows the global
|
||||
font setup (via `next/font` or `<link>`), body background, and overall
|
||||
structure.
|
||||
|
||||
5. **Component files (`*.tsx` / `*.jsx`)** — Look at 5-8 representative
|
||||
components to understand usage patterns.
|
||||
|
||||
## Tailwind Config Extraction
|
||||
|
||||
The Tailwind config is structured and machine-readable. Extract directly:
|
||||
|
||||
```js
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#294056', // → "Deep Muted Teal-Navy" — Primary CTA
|
||||
background: '#FCFAFA', // → "Warm Barely-There Cream" — Page BG
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Manrope', 'sans-serif'],
|
||||
},
|
||||
borderRadius: {
|
||||
card: '12px',
|
||||
button: '8px',
|
||||
},
|
||||
spacing: {
|
||||
section: '5rem',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Map each custom value to a descriptive name and role.
|
||||
|
||||
## CSS Custom Properties
|
||||
|
||||
Look for `:root` or `html` blocks in global CSS:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-primary: #294056;
|
||||
--color-bg: #FCFAFA;
|
||||
--font-heading: 'Manrope', sans-serif;
|
||||
--radius-card: 12px;
|
||||
--spacing-section: 5rem;
|
||||
}
|
||||
```
|
||||
|
||||
These are explicitly declared tokens — use their names as clues for
|
||||
their intended role.
|
||||
|
||||
## Next.js Font Patterns
|
||||
|
||||
Next.js uses `next/font` for optimized font loading:
|
||||
|
||||
```tsx
|
||||
import { Inter, Playfair_Display } from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
|
||||
const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-display' })
|
||||
```
|
||||
|
||||
The `variable` names hint at usage: `--font-display` for headlines,
|
||||
`--font-inter` for body.
|
||||
|
||||
## Component Scanning Strategy
|
||||
|
||||
Don't read every component. Focus on these archetypes:
|
||||
|
||||
| Component Type | What to Extract |
|
||||
|:---|:---|
|
||||
| Layout / Shell | Max-width, padding, grid structure |
|
||||
| Button / CTA | Border radius, colors, hover states, padding |
|
||||
| Card | Shadow, border, radius, internal spacing |
|
||||
| Nav / Header | Typography treatment, active states |
|
||||
| Form / Input | Border, focus state, padding |
|
||||
| Hero / Landing section | Spacing, typography scale, alignment |
|
||||
|
||||
For each, look at the `className` prop for Tailwind classes or the
|
||||
`styled()` / `css()` calls for CSS-in-JS values.
|
||||
|
||||
## CSS-in-JS Patterns (styled-components / Emotion)
|
||||
|
||||
If the project uses CSS-in-JS, look for theme providers:
|
||||
|
||||
```tsx
|
||||
// ThemeProvider wrapping the app
|
||||
const theme = {
|
||||
colors: {
|
||||
primary: '#294056',
|
||||
background: '#FCFAFA',
|
||||
},
|
||||
fonts: {
|
||||
heading: 'Manrope, sans-serif',
|
||||
},
|
||||
radii: {
|
||||
card: '12px',
|
||||
button: '8px',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This theme object *is* the design system. Extract directly.
|
||||
|
||||
## Component Library Integration
|
||||
|
||||
If the project uses Chakra UI, Material UI, Ant Design, or shadcn/ui:
|
||||
|
||||
- **Chakra**: Look for `extendTheme()` calls — these override defaults.
|
||||
- **MUI**: Look for `createTheme()` — palette, typography, and spacing overrides.
|
||||
- **Ant Design**: Look for `ConfigProvider` theme prop or `theme.ts` overrides.
|
||||
- **shadcn/ui**: Colors are defined as CSS custom properties in `globals.css`.
|
||||
Check `components.json` for the style configuration.
|
||||
|
||||
The overrides are the design system — default values are the library's generic
|
||||
styling and should be noted but not emphasized.
|
||||
|
||||
## Responsive Patterns
|
||||
|
||||
Check Tailwind's `screens` config and look for responsive class prefixes
|
||||
(`sm:`, `md:`, `lg:`, `xl:`) in components:
|
||||
|
||||
```js
|
||||
screens: {
|
||||
sm: '640px', // Mobile landscape
|
||||
md: '768px', // Tablet
|
||||
lg: '1024px', // Desktop
|
||||
xl: '1280px', // Large desktop
|
||||
'2xl': '1536px'
|
||||
}
|
||||
```
|
||||
|
||||
Look for `container` configuration and `max-width` patterns on layout
|
||||
components to determine content width strategy.
|
||||
@@ -0,0 +1,101 @@
|
||||
# Svelte / SvelteKit Extraction Patterns
|
||||
|
||||
Svelte co-locates styles even more tightly than Vue. Every `.svelte` file
|
||||
has a `<style>` block that is scoped by default. Design systems in Svelte
|
||||
projects typically live in global CSS, CSS custom properties, or a shared
|
||||
theme store.
|
||||
|
||||
## File Discovery Order
|
||||
|
||||
1. **`src/app.css` / `src/app.postcss`** — Global styles and CSS custom
|
||||
properties. The most important file.
|
||||
|
||||
2. **`svelte.config.js`** — May reference CSS preprocessors, Tailwind, or
|
||||
UnoCSS configuration.
|
||||
|
||||
3. **`tailwind.config.js`** (if Tailwind/UnoCSS) — Same extraction as React.
|
||||
|
||||
4. **`src/lib/theme.ts` / `src/lib/tokens.ts`** — Shared design tokens
|
||||
exported as JS objects.
|
||||
|
||||
5. **`src/routes/+layout.svelte`** — Root layout. Shows global font loading,
|
||||
background, and structural patterns.
|
||||
|
||||
6. **Component `<style>` blocks** — Scoped styles revealing component patterns.
|
||||
|
||||
## Svelte Component Style Patterns
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
export let variant = 'primary'
|
||||
</script>
|
||||
|
||||
<button class="btn btn-{variant}">
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
padding: 0.875rem 2rem;
|
||||
font-weight: 500;
|
||||
transition: all 250ms ease-in-out;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Extraction points:**
|
||||
- Component props (like `variant`) reveal the intended variant system
|
||||
- `var(--*)` references → trace to `app.css`
|
||||
- Transition values reveal the interaction design philosophy
|
||||
|
||||
## SvelteKit Layout Patterns
|
||||
|
||||
- **`+layout.svelte`** at route root — Global header, footer, font loading
|
||||
- **`+layout.ts/js`** — May load theme data or tokens
|
||||
- **`$lib/`** directory — Reusable components and shared utilities
|
||||
|
||||
## CSS Custom Properties Strategy
|
||||
|
||||
Svelte projects heavily use CSS custom properties for theming:
|
||||
|
||||
```css
|
||||
/* app.css */
|
||||
:root {
|
||||
--color-primary: #294056;
|
||||
--color-bg: #FCFAFA;
|
||||
--color-surface: #F5F5F5;
|
||||
--color-text: #2C2C2C;
|
||||
--color-text-muted: #6B6B6B;
|
||||
|
||||
--font-heading: 'Manrope', sans-serif;
|
||||
--font-body: 'Inter', sans-serif;
|
||||
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-full: 9999px;
|
||||
|
||||
--shadow-hover: 0 2px 8px rgba(0,0,0,0.06);
|
||||
--spacing-section: 5rem;
|
||||
}
|
||||
```
|
||||
|
||||
These variable names are highly intentional. Use them as the foundation
|
||||
of your design system extraction.
|
||||
|
||||
## Skeleton UI / DaisyUI / Flowbite-Svelte
|
||||
|
||||
If component libraries are used:
|
||||
- **Skeleton UI**: Theme defined in `tailwind.config.js` using Skeleton's
|
||||
design token system. Look for custom theme config object.
|
||||
- **DaisyUI**: Theme in `tailwind.config.js` → `daisyui.themes` array.
|
||||
- **Flowbite-Svelte**: Standard Tailwind theming.
|
||||
@@ -0,0 +1,107 @@
|
||||
# Vue / Nuxt Extraction Patterns
|
||||
|
||||
Vue projects have a distinctive styling architecture. Styles are often
|
||||
co-located with components inside `<style>` blocks, and Nuxt adds
|
||||
convention-based directories.
|
||||
|
||||
## File Discovery Order
|
||||
|
||||
1. **`nuxt.config.ts` / `nuxt.config.js`** — May contain global CSS paths,
|
||||
font configuration, and Tailwind/UnoCSS module config.
|
||||
|
||||
2. **`assets/css/main.css`** (or similar) — Global styles, CSS custom
|
||||
properties, and font imports.
|
||||
|
||||
3. **`tailwind.config.js`** (if Tailwind is used) — Same as React; extract
|
||||
custom theme values directly.
|
||||
|
||||
4. **`plugins/vuetify.ts`** (if Vuetify) — Theme definition with custom
|
||||
palette and typography.
|
||||
|
||||
5. **Component `<style>` blocks** — Co-located styles (scoped or global).
|
||||
|
||||
## Single-File Component (SFC) Patterns
|
||||
|
||||
Vue components bundle template, script, and style together:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="card">
|
||||
<h2 class="card__title">{{ title }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
background: var(--color-surface);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.card__title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Key extraction points:**
|
||||
- `var(--*)` references → trace back to global CSS for the actual values
|
||||
- `scoped` styles → component-specific, but reveal consistent patterns
|
||||
- BEM naming (`.card__title`) → hints at component hierarchy
|
||||
|
||||
## Vuetify Theme Extraction
|
||||
|
||||
Vuetify projects define their design system explicitly:
|
||||
|
||||
```ts
|
||||
// plugins/vuetify.ts
|
||||
export default createVuetify({
|
||||
theme: {
|
||||
defaultTheme: 'light',
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
primary: '#294056',
|
||||
secondary: '#6B6B6B',
|
||||
background: '#FCFAFA',
|
||||
surface: '#F5F5F5',
|
||||
error: '#EF4444',
|
||||
success: '#10B981',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
This is the design system declaration. Map each key to a functional role
|
||||
and descriptive name.
|
||||
|
||||
## Quasar / PrimeVue / Element Plus
|
||||
|
||||
These component libraries use their own theming systems:
|
||||
|
||||
- **Quasar**: `quasar.config.js` → `framework.config.brand` for colors
|
||||
- **PrimeVue**: CSS themes in `assets/` or theme preset configuration
|
||||
- **Element Plus**: SCSS variables in `element-variables.scss`
|
||||
|
||||
Look for the override file — that's where the project's unique values live.
|
||||
|
||||
## CSS Scoping Behavior
|
||||
|
||||
Vue's `scoped` attribute adds data attributes for CSS isolation. When
|
||||
scanning for patterns, look at multiple components to find repeated values
|
||||
(same `border-radius`, similar `padding`, consistent color references).
|
||||
Repeated patterns across scoped styles = design system conventions.
|
||||
|
||||
## Nuxt-Specific Patterns
|
||||
|
||||
- **`app.vue`** or **`layouts/default.vue`** — Root layout, reveals
|
||||
global background, font loading, and overall structure.
|
||||
- **`assets/`** — Global CSS, fonts, and images.
|
||||
- **`composables/`** — May contain `useTheme` or `useDesignTokens`.
|
||||
- **`nuxt.config.ts`** `css` array — Lists global stylesheets automatically
|
||||
injected into every page.
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
name: stitch::extract-static-html
|
||||
description: >-
|
||||
Extract self-contained static HTML from a built web application or React components by inlining CSS and images. Use this skill whenever you need to capture a specific UI state, share a static version of a page, or prepare assets for Stitch upload, even if the user just asks to 'save the HTML' or 'mock the view'.
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Extract Static HTML
|
||||
|
||||
Extract a self-contained static HTML file from any web application.
|
||||
|
||||
## Which Strategy to Use
|
||||
|
||||
You MUST ask the user to choose which strategy to use before proceeding. Present the options clearly, **recommend Strategy A** as the preferred default, and **provide a brief pros/cons summary** for each option to help them make an informed decision.
|
||||
|
||||
| | Strategy A (Puppeteer) | Strategy B (Browser Subagent) |
|
||||
| :--- | :--- | :--- |
|
||||
| **When** | App runs locally, no auth wall | Need to interact with page first (click, fill forms) |
|
||||
| **Fidelity** | **Highest — computed styles resolved** | High — rendered DOM |
|
||||
| **Setup** | **Zero — no mock needed** | Zero — no mock needed |
|
||||
| **Framework** | **Any** | Any |
|
||||
| **Output** | **Writes to file — no size limit** | May truncate in agent context |
|
||||
|
||||
> [!WARNING]
|
||||
> **Checkpoint — User Confirmation Required.**
|
||||
> You **MUST** ask the user which strategy they prefer before proceeding.
|
||||
> Present the comparison table above, recommend Strategy A as the default, and
|
||||
> wait for explicit approval. Do **NOT** make the decision yourself or proceed
|
||||
> until the user confirms.
|
||||
|
||||
***
|
||||
|
||||
## Strategy A: Puppeteer Snapshot (Recommended)
|
||||
|
||||
Launches headless Chrome, captures the fully rendered DOM, and produces a self-contained HTML file with all CSS inlined and images as base64. Works with **any framework** — no MockPage.jsx needed.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- App running locally (e.g., `npm run dev`)
|
||||
- Node.js with `puppeteer` available (check: `node -e "require('puppeteer')"`)
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Start the App** and note the port.
|
||||
|
||||
> [!WARNING]
|
||||
> **Checkpoint — User Confirmation Required.**
|
||||
> After starting the local server, you **MUST** pause and ask the user for
|
||||
> confirmation before running the snapshot script or launching a browser
|
||||
> subagent. Report the URL and port to the user so they can verify the app
|
||||
> is running and rendering correctly. Do **NOT** proceed to the snapshot
|
||||
> step until the user confirms.
|
||||
|
||||
2. **Run the Snapshot Script**:
|
||||
```bash
|
||||
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
|
||||
--url http://localhost:5173 \
|
||||
--output .stitch/home.html \
|
||||
--wait 2000
|
||||
```
|
||||
|
||||
3. **Multiple pages** — run once per route:
|
||||
```bash
|
||||
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
|
||||
--url http://localhost:5173 --output .stitch/home.html --wait 2000
|
||||
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
|
||||
--url http://localhost:5173/pricing --output .stitch/pricing.html --wait 2000
|
||||
npx tsx <SKILL_DIR>/scripts/snapshot.ts \
|
||||
--url http://localhost:5173/dashboard --output .stitch/dashboard.html --wait 2000 --html-class dark
|
||||
```
|
||||
|
||||
### Script Flags
|
||||
|
||||
| Flag | Default | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `--url` | *(required)* | URL to capture |
|
||||
| `--output` | *(required)* | Output file path |
|
||||
| `--wait` | `1000` | Extra wait (ms) after network idle. Increase for lazy-loading apps. |
|
||||
| `--viewport` | `1280x800` | Viewport size as `WIDTHxHEIGHT` |
|
||||
| `--html-class` | — | Class(es) for `<html>` element (e.g., `dark`) |
|
||||
| `--remove-fixed` | `false` | Remove fixed/sticky elements (cookie banners, chat widgets) |
|
||||
| `--full-height` | `false` | Resize viewport to full scroll height |
|
||||
| `--title` | — | Override page title |
|
||||
|
||||
### What It Does Automatically
|
||||
|
||||
- Inlines all `<link rel="stylesheet">` → `<style>` blocks
|
||||
- Converts `<img>` `src` **and `srcset`** → base64 data URIs (skips fonts)
|
||||
- Inlines `<source srcset>` URLs as base64
|
||||
- Removes failed/dead `srcset` entries so the browser falls back to the inlined `src`
|
||||
- Removes `<script>` tags, Vite overlay, Next.js dev indicators
|
||||
- Resolves relative CSS `url()` paths before inlining
|
||||
|
||||
### Framework Notes
|
||||
|
||||
| Framework | Notes |
|
||||
| :--- | :--- |
|
||||
| **React + Vite** | Works out of the box. `--wait 1000`. |
|
||||
| **Next.js** | `--wait 3000` for SSR hydration. URL: `http://localhost:3000`. `<img srcset>` from `/_next/image` is auto-inlined as base64. |
|
||||
| **Vue / Nuxt** | Works out of the box. |
|
||||
| **Svelte / SvelteKit** | Works out of the box. |
|
||||
| **Storybook** | Use story URL: `--url http://localhost:6006/?path=/story/...` |
|
||||
| **SSR (Webpack)** | May need longer `--wait`. |
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| :--- | :--- |
|
||||
| Images missing | Increase `--wait` |
|
||||
| Images show as broken after server stops | Verify `srcset` was inlined — check log for "Inlined N images". If `srcset` URLs failed, they are auto-removed so `src` (inlined) is used. |
|
||||
| Next.js `/_next/image` not inlined | Ensure the dev server is running when snapshot runs — the script fetches optimized images from the running server. |
|
||||
| Dark mode not applied | `--html-class dark` |
|
||||
| Cookie banner in output | `--remove-fixed` |
|
||||
| Page requires login | Use the Static Fallback (appendix below) |
|
||||
| `Cannot find module 'puppeteer'` | `npm install -g puppeteer` |
|
||||
|
||||
***
|
||||
|
||||
## Strategy B: Browser Subagent Capture
|
||||
|
||||
Use when you need to **interact with the page** (click buttons, fill forms, navigate tabs) before capturing. The browser subagent gives you full control but output may truncate for large pages.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Start the App** locally.
|
||||
2. **Navigate** using a browser subagent.
|
||||
3. **Interact** as needed (click, scroll, fill forms).
|
||||
4. **Extract DOM**: `document.documentElement.outerHTML`
|
||||
|
||||
> [!WARNING]
|
||||
> Large pages may truncate. To handle this:
|
||||
> - Remove `<style>` tags before extraction: `document.querySelectorAll('style').forEach(el => el.remove())`
|
||||
> - Re-add styles statically (Tailwind CDN link, source CSS)
|
||||
5. **Save** to file.
|
||||
|
||||
***
|
||||
|
||||
## Appendix: Static Fallback (MockPage.jsx)
|
||||
|
||||
> [!NOTE]
|
||||
> This method is a **last resort** for when the app cannot run locally (broken deps, missing backend, auth walls with no bypass). It requires manually flattening React components into a single JSX file. **Prefer Strategy A whenever possible.**
|
||||
|
||||
### When to Use
|
||||
|
||||
- App can't run locally at all
|
||||
- Page requires auth with no mock/bypass
|
||||
- You need a specific UI state that's impossible to reach by navigation (error screens, empty states)
|
||||
|
||||
### Quick Reference
|
||||
|
||||
```bash
|
||||
npx tsx <SKILL_DIR>/scripts/extract_inline_html.ts \
|
||||
--index-css src/css/App.css \
|
||||
--extra-css index.html \
|
||||
--outdir .stitch \
|
||||
--page src/MockPage.jsx:Page.html:"Page Title"
|
||||
```
|
||||
|
||||
**Key flags**: `--no-tailwind` (non-Tailwind apps), `--html-class dark` (dark mode), `--css-files` (extra CSS files).
|
||||
|
||||
**Auto-detection**: Tailwind config is auto-detected. `@apply` directives automatically use `<style type="text/tailwindcss">`.
|
||||
|
||||
### MockPage.jsx Rules
|
||||
|
||||
1. **Include the full layout** — header, sidebar, footer (read `App.js` first)
|
||||
2. **Flatten all conditionals** — pick one state, remove all ternaries and `&&` guards
|
||||
3. **Hardcode all data** — replace `{variable}` with concrete values, unroll `.map()` loops
|
||||
4. **Preserve logos** — use `<img>` with local paths (post-process will inline them)
|
||||
5. **Remove floating elements** — cookie banners, chat widgets, feedback buttons
|
||||
|
||||
### Post-Processing
|
||||
|
||||
Inline local images:
|
||||
```bash
|
||||
npx tsx <SKILL_DIR>/scripts/post_process.ts \
|
||||
.stitch/Page.html --base-dir <app-directory>
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,498 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* post_process.ts — Inline local images as base64 in extracted HTML files.
|
||||
*
|
||||
* Scans HTML files for local image references (src attributes and CSS url()
|
||||
* values) and replaces them with inline base64 data URIs. Uses a robust
|
||||
* character-by-character CSS url() parser instead of regex.
|
||||
*
|
||||
* Usage:
|
||||
* npx tsx post_process.ts .stitch/home.html --base-dir my-app
|
||||
* npx tsx post_process.ts .stitch/page1.html .stitch/page2.html --base-dir .
|
||||
* npx tsx post_process.ts .stitch/*.html --base-dir . --json
|
||||
*
|
||||
* Flags:
|
||||
* --base-dir Base directory for resolving relative paths
|
||||
* --json Output machine-readable JSON stats
|
||||
* --dry-run Report what would be inlined without modifying files
|
||||
* --max-size Max file size to inline in bytes (default: 5242880 / 5MB)
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MIME type mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
const MIME_MAP: Record<string, string> = {
|
||||
'.svg': 'image/svg+xml',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.png': 'image/png',
|
||||
'.gif': 'image/gif',
|
||||
'.webp': 'image/webp',
|
||||
'.ico': 'image/x-icon',
|
||||
'.bmp': 'image/bmp',
|
||||
'.avif': 'image/avif',
|
||||
'.tiff': 'image/tiff',
|
||||
'.tif': 'image/tiff',
|
||||
'.apng': 'image/apng',
|
||||
'.cur': 'image/x-icon',
|
||||
};
|
||||
|
||||
function getMime(filePath: string): string {
|
||||
return MIME_MAP[path.extname(filePath).toLowerCase()] || 'application/octet-stream';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
interface Opts {
|
||||
files: string[];
|
||||
baseDir: string;
|
||||
json: boolean;
|
||||
dryRun: boolean;
|
||||
maxSize: number;
|
||||
}
|
||||
|
||||
interface CssUrlRef {
|
||||
url: string;
|
||||
fullMatch: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
interface InlineStats {
|
||||
srcInlined: number;
|
||||
urlInlined: number;
|
||||
skippedTooLarge: Array<{ path: string; size: number }>;
|
||||
skippedNotFound: string[];
|
||||
}
|
||||
|
||||
interface FileStats {
|
||||
file: string;
|
||||
srcInlined: number;
|
||||
urlInlined: number;
|
||||
skippedNotFound: number;
|
||||
skippedTooLarge: number;
|
||||
sizeBytes: number;
|
||||
}
|
||||
|
||||
interface AllStats {
|
||||
files: FileStats[];
|
||||
totalSrcInlined: number;
|
||||
totalUrlInlined: number;
|
||||
totalSkippedNotFound: number;
|
||||
totalSkippedTooLarge: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Argument parsing & validation
|
||||
// ---------------------------------------------------------------------------
|
||||
function parseArgs(): Opts {
|
||||
const args = process.argv.slice(2);
|
||||
const opts: Opts = {
|
||||
files: [],
|
||||
baseDir: '',
|
||||
json: false,
|
||||
dryRun: false,
|
||||
maxSize: 5 * 1024 * 1024, // 5MB
|
||||
};
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case '--base-dir':
|
||||
opts.baseDir = args[++i];
|
||||
break;
|
||||
case '--json':
|
||||
opts.json = true;
|
||||
break;
|
||||
case '--dry-run':
|
||||
opts.dryRun = true;
|
||||
break;
|
||||
case '--max-size':
|
||||
opts.maxSize = parseInt(args[++i], 10);
|
||||
break;
|
||||
case '--help':
|
||||
console.log(`
|
||||
Usage: npx tsx post_process.ts <html_file> [...] [options]
|
||||
|
||||
Options:
|
||||
--base-dir Base directory for resolving relative paths
|
||||
--json Output machine-readable JSON stats
|
||||
--dry-run Report what would be inlined without modifying files
|
||||
--max-size Max file size to inline in bytes (default: 5242880 / 5MB)
|
||||
`);
|
||||
process.exit(0);
|
||||
default:
|
||||
opts.files.push(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
function validateOpts(opts: Opts): void {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (opts.files.length === 0) {
|
||||
errors.push('No HTML files specified');
|
||||
}
|
||||
|
||||
if (opts.baseDir && !fs.existsSync(opts.baseDir)) {
|
||||
errors.push(`Base directory not found: ${opts.baseDir}`);
|
||||
}
|
||||
|
||||
if (isNaN(opts.maxSize) || opts.maxSize < 1) {
|
||||
errors.push('--max-size must be a positive integer');
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error('❌ Validation errors:');
|
||||
errors.forEach((e) => console.error(` • ${e}`));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Robust CSS url() parser — character-by-character (no regex)
|
||||
// ---------------------------------------------------------------------------
|
||||
function extractCssUrls(text: string): CssUrlRef[] {
|
||||
const results: CssUrlRef[] = [];
|
||||
let i = 0;
|
||||
const len = text.length;
|
||||
|
||||
while (i < len) {
|
||||
if (
|
||||
i + 3 < len &&
|
||||
text[i].toLowerCase() === 'u' &&
|
||||
text[i + 1].toLowerCase() === 'r' &&
|
||||
text[i + 2].toLowerCase() === 'l' &&
|
||||
text[i + 3] === '('
|
||||
) {
|
||||
const urlStart = i;
|
||||
i += 4;
|
||||
|
||||
// Skip whitespace
|
||||
while (i < len && (text[i] === ' ' || text[i] === '\t' || text[i] === '\n' || text[i] === '\r')) i++;
|
||||
|
||||
let quote: string | null = null;
|
||||
if (i < len && (text[i] === '"' || text[i] === "'")) {
|
||||
quote = text[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
let url = '';
|
||||
if (quote) {
|
||||
while (i < len && text[i] !== quote) {
|
||||
if (text[i] === '\\' && i + 1 < len) {
|
||||
i++;
|
||||
url += text[i];
|
||||
} else {
|
||||
url += text[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i < len) i++;
|
||||
} else {
|
||||
while (i < len && text[i] !== ')' && text[i] !== ' ' && text[i] !== '\t' && text[i] !== '\n') {
|
||||
url += text[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < len && (text[i] === ' ' || text[i] === '\t' || text[i] === '\n' || text[i] === '\r')) i++;
|
||||
|
||||
if (i < len && text[i] === ')') {
|
||||
const fullMatch = text.substring(urlStart, i + 1);
|
||||
results.push({ url: url.trim(), fullMatch, start: urlStart, end: i + 1 });
|
||||
i++;
|
||||
} else {
|
||||
i = urlStart + 1;
|
||||
}
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Local path resolution
|
||||
// ---------------------------------------------------------------------------
|
||||
function resolveLocalFile(localPath: string, baseDir: string): string | null {
|
||||
const candidates = [localPath];
|
||||
if (baseDir) {
|
||||
candidates.push(path.join(baseDir, localPath.replace(/^\//, '')));
|
||||
}
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
||||
return candidate;
|
||||
}
|
||||
} catch {
|
||||
// Permission errors, etc. — skip
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically open, stat, and read a file using a file descriptor.
|
||||
* Eliminates TOCTOU race conditions by performing all operations on the
|
||||
* same fd, ensuring the file cannot change between the size check and read.
|
||||
* Returns null if the file cannot be opened (e.g., deleted between resolve and open).
|
||||
*/
|
||||
function readFileAtomic(
|
||||
filePath: string,
|
||||
maxSize: number,
|
||||
): { size: number; mime: string; b64: string } | { size: number; tooLarge: true } | null {
|
||||
let fd: number;
|
||||
try {
|
||||
fd = fs.openSync(filePath, 'r');
|
||||
} catch {
|
||||
// File was removed or became inaccessible between resolve and open
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const stat = fs.fstatSync(fd);
|
||||
if (stat.size > maxSize) {
|
||||
return { size: stat.size, tooLarge: true };
|
||||
}
|
||||
const mime = getMime(filePath);
|
||||
const b64 = fs.readFileSync(fd).toString('base64');
|
||||
return { size: stat.size, mime, b64 };
|
||||
} finally {
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is a local (non-remote, non-data) reference.
|
||||
*/
|
||||
function isLocalPath(url: string): boolean {
|
||||
return (
|
||||
!!url &&
|
||||
!url.startsWith('http://') &&
|
||||
!url.startsWith('https://') &&
|
||||
!url.startsWith('data:') &&
|
||||
!url.startsWith('//')
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline images in HTML
|
||||
// ---------------------------------------------------------------------------
|
||||
function inlineImages(
|
||||
html: string,
|
||||
baseDir: string,
|
||||
maxSize: number,
|
||||
dryRun: boolean,
|
||||
): { html: string; stats: InlineStats } {
|
||||
const stats: InlineStats = {
|
||||
srcInlined: 0,
|
||||
urlInlined: 0,
|
||||
skippedTooLarge: [],
|
||||
skippedNotFound: [],
|
||||
};
|
||||
|
||||
// --- Inline src="<local_path>" attributes ---
|
||||
// Handle src, poster, data attributes
|
||||
const srcAttrs = ['src', 'poster', 'data'];
|
||||
for (const attr of srcAttrs) {
|
||||
const regex = new RegExp(`${attr}="((?!https?:\\/\\/|data:|\\/\\/)[^"]+)"`, 'g');
|
||||
html = html.replace(regex, (match: string, localPath: string) => {
|
||||
const resolved = resolveLocalFile(localPath, baseDir);
|
||||
if (!resolved) {
|
||||
if (!localPath.endsWith('.js') && !localPath.endsWith('.css')) {
|
||||
stats.skippedNotFound.push(localPath);
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
const result = readFileAtomic(resolved, maxSize);
|
||||
if (!result) {
|
||||
stats.skippedNotFound.push(localPath);
|
||||
return match;
|
||||
}
|
||||
if ('tooLarge' in result) {
|
||||
stats.skippedTooLarge.push({ path: localPath, size: result.size });
|
||||
return match;
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
stats.srcInlined++;
|
||||
return match;
|
||||
}
|
||||
|
||||
stats.srcInlined++;
|
||||
return `${attr}="data:${result.mime};base64,${result.b64}"`;
|
||||
});
|
||||
}
|
||||
|
||||
// --- Inline CSS url() with local paths (using robust parser) ---
|
||||
const urlRefs = extractCssUrls(html);
|
||||
const localUrlRefs = urlRefs.filter((ref) => isLocalPath(ref.url));
|
||||
|
||||
// Process from end to preserve indices
|
||||
const sorted = [...localUrlRefs].sort((a, b) => b.start - a.start);
|
||||
for (const ref of sorted) {
|
||||
const resolved = resolveLocalFile(ref.url, baseDir);
|
||||
if (!resolved) {
|
||||
stats.skippedNotFound.push(ref.url);
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = readFileAtomic(resolved, maxSize);
|
||||
if (!result) {
|
||||
stats.skippedNotFound.push(ref.url);
|
||||
continue;
|
||||
}
|
||||
if ('tooLarge' in result) {
|
||||
stats.skippedTooLarge.push({ path: ref.url, size: result.size });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
stats.urlInlined++;
|
||||
continue;
|
||||
}
|
||||
|
||||
html =
|
||||
html.substring(0, ref.start) +
|
||||
`url('data:${result.mime};base64,${result.b64}')` +
|
||||
html.substring(ref.end);
|
||||
stats.urlInlined++;
|
||||
}
|
||||
|
||||
// --- Inline SVG <image href="..."> and xlink:href ---
|
||||
const svgHrefRegex = /(href|xlink:href)="((?!https?:\/\/|data:|\/\/)[^"]+)"/g;
|
||||
html = html.replace(svgHrefRegex, (match: string, attrName: string, localPath: string) => {
|
||||
// Skip non-image hrefs (like <a href>)
|
||||
if (!localPath.match(/\.(svg|png|jpg|jpeg|gif|webp|avif|bmp|ico)$/i)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
const resolved = resolveLocalFile(localPath, baseDir);
|
||||
if (!resolved) {
|
||||
stats.skippedNotFound.push(localPath);
|
||||
return match;
|
||||
}
|
||||
|
||||
const result = readFileAtomic(resolved, maxSize);
|
||||
if (!result) {
|
||||
stats.skippedNotFound.push(localPath);
|
||||
return match;
|
||||
}
|
||||
if ('tooLarge' in result) {
|
||||
stats.skippedTooLarge.push({ path: localPath, size: result.size });
|
||||
return match;
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
stats.srcInlined++;
|
||||
return match;
|
||||
}
|
||||
|
||||
stats.srcInlined++;
|
||||
return `${attrName}="data:${result.mime};base64,${result.b64}"`;
|
||||
});
|
||||
|
||||
return { html, stats };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
function main(): void {
|
||||
const opts = parseArgs();
|
||||
validateOpts(opts);
|
||||
|
||||
const allStats: AllStats = {
|
||||
files: [],
|
||||
totalSrcInlined: 0,
|
||||
totalUrlInlined: 0,
|
||||
totalSkippedNotFound: 0,
|
||||
totalSkippedTooLarge: 0,
|
||||
};
|
||||
|
||||
if (opts.dryRun) {
|
||||
console.log('🔍 DRY RUN — no files will be modified\n');
|
||||
}
|
||||
|
||||
for (const file of opts.files) {
|
||||
// Open file once with r+ to eliminate TOCTOU race between read and write.
|
||||
// A single fd is used for both operations, so the file cannot be swapped
|
||||
// between the read and write phases.
|
||||
let fd: number;
|
||||
try {
|
||||
fd = fs.openSync(file, opts.dryRun ? 'r' : 'r+');
|
||||
} catch {
|
||||
console.warn(`⚠️ File not found, skipping: ${file}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let processed: string = '';
|
||||
let stats: InlineStats = { srcInlined: 0, urlInlined: 0, skippedTooLarge: [], skippedNotFound: [] };
|
||||
try {
|
||||
const html = fs.readFileSync(fd, 'utf-8');
|
||||
|
||||
const result = inlineImages(html, opts.baseDir, opts.maxSize, opts.dryRun);
|
||||
processed = result.html;
|
||||
stats = result.stats;
|
||||
|
||||
if (!opts.dryRun) {
|
||||
// Truncate and rewrite using the same fd — no second path-based open
|
||||
fs.ftruncateSync(fd);
|
||||
fs.writeSync(fd, processed, 0, 'utf-8');
|
||||
}
|
||||
} finally {
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
|
||||
const totalInlined = stats.srcInlined + stats.urlInlined;
|
||||
const label = opts.dryRun ? 'would inline' : 'inlined';
|
||||
console.log(
|
||||
`${file}: ${label} ${totalInlined} resources ` +
|
||||
`(${stats.srcInlined} src, ${stats.urlInlined} url()) ` +
|
||||
`— ${processed.length.toLocaleString()} bytes`,
|
||||
);
|
||||
|
||||
if (stats.skippedTooLarge.length > 0) {
|
||||
for (const s of stats.skippedTooLarge) {
|
||||
console.log(
|
||||
` ⚠️ Skipped (too large: ${(s.size / 1024).toFixed(1)} KB): ${s.path}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
allStats.files.push({
|
||||
file,
|
||||
srcInlined: stats.srcInlined,
|
||||
urlInlined: stats.urlInlined,
|
||||
skippedNotFound: stats.skippedNotFound.length,
|
||||
skippedTooLarge: stats.skippedTooLarge.length,
|
||||
sizeBytes: processed.length,
|
||||
});
|
||||
allStats.totalSrcInlined += stats.srcInlined;
|
||||
allStats.totalUrlInlined += stats.urlInlined;
|
||||
allStats.totalSkippedNotFound += stats.skippedNotFound.length;
|
||||
allStats.totalSkippedTooLarge += stats.skippedTooLarge.length;
|
||||
}
|
||||
|
||||
const totalInlined = allStats.totalSrcInlined + allStats.totalUrlInlined;
|
||||
console.log(`\n✅ Total: ${totalInlined} resources inlined across ${allStats.files.length} file(s)`);
|
||||
|
||||
if (allStats.totalSkippedTooLarge > 0) {
|
||||
console.log(` ⚠️ ${allStats.totalSkippedTooLarge} skipped (exceeded ${(opts.maxSize / 1024 / 1024).toFixed(1)} MB limit)`);
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
console.log('\n--- JSON Stats ---');
|
||||
console.log(JSON.stringify(allStats, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,339 @@
|
||||
---
|
||||
name: stitch::generate-design
|
||||
description: >-
|
||||
Generate new screens from text prompts or images, edit existing screens
|
||||
with prompts and design system tokens, and generate design variants using
|
||||
Stitch MCP. Includes prompt enhancement pipeline, design mappings, professional
|
||||
UI/UX terminology, design tokens and theme system capabilities.
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Generate Design
|
||||
|
||||
Create new design screens from text descriptions, images, or mockups, edit
|
||||
existing screens with prompts and design system tokens, and generate design
|
||||
variants using Stitch MCP.
|
||||
|
||||
> [!NOTE]
|
||||
> Refer to your system prompt for instruction on handling MCP tool prefixes for
|
||||
> all tools mentioned in this skill (e.g., `list_projects`,
|
||||
> `generate_screen_from_text`, `edit_screens`).
|
||||
|
||||
## 🎨 Prompt Enhancement Pipeline
|
||||
|
||||
Before calling any Stitch generation or editing tool, you MUST enhance the
|
||||
user's prompt.
|
||||
|
||||
### 1. Analyze Context
|
||||
|
||||
- **Project**: Use `list_projects` to find the correct `projectId`. If no
|
||||
suitable project exists, create one using `create_project`.
|
||||
- **Design System**: Check if a design system exists for the project via
|
||||
`list_design_systems`. If one exists, design tokens (colors, fonts, roundness)
|
||||
are already applied at the project level — do NOT include any color, font, or
|
||||
theme instructions in the generation prompt. If none exists, delegate to the
|
||||
**manage-design-system** skill first before generating screens.
|
||||
|
||||
### 2. Refine UI/UX Terminology
|
||||
|
||||
Consult [Design Mappings](references/design-mappings.md) to replace vague terms.
|
||||
|
||||
- Vague: "Make a nice header"
|
||||
- Professional: "Sticky navigation bar with glassmorphism effect and centered
|
||||
logo"
|
||||
|
||||
Use [Prompting Keywords](references/prompt-keywords.md) for component names,
|
||||
adjective palettes, color roles, and shape descriptions.
|
||||
|
||||
### 3. Structure the Final Prompt
|
||||
|
||||
Format the enhanced prompt for Stitch. Focus exclusively on **layout, content,
|
||||
and structure** — never include colors, fonts, or theme instructions (these are
|
||||
handled by the manage-design-system skill at the project level).
|
||||
|
||||
For **new screens**, use this template:
|
||||
|
||||
```markdown
|
||||
[Overall purpose and user intent of the page]
|
||||
|
||||
**PLATFORM:** [Web/Mobile], [Desktop/Mobile]-first
|
||||
|
||||
**PAGE STRUCTURE:**
|
||||
1. **Header:** [Description of navigation and branding]
|
||||
2. **Hero Section:** [Headline, subtext, and primary CTA]
|
||||
3. **Primary Content Area:** [Detailed component breakdown]
|
||||
4. **Footer:** [Links and copyright information]
|
||||
```
|
||||
|
||||
For **edits**, be specific about what to change:
|
||||
|
||||
- **Location**: "Change the [primary button] in the [hero section]..."
|
||||
- **Visuals**: "...to a darker blue (#004080) and add a subtle shadow."
|
||||
- **Structure**: "Add a secondary button next to the primary one with the text
|
||||
'Learn More'."
|
||||
|
||||
> [!CAUTION]
|
||||
> Do NOT include hex codes, font names, color palettes, roundness values, or
|
||||
> any design system tokens in a **generation** prompt. These are applied at the
|
||||
> project level by the manage-design-system skill and will conflict if
|
||||
> duplicated. (For **edit** prompts, hex codes are acceptable for precise
|
||||
> color adjustments.)
|
||||
|
||||
### 4. Present AI Insights
|
||||
|
||||
After any tool call, always surface the `outputComponents` (Text Description and
|
||||
Suggestions) to the user.
|
||||
|
||||
See [examples/enhanced-prompt.md](examples/enhanced-prompt.md) for a full
|
||||
before/after prompt enhancement example.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Steps
|
||||
|
||||
### Determine the Mode
|
||||
|
||||
Decide which flow to use based on the user's request:
|
||||
|
||||
- User wants to create from a text description → **Generate from Text** flow
|
||||
- User provides an image, screenshot, or mockup → **Generate from Image** flow
|
||||
- User wants to modify an existing screen → **Edit** flow
|
||||
- User wants layout/color/content variations → **Generate Variants** flow
|
||||
|
||||
---
|
||||
|
||||
### Generate from Text Flow (New Screen)
|
||||
|
||||
#### 1. Enhance the User Prompt
|
||||
|
||||
Apply the Prompt Enhancement Pipeline above.
|
||||
|
||||
#### 2. Identify the Project
|
||||
|
||||
Use `list_projects` to find the correct `projectId` if it is not already known.
|
||||
|
||||
#### 3. Generate the Screen
|
||||
|
||||
Call the `generate_screen_from_text` tool with the enhanced prompt and the
|
||||
`designSystem` ID (if found in Step 1).
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"prompt": "[Your Enhanced Prompt]",
|
||||
"designSystem": "assets/...", // Optional: Pass if found in Step 1
|
||||
"deviceType": "DESKTOP" // Options: MOBILE, DESKTOP, TABLET
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Present AI Feedback
|
||||
|
||||
Always show the text description and suggestions from `outputComponents` to the
|
||||
user.
|
||||
|
||||
#### 5. Download Design Assets
|
||||
|
||||
After generation, download the HTML and screenshot urls from `outputComponents`
|
||||
to the `.stitch/designs` directory.
|
||||
|
||||
- **Naming**: Use the screen ID or a descriptive slug for the filename.
|
||||
- **Tools**: Use `curl -o` via `run_command` or similar.
|
||||
- **Directory**: Ensure `.stitch/designs` exists.
|
||||
|
||||
#### 6. Review and Refine
|
||||
|
||||
- If the result is not exactly as expected, continue with the **Edit** flow
|
||||
to make targeted adjustments.
|
||||
- Do NOT re-generate from scratch unless the fundamental layout is wrong.
|
||||
|
||||
---
|
||||
|
||||
### Generate from Image Flow (Image/Mockup → Design)
|
||||
|
||||
Use this flow when the user provides an image, screenshot, or design mockup to
|
||||
recreate in Stitch.
|
||||
|
||||
#### 1. Identify the Project
|
||||
|
||||
Use `list_projects` to find the correct `projectId`. If no suitable project
|
||||
exists, create one using `create_project`.
|
||||
|
||||
#### 2. Upload the Image
|
||||
|
||||
Delegate to the **upload-to-stitch** skill to upload the image to the project.
|
||||
This creates a new screen with the image as its content.
|
||||
|
||||
#### 3. Refine with Edit
|
||||
|
||||
Once uploaded, use `list_screens` to find the newly created `screenId`, then
|
||||
call `edit_screens` with a descriptive prompt to refine the design:
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"selectedScreenIds": ["<uploaded-screen-id>"],
|
||||
"prompt": "[Describe what to adjust, enhance, or recreate from this mockup]"
|
||||
}
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> For best results, describe the intent behind the image rather than just saying
|
||||
> "make it look like this". For example: "This is a dashboard mockup — recreate
|
||||
> it with a proper data table, sidebar navigation, and chart widgets."
|
||||
|
||||
#### 4. Present AI Feedback
|
||||
|
||||
Always show the text description and suggestions from `outputComponents` to the
|
||||
user.
|
||||
|
||||
#### 5. Download Design Assets
|
||||
|
||||
Download the HTML and screenshot urls from `outputComponents` to the
|
||||
`.stitch/designs` directory.
|
||||
|
||||
- **Naming**: Use the screen ID or a descriptive slug for the filename.
|
||||
- **Tools**: Use `curl -o` via `run_command` or similar.
|
||||
- **Directory**: Ensure `.stitch/designs` exists.
|
||||
|
||||
---
|
||||
|
||||
### Edit Flow (Modify Existing Screen)
|
||||
|
||||
#### 1. Identify the Screen
|
||||
|
||||
Use `list_screens` or `get_screen` to find the correct `projectId` and
|
||||
`screenId`.
|
||||
|
||||
#### 2. Formulate the Edit Prompt
|
||||
|
||||
Apply the Prompt Enhancement Pipeline, focusing on specificity:
|
||||
|
||||
- **Location**: "Change the color of the [primary button] in the [hero
|
||||
section]..."
|
||||
- **Visuals**: "...to a darker blue (#004080) and add a subtle shadow."
|
||||
- **Structure**: "Add a secondary button next to the primary one with the text
|
||||
'Learn More'."
|
||||
|
||||
#### 3. Apply the Edit
|
||||
|
||||
Call the `edit_screens` tool.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"selectedScreenIds": ["..."],
|
||||
"prompt": "[Your targeted edit prompt]"
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Present AI Feedback
|
||||
|
||||
Always show the text description and suggestions from `outputComponents` to the
|
||||
user.
|
||||
|
||||
#### 5. Download Design Assets
|
||||
|
||||
After editing, download the updated HTML and screenshot urls from
|
||||
`outputComponents` to the `.stitch/designs` directory, overwriting previous
|
||||
versions to ensure the local files reflect the latest edits.
|
||||
|
||||
- **Naming**: Use the screen ID or a descriptive slug for the filename.
|
||||
- **Tools**: Use `curl -o` via `run_command` or similar.
|
||||
- **Directory**: Ensure `.stitch/designs` exists.
|
||||
|
||||
#### 6. Update Project Metadata
|
||||
|
||||
After downloading assets, update `.stitch/metadata.json` to reflect any changes
|
||||
(e.g., updated screen titles or new screen IDs from the edit). The metadata
|
||||
file tracks all screens, their device types, and design system info. See the
|
||||
**manage-design-system** skill's `examples/metadata.json` for the format.
|
||||
|
||||
#### 7. Verify and Repeat
|
||||
|
||||
- Check the output screen to see if the changes were applied correctly.
|
||||
- If more polish is needed, repeat the edit flow with a new specific prompt.
|
||||
|
||||
---
|
||||
|
||||
### Generate Variants Flow (Explore Variations)
|
||||
|
||||
Use this flow when the user wants to explore alternative layouts, color schemes,
|
||||
or content variations of an existing screen.
|
||||
|
||||
#### 1. Identify the Screen
|
||||
|
||||
Use `list_screens` or `get_screen` to find the correct `projectId` and
|
||||
`screenId`.
|
||||
|
||||
#### 2. Configure Variant Options
|
||||
|
||||
Call the `generate_variants` tool with the appropriate options:
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"selectedScreenIds": ["..."],
|
||||
"prompt": "[Describe the direction for variants]",
|
||||
"variantOptions": {
|
||||
"variantCount": 3,
|
||||
"creativeRange": "EXPLORE",
|
||||
"aspects": ["LAYOUT", "COLOR_SCHEME"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Variant Options:**
|
||||
- **`variantCount`**: 1–5 variants (default: 3)
|
||||
- **`creativeRange`**: `REFINE` (subtle), `EXPLORE` (balanced), or `REIMAGINE`
|
||||
(radical)
|
||||
- **`aspects`**: Focus on specific dimensions — `LAYOUT`, `COLOR_SCHEME`,
|
||||
`IMAGES`, `TEXT_FONT`, `TEXT_CONTENT`, or leave empty for all
|
||||
|
||||
#### 3. Present AI Feedback
|
||||
|
||||
Always show the text description and suggestions from `outputComponents` to the
|
||||
user.
|
||||
|
||||
#### 4. Download Design Assets
|
||||
|
||||
Download the variant HTML and screenshot urls from `outputComponents` to the
|
||||
`.stitch/designs` directory.
|
||||
|
||||
- **Naming**: Use the screen ID or a descriptive slug for the filename.
|
||||
- **Tools**: Use `curl -o` via `run_command` or similar.
|
||||
- **Directory**: Ensure `.stitch/designs` exists.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
- **Be structural**: Break the page down into header, hero, features, and
|
||||
footer in your prompt.
|
||||
- **Content first**: Describe what each section contains (text, images, CTAs)
|
||||
rather than how it looks.
|
||||
- **Iterative Polish**: Prefer editing for targeted adjustments over full
|
||||
re-generation.
|
||||
- **No theme leakage**: Never put hex codes, font names, or color roles in a
|
||||
generation prompt — the design system handles all visual styling.
|
||||
- **Specify interactions**: Mention hover states, animations, and click behavior
|
||||
rather than visual styling.
|
||||
- **Keep edits focused**: One edit at a time is often better than a long list of
|
||||
changes.
|
||||
- **Reference components**: Use professional terms like "navigation bar", "hero
|
||||
section", "footer", "card grid".
|
||||
- **Precise colors in edits**: Use hex codes for exact color matching when
|
||||
editing existing screens.
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [Design Mappings](references/design-mappings.md) — UI/UX keywords and
|
||||
atmosphere descriptors.
|
||||
- [Prompting Keywords](references/prompt-keywords.md) — Technical terms Stitch
|
||||
understands best.
|
||||
- [Enhanced Prompt Example](examples/enhanced-prompt.md) — Before/after prompt
|
||||
enhancement.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Example: Prompt Enhancement
|
||||
|
||||
## User Input (Vague)
|
||||
> "Make a landing page for my fitness app called 'Pulse'. It should have a
|
||||
> place for features and a hero section."
|
||||
|
||||
---
|
||||
|
||||
## Enhanced Prompt (Stitch-Optimized)
|
||||
|
||||
> [!NOTE]
|
||||
> This prompt contains NO colors, fonts, or theme instructions — those are
|
||||
> handled by the project's design system. The prompt focuses exclusively on
|
||||
> layout, structure, and content.
|
||||
|
||||
**Overall Purpose**: A high-energy, modern landing page for 'Pulse', a fitness
|
||||
tracking application. The page should feel powerful, motivating, and
|
||||
high-performance.
|
||||
|
||||
**PLATFORM:** Web, Desktop-first
|
||||
|
||||
**PAGE STRUCTURE:**
|
||||
1. **Header**: Minimalist navigation with Pulse logo (left) and "Start Training"
|
||||
primary call-to-action button (right).
|
||||
2. **Hero Section**: Large, emotive fitness photography background. Headline:
|
||||
"Elevate Every Beat." Sub-headline: "Track, analyze, and crush your fitness
|
||||
goals with Pulse." Primary CTA button: "Get Started".
|
||||
3. **Feature Grid**: Three-column responsive card grid highlighting:
|
||||
- **Real-time Tracking**: Live stats from your wearable.
|
||||
- **AI Coaching**: Personalized workouts based on your performance.
|
||||
- **Community Challenges**: Compete with friends and stay motivated.
|
||||
4. **Social Proof Section**: Horizontal slider showing "Trusted by 500,000+
|
||||
athletes" with partner logos.
|
||||
5. **Footer**: Quick links (Training, Pricing, Support), social icons, and
|
||||
legal text.
|
||||
|
||||
---
|
||||
💡 **Tip**: Notice how the enhanced prompt describes **structure and content**
|
||||
without specifying hex codes, font families, or color roles. The design system
|
||||
(created via the manage-design-system skill) handles all visual theming at the project
|
||||
level.
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
# Design Mappings & Descriptors
|
||||
|
||||
Use these mappings to transform vague user requests into precise, high-fidelity design instructions.
|
||||
Use these mappings to transform vague user requests into precise, high-fidelity
|
||||
design instructions.
|
||||
|
||||
## UI/UX Keyword Refinement
|
||||
|
||||
+33
-16
@@ -1,16 +1,19 @@
|
||||
# UI/UX Keywords Reference
|
||||
|
||||
Progressive disclosure reference for common UI terminology and adjective palettes.
|
||||
Progressive disclosure reference for common UI terminology and adjective
|
||||
palettes.
|
||||
|
||||
## Component Keywords
|
||||
|
||||
### Navigation
|
||||
|
||||
- navigation bar, nav menu, header
|
||||
- breadcrumbs, tabs, sidebar
|
||||
- hamburger menu, dropdown menu
|
||||
- back button, close button
|
||||
|
||||
### Content Containers
|
||||
|
||||
- hero section, hero banner
|
||||
- card, card grid, tile
|
||||
- modal, dialog, popup
|
||||
@@ -18,6 +21,7 @@ Progressive disclosure reference for common UI terminology and adjective palette
|
||||
- carousel, slider
|
||||
|
||||
### Forms
|
||||
|
||||
- input field, text input
|
||||
- dropdown, select menu
|
||||
- checkbox, radio button
|
||||
@@ -27,18 +31,21 @@ Progressive disclosure reference for common UI terminology and adjective palette
|
||||
- submit button, form actions
|
||||
|
||||
### Calls to Action
|
||||
|
||||
- primary button, secondary button
|
||||
- ghost button, text link
|
||||
- floating action button (FAB)
|
||||
- icon button
|
||||
|
||||
### Feedback
|
||||
|
||||
- toast notification, snackbar
|
||||
- alert banner, warning message
|
||||
- loading spinner, skeleton loader
|
||||
- progress bar, step indicator
|
||||
|
||||
### Layout
|
||||
|
||||
- grid layout, flexbox
|
||||
- sidebar layout, split view
|
||||
- sticky header, fixed footer
|
||||
@@ -48,59 +55,69 @@ Progressive disclosure reference for common UI terminology and adjective palette
|
||||
## Adjective Palettes
|
||||
|
||||
### Minimal / Clean
|
||||
|
||||
- minimal, clean, uncluttered
|
||||
- generous whitespace, breathing room
|
||||
- subtle, understated, refined
|
||||
- simple, focused, distraction-free
|
||||
|
||||
### Professional / Corporate
|
||||
|
||||
- sophisticated, polished, trustworthy
|
||||
- corporate, business-like, formal
|
||||
- subtle shadows, clean lines
|
||||
- structured, organized, hierarchical
|
||||
|
||||
### Playful / Fun
|
||||
|
||||
- vibrant, colorful, energetic
|
||||
- rounded corners, soft edges
|
||||
- bold, expressive, dynamic
|
||||
- friendly, approachable, warm
|
||||
|
||||
### Premium / Luxury
|
||||
|
||||
- elegant, luxurious, high-end
|
||||
- dramatic, bold contrasts
|
||||
- sleek, modern, cutting-edge
|
||||
- exclusive, boutique, curated
|
||||
|
||||
### Dark Mode
|
||||
|
||||
- dark theme, night mode
|
||||
- high-contrast accents
|
||||
- soft glows, subtle highlights
|
||||
- deep backgrounds, muted surfaces
|
||||
|
||||
### Organic / Natural
|
||||
|
||||
- earthy tones, natural colors
|
||||
- warm, inviting, cozy
|
||||
- textured, tactile, handcrafted
|
||||
- flowing, organic shapes
|
||||
|
||||
## Color Role Terminology
|
||||
## Interaction & Behavior Keywords
|
||||
|
||||
### Backgrounds
|
||||
- page background, canvas
|
||||
- surface color, card background
|
||||
- overlay, scrim
|
||||
### Animations
|
||||
|
||||
### Text
|
||||
- primary text, heading color
|
||||
- secondary text, body copy
|
||||
- muted text, placeholder
|
||||
- inverse text (on dark backgrounds)
|
||||
- smooth entry animation, slide-in
|
||||
- fade transition, cross-fade
|
||||
- micro-interaction, bounce effect
|
||||
- skeleton loader, shimmer effect
|
||||
|
||||
### Accents
|
||||
- primary accent, brand color
|
||||
- secondary accent, highlight
|
||||
- success, error, warning colors
|
||||
- hover state, active state
|
||||
### Responsiveness
|
||||
|
||||
- responsive layout, adaptive grid
|
||||
- mobile-first, desktop-first
|
||||
- breakpoint, viewport
|
||||
- stacked layout, side-by-side
|
||||
|
||||
### User Flows
|
||||
|
||||
- progressive disclosure, step indicator
|
||||
- expandable section, collapsible panel
|
||||
- infinite scroll, pagination
|
||||
- drag and drop, swipe gesture
|
||||
|
||||
## Shape Descriptions
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
name: stitch::manage-design-system
|
||||
description: >-
|
||||
Manage design systems in Stitch using MCP tools. Includes retrieval of assets,
|
||||
creating/updating design systems in Stitch, and applying them to screens.
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Design-System
|
||||
|
||||
Create a "source of truth" for your project's design language to ensure
|
||||
consistency across all future screens.
|
||||
|
||||
> [!NOTE]
|
||||
> Refer to your system prompt for instruction on handling MCP tool prefixes for
|
||||
> all tools mentioned in this skill (e.g., `get_screen`,
|
||||
> `create_design_system_from_design_md`, `apply_design_system`).
|
||||
|
||||
## 📥 Retrieval
|
||||
|
||||
To analyze a Stitch project, you must retrieve metadata and assets using the
|
||||
Stitch MCP tools:
|
||||
|
||||
1. **Project lookup**: Use `list_projects` to find the target `projectId`.
|
||||
2. **Screen lookup**: Use `list_screens` for that `projectId` to find
|
||||
representative screens (e.g., "Home", "Main Dashboard").
|
||||
3. **Metadata fetch**: Call `get_screen` for the target screen to get
|
||||
`screenshot.downloadUrl` and `htmlCode.downloadUrl`.
|
||||
4. **Asset download**: Use `read_url_content` to fetch the HTML code.
|
||||
|
||||
## 🧠 Synthesis from Description
|
||||
|
||||
If you need to extract a design system from existing screens, use the `design-md` skill (in the `stitch-utilities` plugin).
|
||||
|
||||
If there are no existing screens (new project), or the user provides a direct description (e.g., "dark theme, blue and purple, rounded, Inter font"):
|
||||
|
||||
1. Map the user's vague terms to precise values using the design mappings (see `design-md` skill in `stitch-utilities` or `generate-design` skill).
|
||||
2. Select concrete hex codes, font families, and roundness values.
|
||||
3. Generate the `DESIGN.md` file (refer to the `design-md` skill in `stitch-utilities` for structure).
|
||||
4. Proceed to the "Create or Update Design System in Stitch" step below.
|
||||
|
||||
## 📝 Output Structure
|
||||
|
||||
The `DESIGN.md` file should follow the structure defined in the `design-md` skill (in the `stitch-utilities` plugin).
|
||||
|
||||
## 🚀 Create or Update Design System in Stitch
|
||||
|
||||
After generating `.stitch/DESIGN.md`, make sure to also create or update the
|
||||
design system in Stitch.
|
||||
|
||||
**Two-step design system creation:**
|
||||
|
||||
> [!WARNING]
|
||||
> **Checkpoint — User Confirmation Required.**
|
||||
> Before uploading, you **MUST** pause and ask the user for
|
||||
> confirmation. Present a summary of the design system you are about to create
|
||||
> (display name, key colors, fonts, and roundness) and wait for explicit approval
|
||||
> before proceeding. Do **NOT** upload until the user confirms.
|
||||
|
||||
1. **Upload `DESIGN.md`**:
|
||||
- **Option A (Recommended - Uploader Script)**: Use the modified `upload-to-stitch` Python script which natively handles `.md` files. It base64-encodes the markdown file in-process and sends it to the `/v1/projects/{projectId}/screens:batchCreate` endpoint, bypassing output token limits.
|
||||
```bash
|
||||
python3 stitch-skills/plugins/stitch-design/skills/upload-to-stitch/scripts/upload_to_stitch.py \
|
||||
--project-id <PROJECT_ID> \
|
||||
--file-path /path/to/DESIGN.md \
|
||||
--api-key <API_KEY>
|
||||
```
|
||||
This returns the `sourceScreen` ID and the `screenInstance` ID.
|
||||
- **Option B (Direct MCP Tool)**: If the `DESIGN.md` is small (under ~5KB), you can call the `upload_design_md` MCP tool directly, passing the base64-encoded design markdown content as `designMdBase64`.
|
||||
2. **Create Design System**: Call the `create_design_system_from_design_md` tool immediately after the upload, passing the `projectId` and the `selectedScreenInstance` (containing the `id` and `sourceScreen` returned from the upload step).
|
||||
|
||||
Once the upload script and `create_design_system_from_design_md` have both completed,
|
||||
Stitch holds the design tokens at the project level — you do NOT need to repeat
|
||||
them in generation prompts.
|
||||
|
||||
## 🎨 Apply Design System to Screens
|
||||
|
||||
Use `apply_design_system` to apply a design system to existing screens.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `selectedScreenInstances` must contain **only** `id` and `sourceScreen` — do
|
||||
> NOT include position/dimension fields (`x`, `y`, `width`, `height`) or the
|
||||
> request will fail with "invalid argument". Get the screen instance IDs from
|
||||
> `get_project`.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"assetId": "...",
|
||||
"selectedScreenInstances": [
|
||||
{
|
||||
"id": "...",
|
||||
"sourceScreen": "projects/.../screens/..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**How to get the required IDs:**
|
||||
1. Call `get_project` to retrieve `screenInstances` — each has an `id` and
|
||||
`sourceScreen`.
|
||||
2. Call `list_design_systems` to retrieve the design system `name` (format:
|
||||
`assets/{assetId}`) — use the part after `assets/` as the `assetId`.
|
||||
3. Filter out any instances with `type: "DESIGN_SYSTEM_INSTANCE"` — only pass
|
||||
real screens.
|
||||
|
||||
## 📋 Update Project Metadata
|
||||
|
||||
After writing `.stitch/DESIGN.md`, also create or update `.stitch/metadata.json`
|
||||
to track the `projectId`, `title`, all known screens, and design system summary.
|
||||
See [examples/metadata.json](examples/metadata.json) for the format.
|
||||
|
||||
## Schema Reference
|
||||
|
||||
See [reference/tool-schema.md](reference/tool-schema.md) for the full
|
||||
`designSystem` object schema with all available options.
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
Refer to the `design-md` skill (in the `stitch-utilities` plugin) for best practices on describing design elements.
|
||||
+2
-1
@@ -14,9 +14,10 @@
|
||||
}
|
||||
],
|
||||
"designSystem": {
|
||||
"assetId": "15996705518239280238",
|
||||
"primaryColor": "#1a365d",
|
||||
"secondaryColor": "#718096",
|
||||
"backgroundColor": "#f7fafc",
|
||||
"fontFamily": "Inter, sans-serif"
|
||||
"fontFamily": "INTER"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
# Design System Tool Schemas
|
||||
|
||||
Use these examples to format your Stitch MCP design system tool calls correctly.
|
||||
|
||||
---
|
||||
|
||||
## Upload `DESIGN.md` (via `upload_to_stitch.py` script)
|
||||
|
||||
Uploads a `DESIGN.md` file to a project via the `BatchCreateScreens` endpoint.
|
||||
This is the first step in creating a design system from a markdown file.
|
||||
|
||||
> [!NOTE]
|
||||
> Use the `upload-to-stitch` skill's script instead of the `upload_design_md`
|
||||
> MCP tool. The script handles base64 encoding in-process, avoiding the model's
|
||||
> output token limit.
|
||||
|
||||
```bash
|
||||
python3 <SKILL_DIR>/scripts/upload_to_stitch.py \
|
||||
--project-id <PROJECT_ID> \
|
||||
--file-path /path/to/DESIGN.md \
|
||||
--api-key <API_KEY>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `create_design_system_from_design_md`
|
||||
|
||||
Creates a design system for a project using the uploaded `DESIGN.md` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931",
|
||||
"selectedScreenInstance": {
|
||||
"id": "98b50e2ddc9943efb387052637738f61",
|
||||
"sourceScreen": "projects/4044680601076201931/screens/98b50e2ddc9943efb387052637738f61"
|
||||
},
|
||||
"deviceType": "DESKTOP"
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> You must upload `DESIGN.md` via the script first to get the source screen ID, and then fetch the project details with `get_project` to find the corresponding screen instance ID to pass as `id` in `selectedScreenInstance`.
|
||||
|
||||
---
|
||||
|
||||
## `update_design_system`
|
||||
|
||||
Updates an existing design system for a project. This is **required** immediately after calling `create_design_system` to set the theme and display the design system in the UI.
|
||||
|
||||
> [!NOTE]
|
||||
> While `update_design_system` is mandatory after the basic `create_design_system` call, you do **not** need to call it after `create_design_system_from_design_md`. The latter automatically populates and updates all theme tokens directly from the parsed YAML frontmatter of the uploaded `DESIGN.md`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "assets/15996705518239280238",
|
||||
"projectId": "4044680601076201931",
|
||||
"designSystem": {
|
||||
"displayName": "My Design System", // OPTIONAL. Display name of the design system
|
||||
"theme": { // REQUIRED. The design theme object
|
||||
"colorMode": "LIGHT", // REQUIRED. Options: LIGHT, DARK
|
||||
"headlineFont": "INTER", // REQUIRED. Options: INTER, ROBOTO, OPEN_SANS, LATO, MONTSERRAT, NOTO_SANS, NOTO_SERIF, etc.
|
||||
"bodyFont": "INTER", // REQUIRED. Same font options as headlineFont
|
||||
"labelFont": "INTER", // OPTIONAL. Same font options as headlineFont
|
||||
"roundness": "ROUND_EIGHT", // REQUIRED. Options: ROUND_FOUR, ROUND_EIGHT, ROUND_TWELVE, ROUND_FULL
|
||||
"customColor": "#0EA5E9", // REQUIRED. Primary brand color / seed color for dynamic color system (hex)
|
||||
"colorVariant": "FIDELITY", // OPTIONAL. Options: FIDELITY, TONAL, VIBRANT, EXPRESSIVE, CONTENT, MONOCHROME, FRUIT_SALAD, RAINBOW
|
||||
"overridePrimaryColor": "#996e47", // OPTIONAL. Override primary color (hex)
|
||||
"overrideSecondaryColor": "#0EA5E9", // OPTIONAL. Override secondary color (hex)
|
||||
"overrideTertiaryColor": "#c4956a", // OPTIONAL. Override tertiary color (hex)
|
||||
"overrideNeutralColor": "#0D0D0D", // OPTIONAL. Override neutral color (hex)
|
||||
"designMd": "# Design System..." // OPTIONAL. Markdown string with detailed design system spec
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Field Reference
|
||||
|
||||
#### Required Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|:------|:-----|:------------|
|
||||
| `colorMode` | enum | `LIGHT` or `DARK` |
|
||||
| `headlineFont` | enum | Font for headlines and display text. See font options below. |
|
||||
| `bodyFont` | enum | Font for body text. See font options below. |
|
||||
| `roundness` | enum | `ROUND_FOUR`, `ROUND_EIGHT`, `ROUND_TWELVE`, `ROUND_FULL` |
|
||||
| `customColor` | hex | Primary brand / seed color for the dynamic color system (e.g., `#E8732A`) |
|
||||
|
||||
#### Optional Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|:------|:-----|:------------|
|
||||
| `displayName` | string | Human-readable name for the design system |
|
||||
| `labelFont` | enum | Font for labels and captions. Defaults to `bodyFont` if omitted. |
|
||||
| `colorVariant` | enum | `FIDELITY`, `TONAL`, `VIBRANT`, `EXPRESSIVE`, `CONTENT`, `MONOCHROME`, `FRUIT_SALAD`, `RAINBOW` |
|
||||
| `overridePrimaryColor` | hex | Override primary color (e.g., `#E8732A`) |
|
||||
| `overrideSecondaryColor` | hex | Override secondary color (e.g., `#1B6B93`) |
|
||||
| `overrideTertiaryColor` | hex | Override tertiary color (e.g., `#F2A541`) |
|
||||
| `overrideNeutralColor` | hex | Override neutral color (e.g., `#FAF7F2`) |
|
||||
| `spacingScale` | integer | Spacing scale factor (observed value: `3`) |
|
||||
| `designMd` | string | Markdown string with detailed design system specifications |
|
||||
|
||||
#### Font Options
|
||||
|
||||
The following font enum values are confirmed to work (server-validated):
|
||||
|
||||
| Value | Font Name |
|
||||
|:------|:----------|
|
||||
| `INTER` | Inter |
|
||||
| `ROBOTO` | Roboto |
|
||||
| `OPEN_SANS` | Open Sans |
|
||||
| `LATO` | Lato |
|
||||
| `MONTSERRAT` | Montserrat |
|
||||
| `NOTO_SANS` | Noto Sans |
|
||||
| `NOTO_SERIF` | Noto Serif |
|
||||
| `PLUS_JAKARTA_SANS` | Plus Jakarta Sans |
|
||||
| `BE_VIETNAM_PRO` | Be Vietnam Pro |
|
||||
|
||||
> [!WARNING]
|
||||
> Omit the legacy `font` field when updating the design system to avoid "invalid argument" errors.
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> The `namedColors` object above is abbreviated. The full response contains 50+
|
||||
> Material 3 color tokens including all container, fixed, and inverse variants.
|
||||
|
||||
---
|
||||
|
||||
## `apply_design_system`
|
||||
|
||||
Applies a design system to one or more screens in a project.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `selectedScreenInstances` must contain **only** `id` and `sourceScreen` — do NOT
|
||||
> include position/dimension fields (`x`, `y`, `width`, `height`) or the request
|
||||
> will fail with "invalid argument". Get the screen instance IDs from
|
||||
> `get_project`.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931",
|
||||
"assetId": "c277fcdfc1e04baf91b92d975ff4c54a",
|
||||
"selectedScreenInstances": [
|
||||
{
|
||||
"id": "98b50e2ddc9943efb387052637738f61",
|
||||
"sourceScreen": "projects/4044680601076201931/screens/98b50e2ddc9943efb387052637738f61"
|
||||
},
|
||||
{
|
||||
"id": "ab12cd34ef56789012345678abcdef01",
|
||||
"sourceScreen": "projects/4044680601076201931/screens/ab12cd34ef56789012345678abcdef01"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**How to get the required IDs:**
|
||||
1. Call `get_project` to retrieve `screenInstances` — each has an `id` and
|
||||
`sourceScreen`.
|
||||
2. Call `list_design_systems` to retrieve the design system `name` (format:
|
||||
`assets/{assetId}`) — use the part after `assets/` as the `assetId`.
|
||||
3. Filter out any instances with `type: "DESIGN_SYSTEM_INSTANCE"` — only pass
|
||||
real screens.
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: stitch::upload-to-stitch
|
||||
description: >-
|
||||
Upload local assets (images, mockups, extracted HTML) to a Stitch project.
|
||||
ALWAYS use this skill when you need to upload visual assets or full HTML pages
|
||||
to Stitch, particularly when direct MCP tool calls fail or truncate due to
|
||||
base64 token limits.
|
||||
allowed-tools:
|
||||
- "stitch*:*"
|
||||
- "Bash"
|
||||
- "Read"
|
||||
- "Write"
|
||||
- "web_fetch"
|
||||
---
|
||||
|
||||
# Upload-to-Stitch
|
||||
|
||||
Upload local assets (images, mockups, HTML files) to a Stitch project using the
|
||||
provided upload script, which bypasses the MCP tool's base64 output token limits.
|
||||
|
||||
> [!NOTE]
|
||||
> The AI model cannot upload files via MCP tools directly because the base64
|
||||
> encoding of even a small file exceeds the model's output token limit (~16K
|
||||
> tokens). This script reads the file and sends it directly over HTTP.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Identify Target Project
|
||||
|
||||
Use `list_projects` to find the correct `projectId`.
|
||||
|
||||
### 2. Get the API Key
|
||||
|
||||
Locate your active MCP server configuration file and extract the API key:
|
||||
- **Antigravity**: `.gemini/antigravity/mcp_config.json` or `.gemini/jetski/mcp_config.json`
|
||||
- **Gemini CLI**: `~/.gemini/settings.json` or `~/.gemini/extensions/Stitch/gemini-extension.json`
|
||||
- **Claude Code**: `~/.claude.json`
|
||||
|
||||
Extract:
|
||||
- **API Key**: From the `X-Goog-Api-Key` header or auth argument
|
||||
- **MCP URL** (optional): From the `httpUrl` or endpoint argument (defaults to
|
||||
`https://stitch.googleapis.com`)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you cannot find the API key in any of these locations, or if you cannot access these files, you MUST ask the user to provide the Stitch API key. Do not proceed without a valid API key.
|
||||
|
||||
### 3. Run Upload Script
|
||||
|
||||
> [!WARNING]
|
||||
> **Checkpoint — User Confirmation Required.**
|
||||
> Before running the upload script, you **MUST** pause and present the file(s)
|
||||
> to be uploaded (paths, sizes, and types) to the user and wait for explicit
|
||||
> approval. Do **NOT** execute the upload script until the user confirms.
|
||||
|
||||
Use `run_command` to execute the Python script:
|
||||
|
||||
```bash
|
||||
python3 <SKILL_DIR>/scripts/upload_to_stitch.py \
|
||||
--project-id <PROJECT_ID> \
|
||||
--file-path <PATH_TO_FILE> \
|
||||
--api-key <API_KEY> \
|
||||
[--api-url <STITCH_API_URL>] \
|
||||
[--title <SCREEN_TITLE>]
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> **macOS / SSL Certificate Troubleshooting:**
|
||||
> If the upload fails with `ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] unable to get local issuer certificate`, this means your Python installation does not have root certificate authorities configured.
|
||||
> Fix it by prepending `SSL_CERT_FILE` using the `certifi` bundle:
|
||||
> ```bash
|
||||
> SSL_CERT_FILE=$(python3 -c "import certifi; print(certifi.where())") python3 <SKILL_DIR>/scripts/upload_to_stitch.py \
|
||||
> --project-id <PROJECT_ID> \
|
||||
> --file-path <PATH_TO_FILE> \
|
||||
> --api-key <API_KEY>
|
||||
> ```
|
||||
|
||||
### Supported File Types
|
||||
|
||||
| Extension | MIME Type |
|
||||
|:---|:---|
|
||||
| `.png` | `image/png` |
|
||||
| `.jpg`, `.jpeg` | `image/jpeg` |
|
||||
| `.webp` | `image/webp` |
|
||||
| `.html`, `.htm` | `text/html` |
|
||||
|
||||
The script auto-detects MIME type from the file extension.
|
||||
|
||||
### Script Defaults
|
||||
|
||||
- `--api-url` defaults to `https://stitch.googleapis.com`
|
||||
- `--api-key` is **required**
|
||||
- Screen instances are automatically created for display
|
||||
- For HTML files, `screenType` is set to `DOCUMENT`; for images, `IMAGE`
|
||||
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python3
|
||||
r"""Upload an image or HTML to a Stitch project via BatchCreateScreens.
|
||||
|
||||
WHY THIS SCRIPT EXISTS:
|
||||
The AI model cannot upload files via the MCP tool directly because MCP tool
|
||||
call arguments are part of the model's *output*. The model must re-emit the
|
||||
entire base64-encoded file as generated text, but its output token limit
|
||||
(~16K tokens) is far smaller than a typical file's base64 encoding (e.g.
|
||||
a 53KB PNG becomes ~71K chars of base64). The output gets truncated
|
||||
mid-string, producing a corrupted payload that the API rejects.
|
||||
|
||||
This script bypasses the model entirely — it reads the file, encodes it
|
||||
in-process, and sends the full payload directly over HTTP with no token
|
||||
limits.
|
||||
|
||||
SUPPORTED FILE TYPES:
|
||||
- Images: .png, .jpg, .jpeg, .webp
|
||||
- HTML: .html, .htm
|
||||
|
||||
Usage:
|
||||
python3 upload_to_stitch.py \
|
||||
--project-id <PROJECT_ID> \
|
||||
--file-path <PATH_TO_FILE> \
|
||||
[--api-url <STITCH_API_BASE_URL>] \
|
||||
[--api-key <API_KEY>] \
|
||||
[--title <SCREEN_TITLE>] \
|
||||
[--create-screen-instances]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Any
|
||||
import urllib.request
|
||||
|
||||
|
||||
# Maps file extensions to MIME types.
|
||||
_MIME_TYPES = {
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".webp": "image/webp",
|
||||
".html": "text/html",
|
||||
".htm": "text/html",
|
||||
".md": "text/markdown",
|
||||
}
|
||||
|
||||
|
||||
def encode_file(path: pathlib.Path) -> str:
|
||||
"""Read and base64-encode a file."""
|
||||
with open(path, "rb") as f:
|
||||
return base64.b64encode(f.read()).decode("utf-8")
|
||||
|
||||
|
||||
def call_batch_create_screens(
|
||||
api_url: str,
|
||||
api_key: str,
|
||||
project_id: str,
|
||||
requests: list[dict[str, Any]],
|
||||
create_screen_instances: bool = False,
|
||||
urlopen: Any = urllib.request.urlopen,
|
||||
) -> dict[str, Any]:
|
||||
"""Call BatchCreateScreens REST API directly.
|
||||
|
||||
Endpoint: POST /v1/{parent=projects/*}/screens:batchCreate
|
||||
|
||||
Args:
|
||||
api_url: Base URL of the Stitch API (e.g. https://stitch.googleapis.com).
|
||||
api_key: API key for authentication.
|
||||
project_id: The Stitch project ID.
|
||||
requests: List of CreateScreenRequest dicts, each containing a screen.
|
||||
create_screen_instances: Whether to create screen instances for display.
|
||||
urlopen: The urlopen function to use (for testing).
|
||||
|
||||
Returns:
|
||||
Parsed JSON response dict.
|
||||
"""
|
||||
url = f"{api_url.rstrip('/')}/v1/projects/{project_id}/screens:batchCreate"
|
||||
|
||||
payload = {
|
||||
"parent": f"projects/{project_id}",
|
||||
"requests": requests,
|
||||
"createScreenInstances": create_screen_instances,
|
||||
}
|
||||
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=data,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Goog-Api-Key": api_key,
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
|
||||
try:
|
||||
print("Calling urlopen...")
|
||||
with urlopen(req, timeout=120) as resp:
|
||||
print(f"urlopen returned. Status: {resp.getcode()}")
|
||||
body = resp.read().decode("utf-8")
|
||||
print(f"Response status: {resp.getcode()}")
|
||||
print(f"Response body (first 1000 chars): {body[:1000]}")
|
||||
if not body:
|
||||
print("Error: Empty response body")
|
||||
sys.exit(1)
|
||||
return json.loads(body)
|
||||
except urllib.error.HTTPError as e:
|
||||
error_body = e.read().decode("utf-8")
|
||||
print(f"HTTP Error {e.code}: {e.reason}")
|
||||
print(f"Response: {error_body}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def build_screen_request(
|
||||
mime_type: str,
|
||||
b64_data: str,
|
||||
title: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a CreateScreenRequest dict from a file.
|
||||
|
||||
For images, the file is set as the screenshot.
|
||||
For HTML, the file is set as the html_code.
|
||||
|
||||
Args:
|
||||
mime_type: The MIME type of the file.
|
||||
b64_data: Base64-encoded file content.
|
||||
title: Optional title for the screen.
|
||||
|
||||
Returns:
|
||||
A CreateScreenRequest-shaped dict.
|
||||
"""
|
||||
file_obj = {
|
||||
"fileContentBase64": b64_data,
|
||||
"mimeType": mime_type,
|
||||
}
|
||||
|
||||
if mime_type in ("text/html", "text/markdown"):
|
||||
screen = {
|
||||
"htmlCode": file_obj,
|
||||
"screenType": "DOCUMENT",
|
||||
"isCreatedByClient": True,
|
||||
}
|
||||
else:
|
||||
screen = {
|
||||
"screenshot": file_obj,
|
||||
"screenType": "IMAGE",
|
||||
"isCreatedByClient": True,
|
||||
}
|
||||
|
||||
if title:
|
||||
screen["title"] = title
|
||||
|
||||
return {"screen": screen}
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command-line arguments."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Upload a file to a Stitch project via BatchCreateScreens."
|
||||
)
|
||||
parser.add_argument("--project-id", required=True, help="Stitch project ID")
|
||||
parser.add_argument(
|
||||
"--file-path",
|
||||
required=True,
|
||||
type=pathlib.Path,
|
||||
help=(
|
||||
"Path to the file to upload. Supported types:"
|
||||
f" {', '.join(sorted(_MIME_TYPES.keys()))}"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-url",
|
||||
default="https://stitch.googleapis.com",
|
||||
help="Stitch API base URL. Defaults to https://stitch.googleapis.com.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-key",
|
||||
required=True,
|
||||
help="API key for the Stitch API.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--title",
|
||||
default=None,
|
||||
help="Optional title for the created screen",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
file_path = args.file_path
|
||||
file_suffix = file_path.suffix.lower()
|
||||
mime_type = _MIME_TYPES.get(file_suffix)
|
||||
|
||||
if mime_type is None:
|
||||
print(
|
||||
f"Error: Unsupported file type '{file_suffix}'. Supported types:"
|
||||
f" {', '.join(sorted(_MIME_TYPES.keys()))}"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if not file_path.exists():
|
||||
print(f"Error: File not found: {file_path}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"File: {file_path}")
|
||||
print(f"MIME type: {mime_type}")
|
||||
|
||||
b64_data = encode_file(file_path)
|
||||
print(f"Base64: {len(b64_data)} chars")
|
||||
|
||||
screen_request = build_screen_request(mime_type, b64_data, title=args.title)
|
||||
|
||||
print(f"\nUploading to project: {args.project_id}")
|
||||
print(f"API URL: {args.api_url}")
|
||||
|
||||
result = call_batch_create_screens(
|
||||
api_url=args.api_url,
|
||||
api_key=args.api_key,
|
||||
project_id=args.project_id,
|
||||
requests=[screen_request],
|
||||
create_screen_instances=True,
|
||||
)
|
||||
|
||||
print("\nResponse:")
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "stitch-utilities",
|
||||
"description": "Design utilities and assistants for Google Stitch — generate DESIGN.md specs, enhance prompts, build multi-page sites with stitch-loop, and enforce premium design standards.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "google-labs-code"
|
||||
},
|
||||
"homepage": "https://github.com/google-labs-code/stitch-skills",
|
||||
"repository": "https://github.com/google-labs-code/stitch-skills",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"stitch",
|
||||
"design-system",
|
||||
"prompt-engineering",
|
||||
"design-md",
|
||||
"website-generation",
|
||||
"ui-standards"
|
||||
]
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
# Stitch Design Skill
|
||||
|
||||
Teaches agents to generate high-fidelity, consistent UI designs and maintain project-level design systems using Stitch.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npx skills add google-labs-code/stitch-skills --skill stitch-design --global
|
||||
```
|
||||
|
||||
## What It Does
|
||||
|
||||
Enables professional-grade UI/UX design workflows through Stitch MCP:
|
||||
|
||||
1. **Prompt Enhancement**: Transforms rough intent into structured, high-fidelity prompts with professional terminology and design system context.
|
||||
2. **Design System Synthesis**: Analyzes existing Stitch projects to create and maintain a `.stitch/DESIGN.md` "source of truth".
|
||||
3. **Iterative Generation**: Selects the best generation or editing workflow (`edit_screens`, `generate_variants`) based on user intent.
|
||||
4. **Asset Management**: Synchronizes remote designs by downloading HTML and screenshots to the project's `.stitch/designs` directory.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Stitch MCP Server access
|
||||
- A project `projectId` (can be discovered via `list_projects`)
|
||||
|
||||
## Example Prompt
|
||||
|
||||
```text
|
||||
Design a premium landing page for a mountain resort with a focus on serene luxury and glassmorphism.
|
||||
```
|
||||
|
||||
## Skill Structure
|
||||
|
||||
```
|
||||
stitch-design/
|
||||
├── SKILL.md — Core instructions & Prompt Pipeline
|
||||
├── README.md — This file
|
||||
├── workflows/ — Specialized pipelines (Text-to-UI, Edit, MD)
|
||||
├── references/ — UI/UX keywords & Technical Mappings
|
||||
└── examples/ — Gold-standard references (Solace Mindfulness)
|
||||
```
|
||||
|
||||
## Works With
|
||||
|
||||
- **`react:components` skill**: Hand-off generated designs for frontend implementation.
|
||||
- **`stitch-loop` skill**: Provides the `DESIGN.md` context for autonomous building loops.
|
||||
- **Multi-agent workflows**: Refines prompts before passing design tasks to specialized agents.
|
||||
|
||||
## Learn More
|
||||
|
||||
See [SKILL.md](./SKILL.md) for complete instructions.
|
||||
@@ -1,84 +0,0 @@
|
||||
---
|
||||
name: stitch-design
|
||||
description: Unified entry point for Stitch design work. Handles prompt enhancement (UI/UX keywords, atmosphere), design system synthesis (.stitch/DESIGN.md), and high-fidelity screen generation/editing via Stitch MCP.
|
||||
allowed-tools:
|
||||
- "StitchMCP"
|
||||
- "Read"
|
||||
- "Write"
|
||||
---
|
||||
|
||||
# Stitch Design Expert
|
||||
|
||||
You are an expert Design Systems Lead and Prompt Engineer specializing in the **Stitch MCP server**. Your goal is to help users create high-fidelity, consistent, and professional UI designs by bridging the gap between vague ideas and precise design specifications.
|
||||
|
||||
## Core Responsibilities
|
||||
|
||||
1. **Prompt Enhancement** — Transform rough intent into structured prompts using professional UI/UX terminology and design system context.
|
||||
2. **Design System Synthesis** — Analyze existing Stitch projects to create `.stitch/DESIGN.md` "source of truth" documents.
|
||||
3. **Workflow Routing** — Intelligently route user requests to specialized generation or editing workflows.
|
||||
4. **Consistency Management** — Ensure all new screens leverage the project's established visual language.
|
||||
5. **Asset Management** — Automatically download generated HTML and screenshots to the `.stitch/designs` directory.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Workflows
|
||||
|
||||
Based on the user's request, follow one of these workflows:
|
||||
|
||||
| User Intent | Workflow | Primary Tool |
|
||||
|:---|:---|:---|
|
||||
| "Design a [page]..." | [text-to-design](workflows/text-to-design.md) | `generate_screen_from_text` + `Download` |
|
||||
| "Edit this [screen]..." | [edit-design](workflows/edit-design.md) | `edit_screens` + `Download` |
|
||||
| "Create/Update .stitch/DESIGN.md" | [generate-design-md](workflows/generate-design-md.md) | `get_screen` + `Write` |
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Prompt Enhancement Pipeline
|
||||
|
||||
Before calling any Stitch generation or editing tool, you MUST enhance the user's prompt.
|
||||
|
||||
### 1. Analyze Context
|
||||
- **Project Scope**: Maintain the current `projectId`. Use `list_projects` if unknown.
|
||||
- **Design System**: Check for `.stitch/DESIGN.md`. If it exists, incorporate its tokens (colors, typography). If not, suggest the `generate-design-md` workflow.
|
||||
|
||||
### 2. Refine UI/UX Terminology
|
||||
Consult [Design Mappings](references/design-mappings.md) to replace vague terms.
|
||||
- Vague: "Make a nice header"
|
||||
- Professional: "Sticky navigation bar with glassmorphism effect and centered logo"
|
||||
|
||||
### 3. Structure the Final Prompt
|
||||
Format the enhanced prompt for Stitch like this:
|
||||
|
||||
```markdown
|
||||
[Overall vibe, mood, and purpose of the page]
|
||||
|
||||
**DESIGN SYSTEM (REQUIRED):**
|
||||
- Platform: [Web/Mobile], [Desktop/Mobile]-first
|
||||
- Palette: [Primary Name] (#hex for role), [Secondary Name] (#hex for role)
|
||||
- Styles: [Roundness description], [Shadow/Elevation style]
|
||||
|
||||
**PAGE STRUCTURE:**
|
||||
1. **Header:** [Description of navigation and branding]
|
||||
2. **Hero Section:** [Headline, subtext, and primary CTA]
|
||||
3. **Primary Content Area:** [Detailed component breakdown]
|
||||
4. **Footer:** [Links and copyright information]
|
||||
```
|
||||
|
||||
### 4. Present AI Insights
|
||||
After any tool call, always surface the `outputComponents` (Text Description and Suggestions) to the user.
|
||||
|
||||
---
|
||||
|
||||
## 📚 References
|
||||
|
||||
- [Tool Schemas](references/tool-schemas.md) — How to call Stitch MCP tools.
|
||||
- [Design Mappings](references/design-mappings.md) — UI/UX keywords and atmosphere descriptors.
|
||||
- [Prompting Keywords](references/prompt-keywords.md) — Technical terms Stitch understands best.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
- **Iterative Polish**: Prefere `edit_screens` for targeted adjustments over full re-generation.
|
||||
- **Semantic First**: Name colors by their role (e.g., "Primary Action") as well as their appearance.
|
||||
- **Atmosphere Matters**: Explicitly set the "vibe" (Minimalist, Vibrant, Brutalist) to guide the generator.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
# The "Solace" Design System
|
||||
This is a comprehensive design language for a mindfulness and wellness application.
|
||||
|
||||
## 🎨 Color Palette
|
||||
- **Primary**: Deep Ocean Blue (#1a365d) - for critical navigation and CTAs.
|
||||
- **Secondary**: Calm Slate (#718096) - for subtexts and secondary buttons.
|
||||
- **Background**: Soft Mist (#f7fafc) - for main page content.
|
||||
- **Accent**: Serene Emerald (#38a169) - for positive feedback and completion states.
|
||||
|
||||
## 🔡 Typography
|
||||
- **Heading**: Montserrat, Bold - for titles and hero section headlines.
|
||||
- **Body**: Inter, Regular - for descriptions and general content.
|
||||
- **Size**: 16px as base, 48px for H1.
|
||||
|
||||
## 📐 Components
|
||||
- **Buttons**: Rounded (12px), subtle hover shadow (4px blur).
|
||||
- **Cards**: Minimal border (1px, #e2e8f0), soft shadow (8px elevation).
|
||||
- **Navigation**: Clean, top-aligned, centered menu items.
|
||||
|
||||
---
|
||||
💡 **Tip**: When calling `generate_screen_from_text`, include the tokens above to ensure the new screen matches this design system.
|
||||
@@ -1,28 +0,0 @@
|
||||
# Example: Prompt Enhancement
|
||||
|
||||
## User Input (Vague)
|
||||
> "Make a landing page for my fitness app called 'Pulse'. Use red and black. It should have a place for features and a hero section."
|
||||
|
||||
---
|
||||
|
||||
## Enhanced Prompt (Stitch-Optimized)
|
||||
**Overall Purpose**: A high-energy, modern landing page for 'Pulse', a fitness tracking application. The vibe is powerful, motivating, and high-performance.
|
||||
|
||||
**DESIGN SYSTEM (REQUIRED):**
|
||||
- **Primary Color**: Racing Red (#e11d48) for CTA buttons and active states.
|
||||
- **Secondary Color**: Deep Obsidian (#0f172a) for background and text containers.
|
||||
- **Typography**: Bold, condensed sans-serif headings (e.g., Impact or Inter Tight) to convey speed and strength.
|
||||
- **Style**: Hard edges with sharp, minimal borders; high contrast; dark mode by default.
|
||||
|
||||
**PAGE STRUCTURE:**
|
||||
1. **Header**: Minimalist navigation with Pulse logo (left) and "Start Training" primary button (right).
|
||||
2. **Hero Section**: Large, emotive fitness photography background. Headline: "Elevate Every Beat." Sub-headline: "Track, analyze, and crush your fitness goals with Pulse." Primary red CTA: "Get Started".
|
||||
3. **Feature Grid**: Three-column layout highlighting:
|
||||
- **Real-time Tracking**: Live stats from your wearable.
|
||||
- **AI Coaching**: Personalized workouts based on your performance.
|
||||
- **Community Challenges**: Compete with friends and stay motivated.
|
||||
4. **Social Proof Section**: Subtle slider showing "Trusted by 500,000+ athletes".
|
||||
5. **Footer**: Quick links (Training, Pricing, Support), social icons, and legal.
|
||||
|
||||
---
|
||||
💡 **Tip**: Notice how the enhanced prompt adds specific hex codes, defines the typography "vibe", and breaks the page into a logical numbered structure. This gives Stitch much clearer instructions.
|
||||
@@ -1,76 +0,0 @@
|
||||
# Stitch MCP Tool Schemas
|
||||
|
||||
Use these examples to format your tool calls to the Stitch MCP server correctly.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Project Management
|
||||
|
||||
### `list_projects`
|
||||
Lists all Stitch projects accessible to you.
|
||||
```json
|
||||
// No parameters needed
|
||||
{}
|
||||
```
|
||||
|
||||
### `get_project`
|
||||
Retrieves details of a specific project.
|
||||
```json
|
||||
{
|
||||
"name": "projects/4044680601076201931"
|
||||
}
|
||||
```
|
||||
|
||||
### `create_project`
|
||||
Creates a new Stitch project.
|
||||
```json
|
||||
{
|
||||
"title": "My New App"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Design Generation
|
||||
|
||||
### `generate_screen_from_text`
|
||||
Generates a new screen from a text description.
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931",
|
||||
"prompt": "A modern landing page for a coffee shop with a hero section, menu, and contact form. Use warm brown tones (#4b2c20) and a clean sans-serif font.",
|
||||
"deviceType": "DESKTOP" // Options: MOBILE, DESKTOP, TABLET
|
||||
}
|
||||
```
|
||||
|
||||
### `edit_screens`
|
||||
Edits existing screens with a text prompt.
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931",
|
||||
"selectedScreenIds": ["98b50e2ddc9943efb387052637738f61"],
|
||||
"prompt": "Change the background color to white (#ffffff) and make the call-to-action button larger."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Screen Management
|
||||
|
||||
### `list_screens`
|
||||
Lists all screens within a project.
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931"
|
||||
}
|
||||
```
|
||||
|
||||
### `get_screen`
|
||||
Retrieves details of a specific screen.
|
||||
```json
|
||||
{
|
||||
"projectId": "4044680601076201931",
|
||||
"screenId": "98b50e2ddc9943efb387052637738f61",
|
||||
"name": "projects/4044680601076201931/screens/98b50e2ddc9943efb387052637738f61"
|
||||
}
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
description: Edit an existing design screen using Stitch MCP.
|
||||
---
|
||||
|
||||
# Workflow: Edit-Design
|
||||
|
||||
Make targeted changes to an already generated design.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Identify the Screen
|
||||
Use `list_screens` or `get_screen` to find the correct `projectId` and `screenId`.
|
||||
|
||||
### 2. Formulate the Edit Prompt
|
||||
Be specific about the changes you want to make. Do not just say "fix it".
|
||||
- **Location**: "Change the color of the [primary button] in the [hero section]..."
|
||||
- **Visuals**: "...to a darker blue (#004080) and add a subtle shadow."
|
||||
- **Structure**: "Add a secondary button next to the primary one with the text 'Learn More'."
|
||||
|
||||
### 3. Apply the Edit
|
||||
Call the `mcp_StitchMCP_edit_screens` tool.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"selectedScreenIds": ["..."],
|
||||
"prompt": "[Your target edit prompt]"
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Present AI Feedback
|
||||
Always show the text description and suggestions from `outputComponents` to the user.
|
||||
|
||||
### 5. Download Design Assets
|
||||
After editing, download the updated HTML and screenshot urls from `outputComponents` to the `.stitch/designs` directory, overwriting previous versions to ensure the local files reflect the latest edits.
|
||||
|
||||
### 6. Verify and Repeat
|
||||
- Check the output screen to see if the changes were applied correctly.
|
||||
- If more polish is needed, repeat the process with a new specific prompt.
|
||||
|
||||
## Tips
|
||||
- **Keep it focused**: One edit at a time is often better than a long list of changes.
|
||||
- **Reference components**: Use professional terms like "navigation bar", "hero section", "footer", "card grid".
|
||||
- **Mention colors**: Use hex codes for precise color matching.
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
description: Analyze a Stitch project and synthesize its design system into a .stitch/DESIGN.md file.
|
||||
---
|
||||
|
||||
# Workflow: Generate .stitch/DESIGN.md
|
||||
|
||||
Create a "source of truth" for your project's design language to ensure consistency across all future screens.
|
||||
|
||||
## 📥 Retrieval
|
||||
|
||||
To analyze a Stitch project, you must retrieve metadata and assets using the Stitch MCP tools:
|
||||
|
||||
1. **Project lookup**: Use `list_projects` to find the target `projectId`.
|
||||
2. **Screen lookup**: Use `list_screens` for that `projectId` to find representative screens (e.g., "Home", "Main Dashboard").
|
||||
3. **Metadata fetch**: Call `get_screen` for the target screen to get `screenshot.downloadUrl` and `htmlCode.downloadUrl`.
|
||||
4. **Asset download**: Use `read_url_content` to fetch the HTML code.
|
||||
|
||||
## 🧠 Analysis & Synthesis
|
||||
|
||||
### 1. Identify Identity
|
||||
- Capture Project Title and Project ID.
|
||||
|
||||
### 2. Define Atmosphere
|
||||
- Analyze the HTML and screenshot to capture the "vibe" (e.g., "Airy," "Professional," "Vibrant").
|
||||
|
||||
### 3. Map Color Palette
|
||||
- Extract exact hex codes and assign functional roles (e.g., "Primary Action: #2563eb").
|
||||
|
||||
### 4. Translate Geometry
|
||||
- Convert Tailwind/CSS values into descriptive language (e.g., `rounded-full` → "Pill-shaped").
|
||||
|
||||
### 5. Document Depth
|
||||
- Describe shadow styles and layering (e.g., "Soft, diffused elevation").
|
||||
|
||||
## 📝 Output Structure
|
||||
|
||||
Create a `.stitch/DESIGN.md` file in the project directory with this structure:
|
||||
|
||||
```markdown
|
||||
# Design System: [Project Title]
|
||||
**Project ID:** [Insert Project ID Here]
|
||||
|
||||
## 1. Visual Theme & Atmosphere
|
||||
(Description of mood and aesthetic philosophy)
|
||||
|
||||
## 2. Color Palette & Roles
|
||||
(Descriptive Name + Hex Code + Role)
|
||||
|
||||
## 3. Typography Rules
|
||||
(Font families, weights, and usage)
|
||||
|
||||
## 4. Component Stylings
|
||||
* **Buttons:** Shape, color, behavior
|
||||
* **Containers:** Roundness, elevation
|
||||
|
||||
## 5. Layout Principles
|
||||
(Whitespace strategy and grid alignment)
|
||||
```
|
||||
|
||||
## 💡 Best Practices
|
||||
- **Be Precise**: Always include hex codes in parentheses.
|
||||
- **Be Descriptive**: Use natural language like "Deep Ocean Blue" instead of just "Blue".
|
||||
- **Be Functional**: Explain *why* an element is used.
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
description: Generate new screens from a text prompt using Stitch MCP.
|
||||
---
|
||||
|
||||
# Workflow: Text-to-Design
|
||||
|
||||
Transform a text description into a high-fidelity design screen.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Enhance the User Prompt
|
||||
Before calling the Stitch MCP tool, apply the [Prompt Enhancement Pipeline](../SKILL.md#prompt-enhancement-pipeline).
|
||||
- Identify the platform (Web/Mobile) and page type.
|
||||
- Incorporate any existing project design system from `.stitch/DESIGN.md`.
|
||||
- Use specific [Design Mappings](../references/design-mappings.md) and [Prompting Keywords](../references/prompt-keywords.md).
|
||||
|
||||
### 2. Identify the Project
|
||||
Use `list_projects` to find the correct `projectId` if it is not already known.
|
||||
|
||||
### 3. Generate the Screen
|
||||
Call the `mcp_StitchMCP_generate_screen_from_text` tool with the enhanced prompt.
|
||||
|
||||
```json
|
||||
{
|
||||
"projectId": "...",
|
||||
"prompt": "[Your Enhanced Prompt]",
|
||||
"deviceType": "DESKTOP" // or MOBILE
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Present AI Feedback
|
||||
Always show the text description and suggestions from `outputComponents` to the user.
|
||||
|
||||
### 5. Download Design Assets
|
||||
After generation, download the HTML and screenshot urls from `outputComponents` to the `.stitch/designs` directory.
|
||||
- **Naming**: Use the screen ID or a descriptive slug for the filename.
|
||||
- **Tools**: Use `curl -o` via `run_command` or similar.
|
||||
- **Directory**: Ensure `.stitch/designs` exists.
|
||||
|
||||
### 6. Review and Refine
|
||||
- If the result is not exactly as expected, use the [edit-design](edit-design.md) workflow to make targeted adjustments.
|
||||
- Do NOT re-generate from scratch unless the fundamental layout is wrong.
|
||||
|
||||
## Tips
|
||||
- **Be structural**: Break the page down into header, hero, features, and footer in your prompt.
|
||||
- **Specify colors**: Use hex codes for precision.
|
||||
- **Set the tone**: Explicitly mention if the design should be minimal, professional, or vibrant.
|
||||
Reference in New Issue
Block a user