html-prototype
Skill4 starsChanged 6 months agoMITView on GitHub
Generate HTML prototypes (single-file, browser-openable) for product mockups using the user's design system — fonts, color scales, radii, shadows, components, all driven by a tokens.css file. Use when the user asks to make a 原型 / mockup / HTML 设计图 / 设计图给前端 for a PRD module, OR redesign an existing prototype. Output goes to the PRD module's `prototype/` subfolder.
4 other projects use a version of this.
Adopted by
Most recently taken up by
- alen-hh/agent-skillstheir own version, first seen Oct 15, 2026
- qiulin/skillstheir own version, first seen Oct 15, 2026
- plannotator/effective-htmltheir own version, first seen Jun 13, 2026
- kimmihyeon12/skillstheir own version, first seen Mar 10, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- plannotator/effective-html★ 3.8ksame name
- alen-hh/agent-skills★ 0same name
- kimmihyeon12/skills★ 0same name
- qiulin/skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository6 other files it gives its agents.
- competitor-researchSkill
- daily-researchSkill
- feature-acceptanceSkill
- prdSkill
- research-reportSkill
- ui-walkthroughSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
26 rules. 0 are common, 16 are distinctive, and 10 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
16
Distinctive
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 20
Always save to <PRD-module>/prototype/<page-name>.html, beside the PRD .md file.
- Very specific: line 32
After page load, call lucide.createIcons() to render icons.
- Distinctive: line 61
If the product supports Chinese, put CJK font FIRST so Chinese characters render natively while Latin characters fall through to the Latin …
- Distinctive: line 67
Don't reverse this order — Latin glyphs in PingFang look thin / weak.
- Very specific: line 114
Use the -500 step as the canonical hue.
- Distinctive: line 124
Don't mix bg of one with fg of another.
- Very specific: line 128
Use --radius-md for cards / buttons / inputs.
- Distinctive: line 242
When the UI is showing AI behavior — algorithm info panels, "AI suggested" labels, generation indicators, agent reasoning traces — use the …
- Distinctive: line 250
Don't use the AI scale for non-AI things.
- Very specific: line 348
Default: transparent bg, color #3f3f46, icon #71717a.
- Very specific: line 363
When a list row has more than two operations, the first operation stays inline as a ghost button.
- Distinctive: line 394
When the page is a config / editor:
- Distinctive: line 405
Search + filter row (no sort dropdown unless explicitly required).
- Very specific: line 451
Every clickable is wired: every <button>, onclick=, nav-item, action-column link, dropdown menu item, tab, checkbox group must trigger …
- Distinctive: line 453
When you find gaps, fix them before writing the summary.
- Distinctive: line 458
When the user says "ugly" / "weird color" / "the active state is wrong":
- Distinctive: line 460
Don't defend. Identify the specific element.
- Distinctive: line 463
Avoid full rewrites — surgical edits via Edit tool.
- Distinctive: line 465
When they share a reference screenshot:
- Distinctive: line 467
Look at: spacing density, radii, color usage, font weight, icon style.
- Distinctive: line 468
Don't blindly copy the reference's color palette if it conflicts with your tokens (the prototype is your product, not the reference brand).
- Very specific: line 492
If using cursor-pointer class anywhere, that element must have onclick.
- Distinctive: line 507
If any list violates one of these, treat it as a bug — fix even when the user only asked about something else.
- Very specific: line 519
Use as <input class="input input-search pl-10"> with a left-positioned Lucide search icon.
- Very specific: line 558
Use shadcn segmented tabs (.cfg-tabs + .cfg-tab-h) for two scenarios — they share the exact same control:
- Distinctive: line 628
When a config page has a long, tab-grouped verification list (publish check, lint, dependency check, etc.), don't pin it to the left rail — …
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
HTML Prototype Generation
Generate single-file HTML prototypes that can be opened directly in browser, look like real products, and align with the user's design system.
Important: this skill expects a
references/example-tokens.cssfile with the user's design tokens (colors, fonts, radii, shadows, etc.). Replace it with your own company's tokens before using this skill in production, otherwise all prototypes will share the example brand colors.When to use
2.1. User asks for: 原型 / mockup / 设计图 / HTML 给前端 / 给前端看的页面。 2.2. User asks to redesign an existing prototype to match a reference design. 2.3. Output is referenced from PRD
.mdfiles viafile:///link or[name](prototype/foo.html)markdown link.Output location
Always save to
<PRD-module>/prototype/<page-name>.html, beside the PRD.mdfile. Create theprototype/subdirectory if missing.Naming:
<module>-<view>.html(e.g.agent-management.html,reception-detail.html,knowledge-list.html). One file per module is preferred — use page-switching JS to handle multiple states (list / detail / modal). Multiple files only when modules are unrelated.Tech stack (always these three CDNs, no build)
<script src="https://cdn.tailwindcss.com"></script> <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> <link href="https://fonts.googleapis.com/css2?family=YourFontHere:wght@400;500;600;700&display=swap" rel="stylesheet">After page load, call
lucide.createIcons()to render icons.Inline the canonical tokens file
Always copy the full contents of references/example-tokens.css (or whatever the user has replaced it with) into the prototype's
<style>block at the top. This gives you all CSS variables (--neutral-*, brand scales, semantic tokens, type, spacing, radii, shadows) plus baseline typography.Then layer prototype-specific component CSS below it, referencing tokens via
var(--token)rather than hardcoded hex.Example skeleton:
<style> /* PASTE ENTIRE references/example-tokens.css HERE */ :root { ... } html, body { font-family: var(--font-sans); ... } h1, h2, h3 ... /* Then prototype-specific components */ .btn { background: var(--primary); color: var(--primary-foreground); border-radius: var(--radius-md); ... } .card { background: var(--card); border: var(--border-100) solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); ... } </style>Design System Application
The actual color values, font names, radii, etc. live in
references/example-tokens.css. This section is about how to apply them, not what the specific values are.6.1. Fonts (order matters for CJK + Latin)
If the product supports Chinese, put CJK font FIRST so Chinese characters render natively while Latin characters fall through to the Latin sans:
"PingFang SC", "Geist", "Inter", system-ui, -apple-system, sans-serifDon't reverse this order — Latin glyphs in PingFang look thin / weak. Adjust based on your tokens file.
Role Typical family When Sans (default) CJK + Latin sans All UI, body, headings Mono Monospace (e.g. B612 Mono / JetBrains Mono) IDs, code, numbers in tables Serif Optional display Marketing / hero only Body: 16px / 24px line-height (
--text-base / --lh-base). Usevar(--text-sm)(14/20) for compact UI like sidebars or table cells.6.2. Tailwind CDN preflight conflict
Tailwind CDN injects
@layer base { h1, h2, h3, h4 { font-size: inherit; font-weight: inherit; } }AFTER your<style>block. This kills semantic heading styles unless you use!importanton the base typography rules:h1,.h1 { font-size: var(--text-2xl) !important; ... } h2,.h2 { font-size: var(--text-xl) !important; ... }Without
!important, your<h1>will render at body-text size and your design will look broken. Test by inspecting an<h1>'s computed font-size before declaring done.6.3. In-app heading scale (compact, NOT marketing scale)
Marketing scales (h1=36px / h2=30px / h3=24px / h4=20px) are too big for in-app surfaces (list pages, detail pages, modals). Use a tighter scale:
Token In-app size Use for h122pxboldPage title h218pxsemiboldTab content title h315pxsemiboldSection heading h413pxsemiboldSub-section / form group label Form controls (
.input/.btn) keep their original heights and radii — only the inner text drops to 13px so the visual proportions don't collapse.For marketing / hero blocks, override locally with
text-4xl,text-5xletc.6.4. Accent scales
Most tokens files define multiple accent scales. Typical mapping:
Scale Use when --neutral-*90% of UI: text, surfaces, borders --secondary-*Brand callouts, hero CTA, primary illustrations (sparing) --tertiary-*AI / agent contexts: algo info, AI suggestions, generation --quaternary-*Supporting accent: data viz, secondary info, third option Use the
-500step as the canonical hue;-50/100for subtle bg fills;-700+for high-contrast text or pressed states.6.5. Status colors
Each alert ships paired bg + fg tokens:
background: var(--success-bg); color: var(--success-fg); /* and warning / error / info */Don't mix bg of one with fg of another.
6.6. Radii
Use
--radius-mdfor cards / buttons / inputs. Whatever value your tokens file picks (8px shadcn default, 24px round style, etc.), use it consistently — don't mix multiple radii arbitrarily.6.7. Borders
Three weights via
--border-100(1px) /--border-200(1.33px) /--border-300(2px). Default border colorvar(--border), inputvar(--input). Pair with--shadow-sm, never heavy drop-shadow.6.8. Shadows
5-step layered (
--shadow-xs→--shadow-xl). Each = ambient + projection. Use--shadow-smfor cards,--shadow-lgfor popovers,--shadow-xlfor modals.6.9. Hover / press / focus
- Solid button hover: stack
var(--opacity-90)overlay —box-shadow: inset 0 0 0 999px var(--opacity-90). - Outline / ghost hover: swap to
var(--accent). - Press: deepen one shade; no scale transform.
- Focus ring:
box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 25%, transparent).
6.10. Animation
Fades and slides only. 150–200ms ease-out. No bounces. Loaders use Lucide
loader-2self-spinning.6.11. Icons
- Lucide only via CDN.
- Stroke 1.5–2px.
- 16×16 inline in buttons, 24×24 standalone.
- Never use Unicode characters or emoji as icons. Reserve emoji for user-generated content.
- Solid button hover: stack
Component patterns (battle-tested, all token-based)
7.1. Button
.btn{display:inline-flex;align-items:center;gap:6px;padding:0 var(--space-4);height:36px; border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:var(--weight-medium); cursor:pointer;transition:background .15s;border:var(--border-100) solid transparent} .btn-primary{background:var(--primary);color:var(--primary-foreground)} .btn-primary:hover{box-shadow:inset 0 0 0 999px var(--opacity-90)} .btn-brand{background:var(--secondary-500);color:#fff} .btn-brand:hover{box-shadow:inset 0 0 0 999px var(--opacity-90)} .btn-brand:active{background:var(--secondary-700)} .btn-ai{background:var(--tertiary-500);color:#fff} .btn-ai:hover{box-shadow:inset 0 0 0 999px var(--opacity-90)} .btn-outline{background:var(--background);border-color:var(--border);color:var(--foreground)} .btn-outline:hover{background:var(--accent)} .btn-ghost{color:var(--foreground);background:transparent} .btn-ghost:hover{background:var(--accent)} .btn-icon{width:36px;height:36px;padding:0;border-radius:var(--radius-md)} .btn-sm{height:32px;padding:0 var(--space-3);font-size:var(--text-sm)} .btn:focus-visible{outline:none;box-shadow:0 0 0 3px #6a728240}7.2. Input
.input{width:100%;padding:0 var(--space-4);height:40px; border:var(--border-200) solid var(--input);border-radius:var(--radius-md); font-size:var(--text-sm);background:var(--background);color:var(--foreground); transition:border-color .15s,box-shadow .15s} .input:focus{outline:none;border-color:var(--foreground);box-shadow:0 0 0 3px #6a728240} .input::placeholder{color:var(--neutral-400)} .textarea{min-height:96px;padding:var(--space-3) var(--space-4);line-height:1.6; border-radius:var(--radius-sm);resize:vertical}7.3. Card
.card{background:var(--card);border:var(--border-100) solid var(--border); border-radius:var(--radius-md);padding:var(--space-6);box-shadow:var(--shadow-sm)}7.4. Badge (status pills)
.badge{display:inline-flex;align-items:center;gap:4px;padding:2px var(--space-3); height:22px;border-radius:var(--radius-full);font-size:var(--text-xs); font-weight:var(--weight-medium);background:var(--neutral-50);color:var(--neutral-500); border:var(--border-100) solid var(--border)} .badge-success{background:var(--success-bg);color:var(--success-fg);border-color:transparent} .badge-warning{background:var(--warning-bg);color:var(--warning-fg);border-color:transparent} .badge-error {background:var(--error-bg); color:var(--error-fg); border-color:transparent} .badge-info {background:var(--info-bg); color:var(--info-fg); border-color:transparent} .badge-ai {background:var(--tertiary-50);color:var(--tertiary-700);border-color:var(--tertiary-200)} .badge-brand {background:var(--secondary-50);color:var(--secondary-700);border-color:var(--secondary-200)}7.5. Status dot pattern
<span class="badge badge-success"><span class="dot dot-success"></span>已发布</span>.dot{width:6px;height:6px;border-radius:50%;display:inline-block} .dot-success{background:var(--success-fg)} .dot-warning{background:var(--warning-fg)} .dot-error {background:var(--error-fg)} .dot-info {background:var(--info-fg)}7.6. Avatar (single neutral tone, never rainbow)
.avatar{display:inline-flex;align-items:center;justify-content:center; background:var(--neutral-950);color:var(--background); font-weight:var(--weight-semibold);border-radius:var(--radius-sm)} .avatar-lg{width:52px;height:52px;font-size:var(--text-lg);border-radius:14px} .avatar-md{width:36px;height:36px;font-size:var(--text-sm);border-radius:var(--radius-sm)} .avatar-sm{width:24px;height:24px;font-size:var(--text-xs);border-radius:8px}7.7. AI / Agent affordances (use tertiary scale)
When the UI is showing AI behavior — algorithm info panels, "AI suggested" labels, generation indicators, agent reasoning traces — use the tertiary scale:
.ai-panel{background:var(--tertiary-50);border:var(--border-100) solid var(--tertiary-200); border-radius:var(--radius-md);padding:var(--space-4)} .ai-label{color:var(--tertiary-700);font-size:var(--text-xs);font-weight:var(--weight-semibold)}Don't use the AI scale for non-AI things. Don't use the brand scale for AI things. Keep the semantic mapping crisp.
7.8. Help icon (ⓘ) tooltip — must be complete, never empty
任何指标 / 字段 / 配置项旁边的 ⓘ 帮助图标,title 不能为空、不能只写零碎补充语。每个 ⓘ tooltip 必须自包含、能让运营单独读懂这个指标。
必含内容(按优先级排序):
- 计算公式(指标类必填):
{指标名} = {分子} / {分母} × 100%。 - 业务含义(一句话说明这指标看什么 / 反映了什么问题)。
- 适用范围(如有限制:
仅 XX 模式 适用)。 - 特殊计数 / 守恒约束(如有:
独立计数,N 项率相加可能 ≥ 100%/一个 X 只计 1 次)。 - 环比方向(如有:
上升为正向(绿色 ↑)/上升为负向(红色 ↑)——通常说明 XX)。
格式:每条用
换行,HTML title 属性原生支持多行展示。反例(信息残缺):
<i data-lucide="info" title="上升为负向"></i> <!-- 缺公式、缺业务含义 --> <i data-lucide="info"></i> <!-- 完全空 --> <i data-lucide="info" title="仅 XX 模式"></i> <!-- 只有适用范围 -->正例:
<i data-lucide="info" title="动作执行率 = 动作执行数 / 资源回答数 × 100% 调用动作(含 MCP Tool 调用 + FC 调用两类)后调用大模型回答。 仅 自主决策模式 适用。 与其他细分维度独立计数,5 项率相加可能 ≥ 100%。 上升为正向(绿色 ↑)——说明机器人在用工具补全信息。"></i>自检清单(生成原型后过一遍):
- 每个 ⓘ 都有 title 属性?
- 每个 title 包含公式 + 业务含义 + 适用范围(如有)?
- 业务零碎补充语(如"上升为负向")从未单独出现,必须配合公式同时给?
- 多行用
而非\n或省略换行?
- 计算公式(指标类必填):
Color usage rules (HARD)
Hierarchy of accents:
- Neutral dark — primary actions, current location / navigation, emphasis.
- Brand secondary — brand callout, hero CTA, illustration accents (use SPARINGLY).
- AI tertiary — AI-specific contexts (algorithm info, AI suggestions, agent reasoning).
- Pastel semantic — only for status (success / warning / error / info) badges and banners.
- Neutral grays — everything else.
What NOT to do:
- ✗ Random multicolor avatars (blue / purple / green / orange) — use single dark tone.
- ✗ Type-coded badges in 4 colors (blue / purple / amber / green) — use neutral with one accent for primary status.
- ✗ Cream / beige for navigation active state — use the neutral dark.
- ✗ Cream / beige for status indicators — use neutral or pastel-semantic.
- ✗ Emoji as icons — Lucide only.
- ✗ Multiple shades of same hue competing for attention.
Selection state mapping:
Context Treatment Sidebar nav active Light gray bg + dark text + medium font weight (shadcn sidebar style — see App shell) Tab switcher active Neutral dark bg, white text (or white pill on muted track) Radio card selected Light bg + dark border (only when user actively chose) Currently editing item Subtle muted bg Reference / state indicator Tiny gray icon + gray text, NOT colored badge Layout patterns
9.1. Full viewport (HARD rule, no max-width)
The page must always fill the entire viewport on any screen size. Never cap content width with
max-width/max-w-[Xpx]— large screens (1920+) will look "small and lonely" with content centered and 400px of empty gray on each side.- Root container:
width: 100vw; height: 100vh; padding: 32px. - Inside:
flexrow — sidebar fixed 240px, mainflex: 1filling remainder. - Main content vertically:
flex: 1so it stretches the full viewport height. - Tables / scrollable lists:
flex: 1+overflow-y: auto— data overflows internally, never leaves bottom whitespace when data is sparse.
9.2. App shell (sidebar + main) — shadcn-style sidebar
The sidebar follows shadcn/ui Sidebar visual spec — a single flat list of menu buttons grouped under non-clickable text labels. Replaces the old expand / collapse two-level nav; do not use
.nav-item/.nav-child/.nav-childrenpatterns anymore.Why pure HTML + CSS rather than shadcn React components: prototypes are single-file HTML opened in a browser, no React runtime. We replicate shadcn Sidebar's visual rules (colors, sizes, spacing, states) using plain CSS so the implementation team can swap in real
<Sidebar>/<SidebarMenuButton>later.Shell (
.sb-shell): width 240px exactly,flex-shrink: 0,display: flex; flex-direction: column, font-size 13px baseline. Background#fafafa(≈bg-sidebartoken). Right border1px solid var(--border).Header (
.sb-header): 64px tall (matches the page topbar height so the two bottom borders align as a single line across the screen),padding: 0 12px,gap: 10px,box-sizing: border-box, bottom 1px border. Single row, never two-line brand.Group label (
.sb-group-label) — pure text section header, NOT clickable, no icon, no chevron:- 11px, font-weight 500, color
rgba(63,63,70,.6)(≈text-sidebar-foreground/60). padding: 6px 8px,line-height: 1.- Sits above its
<ul class="sb-menu">. - ⚠️ GroupLabel left padding MUST match MenuButton left padding (8px) so the section header text and the icon column of menu buttons share the same left baseline.
Menu button (
.sb-menu-btn):- Height 32px,
border-radius: 6px,padding: 0 8px,gap: 8px. - Lucide icon 16px (left), text 13px (middle, truncate), optional badge (right).
- Default: transparent bg, color
#3f3f46, icon#71717a. - Hover: bg
#f4f4f5, color#18181b, icon#18181b. - Active: bg
var(--quaternary-50), text + iconvar(--quaternary-500),font-weight: 500— NOT the gray hover bg. The active item reads as a soft brand-color affordance, not a hover holdover.
Sub-menu (third-level) — for parents with grandchildren:
<li class="sb-menu-li">wraps the parent MenuButton + the<ul class="sb-menu-sub">.- Parent MenuButton ends with
<i class="sb-menu-caret" data-lucide="chevron-down">; toggle by adding / removing.collapsedon the.sb-menu-li. .sb-menu-sub:list-style: none; margin: 2px 0 2px 16px; border-left: 1px solid var(--border).- 默认收起:
<li class="sb-menu-li collapsed">— 带子菜单的父项默认折叠,用户点击 chevron 才展开。
Footer (
.sb-footer):padding: 8px, top 1px border. Avatar 24×24 dark square + name (12px medium) + email (11px muted) + Lucidechevrons-up-down14px caret. Hover bg#f4f4f5.9.3. Dropdown menu
When a list row has more than two operations, the first operation stays inline as a ghost button; the rest collapse into a
more-horizontal(⋮) icon button that opens a.menu. Required structure:<div class="row-menu-wrap"><!-- position: relative --> <button class="btn btn-ghost btn-sm btn-icon" onclick="toggleRowMenu(this,event)">⋮</button> <div class="menu" style="right:0; top: calc(100% + 4px)"> <div class="menu-item"><i data-lucide="copy"></i>复制</div> <div class="menu-item danger"><i data-lucide="trash-2"></i>删除</div> </div> </div>.menu{position:absolute;background:var(--card);border:var(--border-100) solid var(--border); border-radius:var(--radius-sm);box-shadow:var(--shadow-lg);padding:4px;min-width:140px; z-index:50;display:none} .menu.open{display:block} .menu-item{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:var(--radius-xs); font-size:14px;color:var(--foreground);cursor:pointer} .menu-item:hover{background:var(--accent)} .menu-item.danger{color:var(--error-fg)} .menu-item.danger:hover{background:var(--error-bg)} .menu-item .lucide{width:14px;height:14px;color:var(--neutral-500)} .menu-item.danger .lucide{color:var(--error-fg)} .menu-divider{height:1px;background:var(--border);margin:4px 0}Behavior: clicking ⋮ toggles
.open; clicking outside closes it; only one menu open at a time. Don't fall back to "show all 3 buttons inline" — that's the legacy pattern.9.4. Detail page (left tabs + main + floating debug)
When the page is a config / editor:
- Top bar: back link + avatar + title + status badge + ID + "last saved" line + actions (right).
- Left sub-sidebar: vertical tabs + 发布检查 / checklist panel below.
- Main: content area fills remaining width (no max-width), 32px padding.
- Right-side debug panel: per requirements doc; if user wants persistent, dock right-side; else FAB + drawer.
9.5. List page
- H1 + subtitle (bigger entry feel).
- Tab filter row with counts:
全部 8 / 已发布 5 / 草稿 3. - Search + filter row (no sort dropdown unless explicitly required).
- Table fills full width of main area, no card wrapper — table border + header bg already provides visual containment.
- Hover row: subtle muted bg.
9.6. Table component (fixed spec, NOT tokens)
These are designer hard values — do not substitute with tokens like
var(--space-3). Use the literal px:Element Value Header height 40pxHeader background #F9FAFBHeader bottom border 1px solid #F3F4F6Header corner radius matches --radius-md(top-left + top-right of first / last<th>)Header text text-xs(12px),var(--muted-foreground),font-weight: 500Row height 72px(generous, not cramped)Row separator 1px solid #F3F4F6Row text color var(--foreground)Status badge height 20px, radius--radius-md, padding2px 8px, font12px; bg = pastel by category, no borderAction column buttons All Ghost style ( btn-ghost), no outline borders. Left-aligned (see Action column alignment section). Wrap multiple buttons ininline-flex gap-1Action column icon size 16px9.7. Modal
- Width 480–560px,
border-radiusmatches--radius-lg(large),shadow-xl. - Header (title + small description) → body → footer (right-aligned actions).
- Root container:
Copy / voice rules
10.1. Sentence case for everything except proper nouns / TitleCase identifiers. 10.2. "You" addresses user directly; never "we" / "our" / "let's". 10.3. English first, CN secondary; both at same font size (for products with mixed audience). 10.4. Two sentences max for descriptions: define → "Use ..." imperative. 10.5. No emoji in chrome (labels, buttons, nav). Reserve for user-generated content. 10.6. No internal-rule annotations leaking into the UI. The prototype is a product view, not a PRD spec sheet. Any line that describes why a behavior is designed this way belongs in the PRD doc, NOT in a
.subsubtitle,.hint, or trailing.muted smallnote. Heuristic: if you can lift the sentence verbatim into the PRD's 操作逻辑 section and it reads naturally, it doesn't belong in the prototype. Hints / banners are only for operator-facing in-task guidance.Generation workflow
11.1. Read the PRD first. Look for "原型示意" sections, field lists, operation logic. Don't invent fields not in the PRD. 11.2. Plan pages / states — list / detail / modal / specific tab states. One HTML file with page-switch JS unless the user asks otherwise. 11.3. Reuse the existing prototype style if there's already one in the same module's
prototype/folder. Don't invent a third style. 11.4. Brief the user on what's covered before generating: pages, states, design choices. 11.5. Generate single-file HTML with all CSS inline, all JS inline. 11.6. Open in browser withopen <path>on macOS so the user can see it immediately. 11.7. Self-verify before reporting done — this is mandatory, not optional: 1. PRD coverage: walk the PRD's 功能清单 line by line; every listed feature must have a matching entry-point in the prototype (page / button / modal). List ✓ / ✗ in the summary. 2. Every clickable is wired: every<button>,onclick=, nav-item, action-column link, dropdown menu item, tab, checkbox group must trigger something — open a modal, switch a page, swap state, or at minimum atoast()placeholder. No deadonclick=""or visual-only buttons that look interactive but do nothing. 3. Click behavior matches PRD: cross-check that what happens on click matches the PRD's "操作逻辑" — second-confirmation copy, error message text, modal that opens, follow-up state, etc. 4. When you find gaps, fix them before writing the summary. Don't surface the gaps in the summary as TODOs and stop. 11.8. End with a short summary: pages covered, design choices, what's still open.Iteration etiquette
When the user says "ugly" / "weird color" / "the active state is wrong":
- Don't defend. Identify the specific element.
- Reference the color usage rules above to decide the right replacement.
- Tell them what changed and why (one or two lines, not paragraphs).
- Avoid full rewrites — surgical edits via Edit tool.
When they share a reference screenshot:
- Look at: spacing density, radii, color usage, font weight, icon style.
- Don't blindly copy the reference's color palette if it conflicts with your tokens (the prototype is your product, not the reference brand).
- Adopt: layout patterns, component shapes, hierarchy. NOT necessarily color.
Interactivity (HARD)
Every visually-clickable element must actually be clickable. Dead visual elements (a styled button with no
onclick) feel broken and undermine the prototype's credibility.Rules:
13.1. Buttons (
.btn,.btn-icon,.btn-ghost,.btn-outline, etc.): always haveonclick. Useonclick="toast('...')"as a placeholder if the destination doesn't exist yet. 13.2. Table rows withcursor-pointer: always haveonclick="openX(...)"on the<tr>; secondary buttons inside the row needonclick="event.stopPropagation()"on their wrapping<td>(or per-button) so the row click doesn't fire when clicking a sub-action. 13.3. Dropdown items, menu links, chips, tabs, segmented controls: always haveonclick. A purely decorative chip should not look like a button (no hover, no cursor change). 13.4. Icons used as actions (e.g.,<i data-lucide="more-horizontal">inside a.btn): the parent button takes theonclick, not the<i>itself. 13.5. Toggles / switches / checkboxes: at minimum,onclick="this.classList.toggle('on')"so the visual state changes. 13.6. Avatar / link / hover-able cards: if hover styling implies it's clickable, give itonclick; if it isn't clickable, drop the hover styling. 13.7. Tab filters in a list: clicking should at minimum re-render the list count or toggle visible rows; never leave them as static. 13.8. Modal triggers (open / close / cancel / confirm): all four states must work — open, close (×), cancel button, confirm button. Confirm should at least show atoastand close.Self-check before declaring a prototype ready:
- Click every button at least once. Each one must do something (navigate, open modal, toggle state, or show toast).
- Click every table row in the list. Should open the detail view.
- Open every modal and try every button inside it (close, cancel, primary action).
- Open every dropdown and click each item.
- If using
cursor-pointerclass anywhere, that element must haveonclick.
List rules — apply to EVERY list-style table
Mandatory checklist whenever you create or modify a list:
14.1. Container:
.table-wrap(overflow: auto+ border +--radius-md) — never wrap a list table in a generic<div class="border rounded">that lacks horizontal scroll. 14.2. Per-columnmin-widthinline on each<th>— never hardcode a table-wide min-width. 14.3. First column.sticky-l, action column.sticky-r: applied on<th>in thead and on every<td>in tbody. 14.4. Pagination → always the shadcn Data Table style Detailed Pagination:已选 X / Y 行(left) +每页 [select]+第 N / M 页+ 4 round icon buttons«‹›»(right). Round buttons, not square. Always include the rows-per-page selector. Never just共 N 条text. No variants across the product. 14.5. Action column = LEFT-aligned, not right-aligned. With.sticky-rit already hugs the right edge; left-aligning the cell content keeps the operation buttons close to the column header. 14.6. Search input above the table =.input-search(36px), not the global 40px.input. 14.7. Hover state on sticky cells must re-apply--neutral-50, see Responsive table pitfalls. 14.8. Filter tabs (e.g. 全部 / 已发布 / 草稿) =.cfg-tab-hinside.cfg-tabs— the same shadcn segmented control used on detail-page top tabs. Do NOT use the underline-style.tab-filter.If any list violates one of these, treat it as a bug — fix even when the user only asked about something else.
List page chrome (search + pagination)
15.1. Search input — list page filter row
The list-page search input is 36px tall with a 1px border (lighter than the global 40px
.input). Reason: shadcn "With Icon" input scale; full-height 40px feels too dense in a filter row..input-search { height: 36px; border-width: 1px; border-color: #D1D5DC }Use as
<input class="input input-search pl-10">with a left-positioned Lucide search icon. Don't override.inputglobally.15.2. Detailed Pagination (shadcn Data Table style)
Three-block footer with the canonical shadcn data-table layout:
- Left:
已选 <strong>X</strong> / <strong>Y</strong> 行— text-sm muted; X/Y in mono font medium weight. - Right cluster (flex, gap 24px):
每页label +<select class="pgn-select">(10 / 20 / 50 / 100), 32px tall, mono font, custom chevron-down arrow.第 <strong>N</strong> / <strong>M</strong> 页— text-sm, mono font on N/M.- 4 round 32×32 icon buttons in a row (gap 4px):
chevrons-left/chevron-left/chevron-right/chevrons-right,border-radius: var(--radius-full), 1px border, ghost when enabled, neutral-300 when disabled.
<div class="pagination"> <div class="pagination-meta">已选 <strong>0</strong> / <strong>8</strong> 行</div> <div class="pagination-ctrl"> <div class="pagination-rows"> <span>每页</span> <select class="pgn-select"><option>10</option><option>20</option><option>50</option><option>100</option></select> </div> <div class="pagination-page">第 <strong>1</strong> / <strong>1</strong> 页</div> <div class="pagination-nav"> <button class="pgn-icon" disabled><i data-lucide="chevrons-left"></i></button> <button class="pgn-icon" disabled><i data-lucide="chevron-left"></i></button> <button class="pgn-icon" disabled><i data-lucide="chevron-right"></i></button> <button class="pgn-icon" disabled><i data-lucide="chevrons-right"></i></button> </div> </div> </div>⛔ Don't use simpler patterns instead:
- ❌
共 N 条text only (no controls). - ❌ prev / number / next buttons without rows-per-page selector.
- ❌ square buttons or pill buttons — must be round (
--radius-full) icon-only.
- Left:
Top segmented tabs (in-page Tabs replacement)
Use shadcn segmented tabs (
.cfg-tabs+.cfg-tab-h) for two scenarios — they share the exact same control:16.1. Detail-page top tabs. Replaces left-side vertical tab columns when the page has a heavy toolbar (debug / publish-check / save / publish), which would otherwise create a three-column layout that crushes main content below 600px on 1280 viewports. 16.2. List-page filter tabs (全部 / 已发布 / 草稿 above a list, etc.). Replaces the older underline-style
.tab-filter— keeps filter UI consistent with detail-page tabs across the product..cfg-tabs-bar { padding: 14px 32px; background: var(--card); border-bottom: 1px solid var(--border) } .cfg-tabs { display: inline-flex; gap: 2px; background: #F3F4F6; border-radius: var(--radius-sm); padding: 4px } .cfg-tab-h { height: 32px; padding: 6px 14px; gap: 6px; border-radius: 8px; font-size: var(--text-sm); color: var(--neutral-600); font-weight: 500; background: transparent; border: none; cursor: pointer } .cfg-tab-h.active { background: #FFFFFF; color: var(--foreground); box-shadow: var(--shadow-xs) } .cfg-tab-h:hover:not(.active) { color: var(--foreground) } .cfg-tab-h .lucide { width: 14px; height: 14px }Responsive table with sticky columns (HARD rules)
For list pages where the table can overflow horizontally (typically when viewport drops below ~1200px), enable horizontal scroll with sticky first / last columns. Do not hardcode a fixed table min-width.
.table-wrap { position: relative; overflow: auto; border-radius: var(--radius-md); background: var(--card); border: 1px solid var(--border) } .table-wrap .table { min-width: max-content } .table .sticky-l { position: sticky; left: 0; z-index: 1 } .table .sticky-r { position: sticky; right: 0; z-index: 1 } .table thead .sticky-l, .table thead .sticky-r { z-index: 3; background: #F9FAFB } .table tbody td.sticky-l, .table tbody td.sticky-r { z-index: 2; background: var(--card) } .table tbody tr:hover td.sticky-l, .table tbody tr:hover td.sticky-r { background: var(--neutral-50) } .table-wrap.scrolled-left .sticky-l { box-shadow: 4px 0 6px -2px rgba(0,0,0,0.05) } .table-wrap.scrolled-right .sticky-r { box-shadow: -4px 0 6px -2px rgba(0,0,0,0.05) }JS — toggle shadow classes only when actually scrolled:
function refreshTableShadow(wrap){ const sl = wrap.scrollLeft, sw = wrap.scrollWidth, cw = wrap.clientWidth; wrap.classList.toggle('scrolled-left', sl > 0); wrap.classList.toggle('scrolled-right', sl + cw < sw - 1); } window.addEventListener('load', () => document.querySelectorAll('.table-wrap').forEach(w => { refreshTableShadow(w); w.addEventListener('scroll', () => refreshTableShadow(w)); })); window.addEventListener('resize', () => document.querySelectorAll('.table-wrap').forEach(refreshTableShadow));⚠️ Critical pitfalls:
- Sticky cells without opaque bg → scrolling content shows through, looks like rendering bug.
- Thead sticky z-index ≤ tbody sticky z-index → tbody covers header on scroll.
- Forgetting
tr:hover td.sticky-*rule → sticky cells stay white while the rest of the row tints, splitting the row visually. - Always-on shadows → users see fake "two columns" with no actual scroll happening; toggle via JS scroll/resize listeners.
- JS-positioned columns instead of
position: sticky→ slow, broken on async row updates, fights row hover.
17.1. Action column alignment (left-align, not right)
The action column header and the buttons inside cells are both left-aligned — column header sits flush with the column's left padding edge, and operation buttons sit underneath, vertically aligned to that same edge.
Three rules:
- ⛔ Don't add
text-right pr-3/pr-5to the action<th>/<td>— that pushes content to the right edge, which then no longer aligns with the column header. - ⛔ Don't wrap action buttons in
<div class="inline-flex justify-end">—justify-enddefeats left alignment. - ✅ Do offset the first action button by
margin-left:calc(-1 * var(--space-3)). Reason:.btn-smhas 12px horizontal padding, so the button's text starts 12px inside its border. The negative margin pulls the button left so its text lines up with the column header text, not its border.
Right-side drawer (publish-check / verification panel)
When a config page has a long, tab-grouped verification list (publish check, lint, dependency check, etc.), don't pin it to the left rail — it grows unboundedly with new check items and competes with config tabs. Use a right-side drawer triggered from the toolbar.
- Trigger: outline button in page-header right cluster, with
check-circle-2icon + label. If unresolved warnings exist, attach a small absolute-positioned badge (top: -5px right: -5px) with--warning-fgbg, white text, 16px round, count number, font-weight bold. - Drawer: 420px wide, slides in from right (
transform: translateX(100%)→0),--shadow-xl, white bg, left border. z-index above main page content but below modals. - Drawer body: group rows by config tab. Each group has small uppercase label header (
text-xs muted, letter-spacing 0.05em) followed by rows. - Each row: status icon (success
check, warnalert-circle, failx-circle— colored via--success-fg/--warning-fg/--error-fg) + label + rightchevron-rightarrow. Row has 1px border,--radius-sm, hover gets--accentbg. Click closes the drawer + switches to the corresponding config tab + scrolls/highlights the section.
- Trigger: outline button in page-header right cluster, with
弹窗放页面里,不做浮层
所有"弹窗"都嵌入到页面布局里,不要做成 position: fixed + 半透明遮罩的中央浮层。
为什么:
- 浮层挡住主内容,用户失去上下文(看不到自己刚才在哪里、改了什么)。
- 多步操作场景下,浮层之间切换更打乱注意力。
- 嵌入式更接近"工作流就是页面本身"的设计哲学。
改造思路:
- 触发"弹窗"的入口(按钮 / 链接)原地展开成内嵌区块(accordion 风格),或推到右侧 Panel 作为新 tab。
- CSS 上:去掉
position: fixed+modal-mask遮罩;用border + radius + shadow在页面里圈出区域。 - 关闭按钮 → 折叠回去 / 关闭 tab,不是"取消模态"。
- 如果一定需要打断当前流程的强提示(如"删除二次确认"这种破坏性操作),用底部 toast / inline confirm bar 而不是浮层。
Anti-patterns:
- ❌
<div class="modal-mask"><div class="modal">...</div></div>中央浮层 + 遮罩。 - ❌ 把"打开新建数据集"做成 modal,把"添加用例"做成 modal,把"提交"做成 modal——一连串浮层互相叠。
- ❌ 简单的"确认 / 取消"也做成 modal——用 inline confirm。
例外(可保留浮层):
mention-popup(@提及候选列表)—— 依附在输入位置的轻量级浮层 OK。tooltip(悬停提示)—— 本来就是浮层。- 文件上传系统调起的原生文件选择器 —— 浏览器自带,无法控制。
Anti-patterns (learned from past iterations)
20.1. ❌ Multiple gradient avatar colors (blue / purple / green / orange / pink / indigo) — "rainbow" avatars look amateurish. 20.2. ❌ Cream / yellow active state on a multi-language tab switcher — too emphatic for navigation. 20.3. ❌ Cream badge for "已引用" status — competes with main content; use tiny gray icon + text. 20.4. ❌ Generic 6–8px border radius when your tokens dictate otherwise — follow your
--radius-md. 20.5. ❌ Putting<h1>without!important— Tailwind CDN preflight kills semantic heading styles. Either use!importantor class-based.h1. 20.6. ❌ Using full marketing scale (h1 = 36px) for in-app pages — feels comically large. Use the in-app heading scale above. 20.7. ❌ Showing entity ID as a separate column in a list table — prefer compact format: ID in mono font under the entity name (saves a column). 20.8. ❌ Showing operator name in a separate column from update time — combine: large date + small "由 X 更新" caption underneath. 20.9. ❌ Gray bg on sub-sidebars / panels (var(--neutral-50)etc.) when spec says pure white default — separate viaborder-rightonly. Gray fills are reserved for hover / secondary buttons. 20.10. ❌font-family: var(--font-mono)on text containing CJK — most mono fonts have no Chinese glyphs; CJK falls back but keeps mono spacing → looks broken. Apply<code>only to the actual numeric / ID / date span, not the surrounding sentence. 20.11. ❌ Tab filter row usingflex items-centertogether with parentborder-b— tab bottom border lands at row middle while parent border sits at row bottom, creating two parallel lines. Useflex items-end. 20.12. ❌ Mixingbtn-sm(32px) with defaultbtn-icon(36px) in a row — heights don't match. Usebtn-sm.btn-icon(32×32) when paired withbtn-sm. 20.13. ❌ Sidebar header height set by padding + content (variable), while topbar uses fixedh-16(64px) — borders end up 1–2px apart. Always set the sidebar header to an explicit height matching the topbar.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

