/html-prototype
可交互 HTML 原型,生成浏览器可打开的原型页面供用户查看和选择设计方案
Claude Code
$ npx -y skills add echoVic/boss-skill --skill html-prototype --agent claude-code
Codex
$ npx skills add echoVic/boss-skill --skill html-prototype -a codex -y
Cursor
$ npx skills add echoVic/boss-skill --skill html-prototype -a cursor -y
Gemini CLI
$ npx skills add echoVic/boss-skill --skill html-prototype -a gemini-cli -y
GitHub Copilot
$ npx skills add echoVic/boss-skill --skill html-prototype -a github-copilot -y
OpenCode
$ npx skills add echoVic/boss-skill --skill html-prototype -a opencode -y
Windsurf
$ npx skills add echoVic/boss-skill --skill html-prototype -a windsurf -y
SKILL.md
html-prototype.SKILL.mdname: ui-designer/html-prototype
description: 可交互 HTML 原型,生成浏览器可打开的原型页面供用户查看和选择设计方案
version: 1.0.0
agent: ui-designer
type: workflow
user-invocable: false
agent-invocable: true
dependencies:
- ui-designer/design-system
triggers:
- 用户需要直观看到设计效果而非纯文字描述时
- 存在多个设计变体需要用户对比选择时
- 设计方向存在分歧,需要可视化决策时
metadata:
internal: true
可交互 HTML 原型
适用场景
当设计方案需要用户**直观看到**才能决策时,生成可交互的 HTML 原型页面,在浏览器中展示并让用户点击选择。
**适用**:
- 多设计变体需要用户对比选择
- 布局/视觉方向需要用户确认
- 复杂交互流程需要走查
**不适用**:
- 纯后端/API 项目
- 用户已明确指定设计方向
- 简单页面无需可视化确认
核心流程
1. 征求同意 → 2. 生成原型 HTML → 3. 启动本地服务 → 4. 用户查看/选择 → 5. 读取选择 → 6. 清理
步骤 1:征求同意
在开始设计前,**单独发一条消息**询问用户:
> 接下来的设计方案如果能在浏览器里直观看到,会比纯文字描述更容易决策。我可以生成可交互的 HTML 原型页面,你打开本地链接就能看到效果并点击选择方案。需要开启吗?
**这条消息必须独立发送**,不要和其他内容混在一起。等待用户回复。
- 用户同意 → 继续步骤 2
- 用户拒绝 → 跳过 HTML 原型,用文字描述 + JSON 交付
步骤 2:生成原型 HTML
在 `.boss/<feature>/prototype/` 目录下生成原型文件。
**文件结构**:
.boss/<feature>/prototype/
├── index.html # 主页面:展示所有变体,可点击选择
├── server.cjs # 本地服务脚本(由 Agent 写入)
└── selections.json # 用户选择记录(服务运行时生成)
2.1 写入服务脚本
将以下代码**原样写入** `.boss/<feature>/prototype/server.cjs`:
const http = require('http');
const fs = require('fs');
const path = require('path');
const DIR = __dirname;
const PORT = process.env.PROTO_PORT || 0;
const MIME = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
};
let selections = [];
const selFile = path.join(DIR, 'selections.json');
try {
selections = JSON.parse(fs.readFileSync(selFile, 'utf-8'));
} catch (e) {}
function saveSelections() {
fs.writeFileSync(selFile, JSON.stringify(selections, null, 2));
}
const clients = new Set();
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/select') {
let body = '';
req.on('data', (c) => (body += c));
req.on('end', () => {
try {
const sel = JSON.parse(body);
sel.timestamp = Date.now();
selections.push(sel);
saveSelections();
for (const c of clients) {
try {
c.write(`data: ${JSON.stringify(sel)}\n\n`);
} catch (e) {}
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
} catch (e) {
res.writeHead(400);
res.end('Bad request');
}
});
return;
}
if (req.url === '/selections') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(selections));
return;
}
if (req.url === '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
clients.add(res);
req.on('close', () => clients.delete(res));
return;
}
let relPath = req.url === '/' ? '/index.html' : req.url.split('?')[0];
let filePath = path.join(DIR, relPath);
if (!filePath.startsWith(DIR)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
if (!fs.existsSync(filePath)) {
res.writeHead(404);
res.end('Not found');
return;
}
const ext = path.extname(filePath).toLowerCase();
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
res.end(fs.readFileSync(filePath));
});
server.listen(PORT, '127.0.0.1', () => {
const port = server.address().port;
console.log(JSON.stringify({ url: `http://localhost:${port}`, port }));
});2.2 写入原型页面
写 `index.html`,使用以下骨架。**所有 CSS 内联**,不依赖外部资源。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>设计原型 · <feature></title>
<style>
:root {
--bg: #f8fafc;
--card: #ffffff;
--border: #e2e8f0;
--text: #0f172a;
--muted: #64748b;
--primary: #3b82f6;
--primary-hover: #2563eb;
--success: #10b981;
--radius: 12px;
--shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); padding: 2rem 1rem; }
.container { max-width: 1200px; margin: 0 auto; }
.header { text-align: center; margin-bottom: 2rem; }
.header h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
.header p { color: var(--muted); font-size: 0.95rem; }
.variants { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.variant {
background: var(--card); border: 2px solid var(--border); border-radius: var(--radius);
overflow: hidden; box-shadow: var(--shadow); transition: all 0.2s ease; cursor: pointer;
display: flex; flex-direction: column;
}
.variant:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: var(--primary); }
.variant.selected { border-color: var(--success); box-shadow: 0 0 0 3px rgba(16,185,129,0.2); }
.variant-preview {
background: #f1f5f9; padding: 1.5rem; min-height: 200px;
display: flex; align-items: center; justify-content: center;
}
.variant-body { padding: 1rem 1.25rem; flex: 1; display: flex; flex-direction: column; }
.variant-body h3 { font-size: 1.1rem; margin-bottom: 0.5rem; }
.variant-body .concept { color: var(--muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
.variant-body .tradeoffs { font-size: 0.85rem; margin-bottom: 1rem; }
.variant-body .tradeoffs .pros { color: #059669; }
.variant-body .tradeoffs .cons { color: #dc2626; }
.select-btn {
margin-top: auto; padding: 0.6rem 1rem; background: var(--primary); color: #fff;
border: none; border-radius: 8px; font-size: 0.9rem; font-weight: 500; cursor: pointer;
transition: background 0.15s;
}
.select-bRead more
name: ui-designer/html-prototype description: 可交互 HTML 原型,生成浏览器可打开的原型页面供用户查看和选择设计方案 version: 1.0.0 agent: ui-designer type: workflow user-invocable: false agent-invocable: true dependencies: - ui-designer/design-system triggers: - 用户需要直观看到设计效果而非纯文字描述时 - 存在多个设计变体需要用户对比选择时 - 设计方向存在分歧,需要可视化决策时 metadata: internal: true
可交互 HTML 原型
适用场景
当设计方案需要用户**直观看到**才能决策时,生成可交互的 HTML 原型页面,在浏览器中展示并让用户点击选择。
**适用**:
- 多设计变体需要用户对比选择
- 布局/视觉方向需要用户确认
- 复杂交互流程需要走查
**不适用**:
- 纯后端/API 项目
- 用户已明确指定设计方向
- 简单页面无需可视化确认
核心流程
1. 征求同意 → 2. 生成原型 HTML → 3. 启动本地服务 → 4. 用户查看/选择 → 5. 读取选择 → 6. 清理
步骤 1:征求同意
在开始设计前,**单独发一条消息**询问用户:
> 接下来的设计方案如果能在浏览器里直观看到,会比纯文字描述更容易决策。我可以生成可交互的 HTML 原型页面,你打开本地链接就能看到效果并点击选择方案。需要开启吗?
**这条消息必须独立发送**,不要和其他内容混在一起。等待用户回复。
- 用户同意 → 继续步骤 2
- 用户拒绝 → 跳过 HTML 原型,用文字描述 + JSON 交付
步骤 2:生成原型 HTML
在 `.boss/<feature>/prototype/` 目录下生成原型文件。
**文件结构**:
.boss/<feature>/prototype/ ├── index.html # 主页面:展示所有变体,可点击选择 ├── server.cjs # 本地服务脚本(由 Agent 写入) └── selections.json # 用户选择记录(服务运行时生成)
2.1 写入服务脚本
将以下代码**原样写入** `.boss/<feature>/prototype/server.cjs`:
const http = require('http');
const fs = require('fs');
const path = require('path');
const DIR = __dirname;
const PORT = process.env.PROTO_PORT || 0;
const MIME = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
};
let selections = [];
const selFile = path.join(DIR, 'selections.json');
try {
selections = JSON.parse(fs.readFileSync(selFile, 'utf-8'));
} catch (e) {}
function saveSelections() {
fs.writeFileSync(selFile, JSON.stringify(selections, null, 2));
}
const clients = new Set();
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/select') {
let body = '';
req.on('data', (c) => (body += c));
req.on('end', () => {
try {
const sel = JSON.parse(body);
sel.timestamp = Date.now();
selections.push(sel);
saveSelections();
for (const c of clients) {
try {
c.write(`data: ${JSON.stringify(sel)}\n\n`);
} catch (e) {}
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
} catch (e) {
res.writeHead(400);
res.end('Bad request');
}
});
return;
}
if (req.url === '/selections') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(selections));
return;
}
if (req.url === '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
clients.add(res);
req.on('close', () => clients.delete(res));
return;
}
let relPath = req.url === '/' ? '/index.html' : req.url.split('?')[0];
let filePath = path.join(DIR, relPath);
if (!filePath.startsWith(DIR)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
if (!fs.existsSync(filePath)) {
res.writeHead(404);
res.end('Not found');
return;
}
const ext = path.extname(filePath).toLowerCase();
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
res.end(fs.readFileSync(filePath));
});
server.listen(PORT, '127.0.0.1', () => {
const port = server.address().port;
console.log(JSON.stringify({ url: `http://localhost:${port}`, port }));
});2.2 写入原型页面
写 `index.html`,使用以下骨架。**所有 CSS 内联**,不依赖外部资源。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>设计原型 · <feature></title>
<style>
:root {
--bg: #f8fafc;
--card: #ffffff;
--border: #e2e8f0;
--text: #0f172a;
--muted: #64748b;
--primary: #3b82f6;
--primary-hover: #2563eb;
--success: #10b981;
--radius: 12px;
--shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); padding: 2rem 1rem; }
.container { max-width: 1200px; margin: 0 auto; }
.header { text-align: center; margin-bottom: 2rem; }
.header h1 { font-size: 1.5rem; margin-bottom: 0.5rem; }
.header p { color: var(--muted); font-size: 0.95rem; }
.variants { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.variant {
background: var(--card); border: 2px solid var(--border); border-radius: var(--radius);
overflow: hidden; box-shadow: var(--shadow); transition: all 0.2s ease; cursor: pointer;
display: flex; flex-direction: column;
}
.variant:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: var(--primary); }
.variant.selected { border-color: var(--success); box-shadow: 0 0 0 3px rgba(16,185,129,0.2); }
.variant-preview {
background: #f1f5f9; padding: 1.5rem; min-height: 200px;
display: flex; align-items: center; justify-content: center;
}
.variant-body { padding: 1rem 1.25rem; flex: 1; display: flex; flex-direction: column; }
.variant-body h3 { font-size: 1.1rem; margin-bottom: 0.5rem; }
.variant-body .concept { color: var(--muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
.variant-body .tradeoffs { font-size: 0.85rem; margin-bottom: 1rem; }
.variant-body .tradeoffs .pros { color: #059669; }
.variant-body .tradeoffs .cons { color: #dc2626; }
.select-btn {
margin-top: auto; padding: 0.6rem 1rem; background: var(--primary); color: #fff;
border: none; border-radius: 8px; font-size: 0.9rem; font-weight: 500; cursor: pointer;
transition: background 0.15s;
}
.select-bLanguages / 语言 / 言語 / 언어 / Idiomas / Langues: English · 中文 · 日本語 · 한국어 · Español · Français · Português This project is part of the AtomGit G-Star Incubation Program.
Other skills on boss.
brainstorming
你是 Boss 的前置环节。用户通常只会丢过来一句话——可能是"帮我做个 XX"或者"我想搞个 XX"。你的工作是把这句话**翻译成下游流水线能跑起来的需求**。
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
accessibility
Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…
compiler-commit
Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…
Questions about html-prototype.
- Which coding agents can use html-prototype?
- html-prototype installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

