Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ await client.close();

The client auto-connects on the first `callTool` or `listTools` call. No explicit `connect()` needed.

## Examples

- [Vite + Tailwind v4 Preview App](examples/vite-preview/README.md): Teaches an agent how to convert Stitch designs to Vite + React apps using Tailwind v4.

## API Reference

### `Stitch`
Expand Down
33 changes: 33 additions & 0 deletions packages/sdk/examples/vite-preview/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Vite + Tailwind v4 Preview App Skill

This directory contains an Agent Skill that teaches an AI agent how to integrate a UI design generated by the Stitch SDK into a Vite + React application using Tailwind CSS v4.

## Overview

Stitch generates designs using a CDN version of Tailwind with custom theme configurations embedded in a `<script id="tailwind-config">` tag. Modern frontend development (like Vite) uses build-time Tailwind CSS (version 4 utilizes an `@theme` CSS block rather than a `tailwind.config.js` file).

This skill provides:
1. **`SKILL.md`**: Instructions for an agent on how to scaffold a Vite app, configure Tailwind v4, and map the Stitch HTML and Google Fonts into the application.
2. **`scripts/extract-theme.ts`**: A utility script that parses the Stitch HTML output, extracts the embedded configuration, and converts it into Tailwind v4 `@theme` CSS variables.

## Usage

Agents can read `SKILL.md` to learn how to perform the translation and utilize the `extract-theme.ts` script.

To manually run the theme extractor:

```bash
# Extract the theme from a saved HTML file
bun run scripts/extract-theme.ts path/to/stitch-output.html
```

The output will be a CSS block like:

```css
@theme {
--color-primary: #3b82f6;
--font-sans: "Inter", sans-serif;
}
```

This can be prepended to the Vite app's `index.css` alongside `@import "tailwindcss";`.
40 changes: 40 additions & 0 deletions packages/sdk/examples/vite-preview/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Skill: Vite + Tailwind v4 Preview App

This skill teaches how to convert Stitch's CDN Tailwind config to Tailwind v4 `@theme` syntax and set up a Vite preview app.

## Capabilities

1. **Extract Theme:** Use the provided script to extract the embedded Tailwind configuration from a Stitch HTML file and convert it to a Tailwind v4 `@theme` CSS block.
2. **Scaffold App:** Set up a Vite project with React and `@tailwindcss/vite`.
3. **Integrate Design:** Take the HTML body and Google Fonts links from Stitch and put them into the React app.

## Using the Theme Extractor

To extract the theme from an HTML string or file:

```bash
bun run scripts/extract-theme.ts "path/to/stitch-output.html" > src/theme.css
```

This takes the `tailwind.config` JSON-like object from the HTML and converts fields like `theme.extend.colors` into `@theme { --color-*: *; }` variables for Tailwind v4.

## Manual Steps (for an Agent)

1. Generate a design with Stitch and download the HTML.
2. Initialize Vite: `bun create vite my-preview --template react-ts`
3. Install Tailwind v4: `cd my-preview && bun add tailwindcss @tailwindcss/vite`
4. Configure `vite.config.ts`:
```ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
plugins: [tailwindcss(), react()],
})
```
5. Extract the theme from the HTML using the script in `scripts/extract-theme.ts` and save it to `src/index.css`.
6. Prepend `@import "tailwindcss";` to the top of `src/index.css`.
7. Copy the `<body>` content from the Stitch HTML into `src/App.tsx`, converting it to JSX (e.g., `class` -> `className`, closing self-closing tags).
8. Copy the Google Fonts `<link>` tags from the Stitch HTML `<head>` into `index.html`.
9. Run the app: `bun run dev`
72 changes: 72 additions & 0 deletions packages/sdk/examples/vite-preview/scripts/extract-theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as fs from "fs";

/**
* Extracts a Tailwind v3 config from Stitch HTML and converts it to Tailwind v4 @theme CSS.
*/
function extractTheme(htmlPath: string): string {
const html = fs.readFileSync(htmlPath, "utf-8");
const configMatch = html.match(/<script id="tailwind-config">([\s\S]*?)<\/script>/);

if (!configMatch) {
console.error("No tailwind-config script found in the HTML.");
process.exit(1);
}

const scriptContent = configMatch[1];
const configObjMatch = scriptContent.match(/tailwind\.config\s*=\s*({[\s\S]*?});/);

if (!configObjMatch) {
console.error("Could not parse tailwind.config object.");
process.exit(1);
}

let config: any;
try {
// The config is a JS object literal, not strict JSON. Use a safe eval-like approach or relaxed parser.
// For simplicity in this example, we'll try evaluating it in a sandbox or cleaning it up.
// Since it's from a trusted source (Stitch), eval is a quick way to parse JS object literals.
config = new Function(`return ${configObjMatch[1]}`)();
} catch (e) {
console.error("Failed to parse tailwind config:", e);
process.exit(1);
}

let themeCss = "@theme {\n";

if (config?.theme?.extend?.colors) {
for (const [colorName, colorValue] of Object.entries(config.theme.extend.colors)) {
if (typeof colorValue === "string") {
themeCss += ` --color-${colorName}: ${colorValue};\n`;
} else if (typeof colorValue === "object" && colorValue !== null) {
for (const [shade, hex] of Object.entries(colorValue)) {
themeCss += ` --color-${colorName}-${shade}: ${hex};\n`;
}
}
}
}

if (config?.theme?.extend?.fontFamily) {
for (const [fontName, fontValue] of Object.entries(config.theme.extend.fontFamily)) {
if (typeof fontValue === "string") {
themeCss += ` --font-${fontName}: ${fontValue};\n`;
} else if (Array.isArray(fontValue)) {
themeCss += ` --font-${fontName}: ${fontValue.join(", ")};\n`;
}
}
}

themeCss += "}\n";
return themeCss;
}

if (require.main === module) {
const file = process.argv[2];
if (!file) {
console.error("Usage: bun run extract-theme.ts <path-to-html>");
process.exit(1);
}
const css = extractTheme(file);
console.log(css);
}

export { extractTheme };
Loading