Install
openclaw skills install @fbbyqsyea/premium-html-studioGenerate professional-grade single-file HTML technical docs and proposals (方案) with two premium design systems. (1) International: Playfair Display + Inter + JetBrains Mono, indigo/purple palette, Stripe/Linear/Apple Docs quality for API docs, architecture, workflow analysis, research reports. (2) Chinese Technical Spec style (中文技术规格, Multi-ES style): Noto Sans SC + Noto Serif SC + JetBrains Mono, deep-purple gradient cover with KPI stats, numbered Part dividers, vertical pipeline flow charts with colored stage bars, data-flow funnel, tinted callouts — ideal for Chinese 流程详解/模式详解/链路图/节点拆解/参数说明 docs. Pairs with Prism.js syntax highlighting, layered shadows, gradient accents, publication-quality SVG diagrams. Triggers on: 技术文档, HTML 文档, 技术方案, 项目方案, 架构方案, 设计方案, 实施方案, 研究报告, 流程详解, 模式详解, 链路图, 流程图, 节点详解, pipeline 说明, proposal, premium docs, 专业文档, 漂亮的页面, 生成 HTML, make a professional HTML doc, write a proposal, generate a doc like Multi-ES, 按 Multi-ES 样式.
openclaw skills install @fbbyqsyea/premium-html-studioGenerate professional, publication-quality HTML documentation pages AND structured proposals with a sophisticated design system.
Before diving into the design system, here are 3 complete working examples you can use as starting points:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redis 缓存层技术方案</title>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;0,800;0,900;1,400;1,700&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<style>
:root {
--bg: #fafaf9;
--surface: #ffffff;
--border: #e5e7eb;
--text: #111827;
--text-muted: #6b7280;
--brand: #059669;
--brand-dark: #047857;
--brand-light: #d1fae5;
--brand-soft: #ecfdf5;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
--shadow-brand: 0 8px 24px rgba(5,150,105,0.20), 0 2px 8px rgba(5,150,105,0.10);
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
--r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px;
--ease: cubic-bezier(0.16, 1, 0.3, 1);
--font-display: 'Playfair Display', Georgia, serif;
--font-body: 'Inter', 'PingFang SC', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
[data-theme="dark"] {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #c9d1d9;
--text-muted: #8b949e;
--brand: #10b981;
--brand-dark: #059669;
--brand-light: #064e3b;
--brand-soft: #022c22;
}
* { box-sizing: border-box; }
body {
font-family: var(--font-body);
background: var(--bg);
color: var(--text);
line-height: 1.65;
margin: 0;
transition: background 0.3s var(--ease), color 0.3s var(--ease);
}
.container { max-width: 1280px; margin: 0 auto; padding: 0 var(--sp-5) var(--sp-7); }
header.doc-header {
background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand) 100%);
color: white;
padding: var(--sp-8) var(--sp-7) var(--sp-6);
text-align: center;
max-width: 1280px;
margin: var(--sp-5) auto var(--sp-5);
border-radius: var(--r-xl);
box-shadow: var(--shadow-brand);
}
.eyebrow {
font-family: var(--font-mono);
font-size: 0.78em;
letter-spacing: 0.25em;
text-transform: uppercase;
opacity: 0.85;
margin-bottom: var(--sp-3);
display: inline-block;
padding: 4px 14px;
background: rgba(255,255,255,0.15);
border-radius: 20px;
backdrop-filter: blur(10px);
}
h1 {
font-family: var(--font-display);
font-weight: 800;
font-size: 3em;
line-height: 1.05;
letter-spacing: -0.02em;
margin: 0 0 var(--sp-3);
}
h1 em { font-style: italic; color: #fde68a; }
section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--r-lg);
padding: var(--sp-6) var(--sp-7);
margin-bottom: var(--sp-5);
box-shadow: var(--shadow-sm);
transition: box-shadow 0.3s var(--ease);
}
section:hover { box-shadow: var(--shadow-md); }
h2 {
font-family: var(--font-display);
font-size: 1.75em;
font-weight: 800;
margin: 0 0 var(--sp-5);
padding-bottom: var(--sp-3);
border-bottom: 2px solid var(--border);
color: var(--text);
}
pre[class*="language-"] {
background: #1e1e1e !important;
border-radius: var(--r-md);
padding: var(--sp-5) !important;
margin: var(--sp-4) 0 var(--sp-5);
font-size: 0.85em !important;
box-shadow: var(--shadow-md);
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 50%;
width: 40px;
height: 40px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2em;
box-shadow: var(--shadow-sm);
transition: all 0.2s var(--ease);
z-index: 1000;
}
.theme-toggle:hover {
transform: scale(1.1);
box-shadow: var(--shadow-md);
}
@media (max-width: 900px) {
.container { padding: 0 var(--sp-4) var(--sp-6); }
section { padding: var(--sp-5); }
}
@media (max-width: 600px) {
body { font-size: 15px; }
h1 { font-size: 2em; }
section { padding: var(--sp-4); }
}
@media print {
.theme-toggle { display: none; }
section { break-inside: avoid; }
}
</style>
</head>
<body>
<button class="theme-toggle" onclick="toggleTheme()" aria-label="切换深色模式">🌓</button>
<div class="container">
<header class="doc-header">
<span class="eyebrow">Technical Proposal · v1.0</span>
<h1>Redis 缓存层 · <em>技术方案</em></h1>
</header>
<section>
<h2>背景</h2>
<p>当前系统直接查询数据库,响应时间 200-500ms,需要优化到 50ms 以内。</p>
</section>
<section>
<h2>方案</h2>
<p>引入 Redis 作为缓存层,预计提升 8-10 倍性能。</p>
</section>
</div>
<script>
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-theme');
html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
</body>
</html>
[See existing: docs/llm_prompt_engineering.html]
[See existing: docs/redis-cache-proposal.html]
All premium HTML documents support dark mode out of the box:
/* Light mode (default) */
:root {
--bg: #fafaf9;
--surface: #ffffff;
--text: #111827;
}
/* Dark mode */
[data-theme="dark"] {
--bg: #0d1117;
--surface: #161b22;
--text: #c9d1d9;
}
Toggle button:
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode">🌓</button>
<script>
function toggleTheme() {
const html = document.documentElement;
const current = html.getAttribute('data-theme');
html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
}
</script>
All generated documents must meet WCAG 2.1 AA standards:
<!-- Tab buttons -->
<button class="tab active" data-tab="en"
aria-selected="true"
aria-controls="tab-en"
role="tab">English</button>
<!-- Callouts -->
<div class="callout danger" role="alert">
<div class="callout-title">⚠️ Warning</div>
</div>
<!-- Theme toggle -->
<button aria-label="Toggle dark mode">🌓</button>
/* Focus indicators */
.tab:focus, button:focus {
outline: 2px solid var(--brand);
outline-offset: 2px;
}
/* Tab navigation */
.tabs {
display: flex;
gap: var(--sp-1);
}
.tab:focus-visible {
outline: 2px solid var(--brand);
}
<!-- Decorative icons -->
<span aria-hidden="true">🌐</span> English
<!-- Meaningful icons -->
<button aria-label="Search documentation">
<svg aria-hidden="true">...</svg>
</button>
Support 5 brand color presets:
/* Emerald (default) */
:root { --brand: #059669; --brand-dark: #047857; }
/* Indigo */
:root { --brand: #4338ca; --brand-dark: #3730a3; }
/* Purple */
:root { --brand: #7c3aed; --brand-dark: #6d28d9; }
/* Sunset */
:root { --brand: #ea580c; --brand-dark: #c2410c; }
/* Ocean */
:root { --brand: #0284c7; --brand-dark: #0369a1; }
Usage:
<link rel="stylesheet" href="styles-emerald.css">
<!-- or -->
<link rel="stylesheet" href="styles-indigo.css">
:root {
--transition-fast: 150ms ease-out;
--transition-normal: 300ms ease-in-out;
--transition-slow: 500ms ease-in-out;
}
/* Cards */
.card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
transition: transform var(--transition-fast),
box-shadow var(--transition-fast);
}
/* Buttons */
button:hover {
transform: scale(1.02);
transition: transform var(--transition-fast);
}
/* Links */
a:hover {
color: var(--brand);
transition: color var(--transition-fast);
}
// Fade in on scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
}
});
});
document.querySelectorAll('.reveal').forEach(el => {
observer.observe(el);
});
Support 4 breakpoints for all screen sizes:
/* Mobile (< 600px) */
@media (max-width: 600px) {
body { font-size: 15px; }
h1 { font-size: 2em; }
.container { padding: 0 var(--sp-4) var(--sp-6); }
section { padding: var(--sp-4); }
.grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}
/* Tablet (600px - 900px) */
@media (min-width: 601px) and (max-width: 900px) {
.container { padding: 0 var(--sp-4) var(--sp-6); }
section { padding: var(--sp-5); }
.grid-3, .grid-4 { grid-template-columns: 1fr 1fr; }
}
/* Desktop (900px - 1440px) - default */
/* No media query needed */
/* Large Desktop (> 1440px) */
@media (min-width: 1441px) {
.container { max-width: 1440px; }
h1 { font-size: 3.5em; }
}
/* Ultra-wide (> 1920px) */
@media (min-width: 1921px) {
.container { max-width: 1600px; }
body { font-size: 18px; }
}
Optimize documents for printing:
@media print {
/* Hide interactive elements */
.theme-toggle, .tabs, button { display: none; }
/* Optimize for paper */
body {
font-size: 12pt;
color: black;
background: white;
}
/* Page breaks */
section {
break-inside: avoid;
page-break-inside: avoid;
}
h2 {
break-after: avoid;
page-break-after: avoid;
}
/* Links */
a {
text-decoration: underline;
color: black;
}
a[href^="http"]:after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: #666;
}
/* Images */
img {
max-width: 100%;
page-break-inside: avoid;
}
/* Page margins */
@page {
margin: 2cm;
}
/* Print-only content */
.print-only {
display: block !important;
}
.no-print {
display: none !important;
}
}
Print-only content:
<div class="print-only" style="display: none;">
<p>Printed on: <span id="print-date"></span></p>
<script>
document.getElementById('print-date').textContent = new Date().toLocaleDateString();
</script>
</div>
Support 15+ programming languages:
<!-- Include Prism.js with all languages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
Supported languages:
Usage:
<pre class="code" data-lang="typescript">
<code class="language-typescript">
const greeting: string = "Hello";
console.log(greeting);
</code>
</pre>
Autoloader configuration:
<script>
Prism.plugins.autoloader.languages_path = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/";
</script>
For long documents, use collapsible sections:
<details class="collapsible">
<summary class="collapsible-header">
<span class="collapsible-icon">▶</span>
<span>点击展开详细内容</span>
</summary>
<div class="collapsible-content">
<p>折叠的内容...</p>
</div>
</details>
CSS:
.collapsible {
border: 1px solid var(--border);
border-radius: var(--r-md);
margin: var(--sp-4) 0;
overflow: hidden;
}
.collapsible-header {
padding: var(--sp-3) var(--sp-4);
background: var(--surface);
cursor: pointer;
display: flex;
align-items: center;
gap: var(--sp-2);
font-weight: 600;
transition: background var(--transition-fast);
}
.collapsible-header:hover {
background: var(--primary-light);
}
.collapsible-icon {
transition: transform var(--transition-fast);
font-size: 0.8em;
}
details[open] .collapsible-icon {
transform: rotate(90deg);
}
.collapsible-content {
padding: var(--sp-4);
background: var(--bg);
border-top: 1px solid var(--border);
}
/* Accessibility */
.collapsible-header:focus {
outline: 2px solid var(--brand);
outline-offset: -2px;
}
JavaScript for smooth animation:
document.querySelectorAll('.collapsible').forEach(details => {
details.addEventListener('toggle', () => {
const content = details.querySelector('.collapsible-content');
if (details.open) {
content.style.maxHeight = content.scrollHeight + 'px';
} else {
content.style.maxHeight = '0';
}
});
});
✅ L-shape paths (simple connections):
<path d="M100 100 L100 150 L200 150" stroke="#000" stroke-width="2" fill="none"/>
✅ Bezier curves (complex flows):
<!-- Organic flows, S-curves -->
<path d="M100 100 C150 150, 200 50, 250 100" stroke="#000" stroke-width="2" fill="none"/>
✅ Curved arrows (when L-shape is impractical):
<path d="M100 100 Q150 150, 200 100" stroke="#000" stroke-width="2" fill="none" marker-end="url(#arrow)"/>
✅ Rounded rectangles:
<rect x="50" y="50" width="100" height="80" rx="8" fill="#fff" stroke="#000"/>
When using Bezier curves (C, Q):
Keep control points reasonable
<!-- ✓ GOOD: Smooth, natural curve -->
<path d="M100 100 C150 120, 200 80, 250 100"/>
<!-- ✗ BAD: Extreme control points -->
<path d="M100 100 C500 500, -200 -200, 250 100"/>
Use for organic flows, not simple connections
<!-- ✓ Use Bezier for: Process flows, timelines -->
<path d="M50 100 C150 100, 200 150, 300 150"/>
<!-- ✗ Use L-shape for: Simple A→B connections -->
<path d="M50 100 L150 100 L150 150 L300 150"/>
Test across browsers
<!-- Add browser-specific fallbacks if needed -->
<style>
@supports not (d: path('M0 0')) {
/* Fallback for older browsers */
}
</style>
<defs>
<!-- Straight arrow -->
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto">
<path d="M0,0 L10,5 L0,10 z" fill="#000"/>
</marker>
<!-- Curved arrow (for Bezier paths) -->
<marker id="arrow-curved" viewBox="0 0 10 10" refX="8" refY="5"
markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M0,2 L8,5 L0,8 z" fill="#000"/>
</marker>
</defs>
Use Mermaid for flowcharts, sequence diagrams, and more:
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
flowchart: { useMaxWidth: true }
});
</script>
Flowchart:
<div class="mermaid">
graph TD
A[用户请求] --> B{缓存命中?}
B -->|是| C[返回缓存数据]
B -->|否| D[查询数据库]
D --> E[写入缓存]
E --> C
</div>
Sequence Diagram:
<div class="mermaid">
sequenceDiagram
participant U as 用户
participant C as 客户端
participant R as Redis
participant D as 数据库
U->>C: 发起请求
C->>R: 查询缓存
alt 缓存命中
R-->>C: 返回数据
else 缓存未命中
R-->>C: 未命中
C->>D: 查询数据库
D-->>C: 返回数据
C->>R: 写入缓存
C-->>U: 返回结果
end
</div>
Supported diagram types:
Add search functionality for long documents:
<!-- Include FlexSearch -->
<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.31/dist/flexsearch.bundle.js"></script>
<!-- Search input -->
<div class="search-container">
<input type="text" id="search-input" placeholder="搜索文档..." aria-label="搜索">
<div id="search-results" class="search-results"></div>
</div>
CSS:
.search-container {
position: relative;
max-width: 600px;
margin: var(--sp-5) auto;
}
#search-input {
width: 100%;
padding: var(--sp-3) var(--sp-4);
border: 1px solid var(--border);
border-radius: var(--r-md);
font-size: 1em;
transition: border-color var(--transition-fast);
}
#search-input:focus {
outline: none;
border-color: var(--brand);
box-shadow: 0 0 0 3px var(--brand-soft);
}
.search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--r-md);
margin-top: var(--sp-2);
max-height: 400px;
overflow-y: auto;
box-shadow: var(--shadow-lg);
display: none;
}
.search-results.active {
display: block;
}
.search-result-item {
padding: var(--sp-3) var(--sp-4);
border-bottom: 1px solid var(--border);
cursor: pointer;
transition: background var(--transition-fast);
}
.search-result-item:hover {
background: var(--primary-light);
}
.search-result-item:last-child {
border-bottom: none;
}
JavaScript:
const index = new FlexSearch.Document({
document: {
id: 'id',
index: ['title', 'content']
}
});
// Index all sections
document.querySelectorAll('section').forEach((section, i) => {
const title = section.querySelector('h2')?.textContent || '';
const content = section.textContent;
index.add({ id: i, title, content });
});
// Search on input
const searchInput = document.getElementById('search-input');
const searchResults = document.getElementById('search-results');
searchInput.addEventListener('input', (e) => {
const query = e.target.value;
if (query.length < 2) {
searchResults.classList.remove('active');
return;
}
const results = index.search(query);
searchResults.innerHTML = results.map(r => {
const section = document.querySelectorAll('section')[r.id];
const title = section.querySelector('h2').textContent;
return `<div class="search-result-item" onclick="scrollToSection(${r.id})">
<strong>${title}</strong>
<p>${section.textContent.substring(0, 100)}...</p>
</div>`;
}).join('');
searchResults.classList.add('active');
});
function scrollToSection(id) {
document.querySelectorAll('section')[id].scrollIntoView({ behavior: 'smooth' });
searchResults.classList.remove('active');
searchInput.value = '';
}
For interactive documentation:
<!-- Text input -->
<div class="form-group">
<label for="input-name">名称</label>
<input type="text" id="input-name" class="form-input" placeholder="输入名称">
</div>
<!-- Select -->
<div class="form-group">
<label for="select-lang">语言</label>
<select id="select-lang" class="form-select">
<option value="en">English</option>
<option value="zh">中文</option>
</select>
</div>
<!-- Checkbox -->
<div class="form-group">
<label class="form-checkbox">
<input type="checkbox">
<span>启用深色模式</span>
</label>
</div>
CSS:
.form-group {
margin: var(--sp-4) 0;
}
.form-group label {
display: block;
margin-bottom: var(--sp-2);
font-weight: 600;
color: var(--text);
}
.form-input, .form-select {
width: 100%;
padding: var(--sp-2) var(--sp-3);
border: 1px solid var(--border);
border-radius: var(--r-sm);
font-size: 0.95em;
transition: border-color var(--transition-fast);
}
.form-input:focus, .form-select:focus {
outline: none;
border-color: var(--brand);
box-shadow: 0 0 0 3px var(--brand-soft);
}
.form-checkbox {
display: flex;
align-items: center;
gap: var(--sp-2);
cursor: pointer;
}
.form-checkbox input {
width: 18px;
height: 18px;
cursor: pointer;
}
Ctrl+P (Windows/Linux) or Cmd+P (Mac)@media print {
/* Hide interactive elements */
.theme-toggle, .search-container, .tabs, button, nav {
display: none !important;
}
/* Optimize for paper */
body {
font-size: 11pt;
line-height: 1.5;
color: #000;
background: #fff;
}
/* Page setup */
@page {
size: A4;
margin: 2cm;
}
/* Section breaks */
section {
break-inside: avoid;
page-break-inside: avoid;
}
h2, h3 {
break-after: avoid;
page-break-after: avoid;
}
/* Links */
a {
color: #000;
text-decoration: underline;
}
a[href^="http"]:after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: #666;
}
/* Images */
img {
max-width: 100%;
break-inside: avoid;
page-break-inside: avoid;
}
/* Code blocks */
pre {
font-size: 9pt;
break-inside: avoid;
page-break-inside: avoid;
}
/* Print-only content */
.print-only {
display: block !important;
}
.no-print {
display: none !important;
}
}
Print-only footer:
<div class="print-only" style="display: none; text-align: center; margin-top: 2cm; font-size: 9pt; color: #666;">
<p>Generated by Premium HTML Studio · <span id="print-date"></span></p>
<script>
document.getElementById('print-date').textContent = new Date().toLocaleDateString();
</script>
</div>
Systematic multi-language support:
<!-- Language switcher -->
<div class="lang-switcher">
<select id="lang-select" aria-label="选择语言">
<option value="en">English</option>
<option value="zh">中文</option>
<option value="ja">日本語</option>
</select>
</div>
<!-- Multi-language content -->
<section>
<h2>
<span data-lang="en">Background</span>
<span data-lang="zh" hidden>背景</span>
<span data-lang="ja" hidden>背景</span>
</h2>
<p>
<span data-lang="en">Current system latency is 200-500ms.</span>
<span data-lang="zh" hidden>当前系统延迟为 200-500ms。</span>
<span data-lang="ja" hidden>現在のシステムレイテンシは 200-500ms です。</span>
</p>
</section>
CSS:
.lang-switcher {
position: fixed;
top: 20px;
left: 20px;
z-index: 1000;
}
#lang-select {
padding: var(--sp-2) var(--sp-3);
border: 1px solid var(--border);
border-radius: var(--r-sm);
background: var(--surface);
cursor: pointer;
}
[data-lang] {
display: none;
}
[data-lang].active {
display: inline;
}
JavaScript:
const langSelect = document.getElementById('lang-select');
function switchLanguage(lang) {
// Hide all languages
document.querySelectorAll('[data-lang]').forEach(el => {
el.classList.remove('active');
el.hidden = true;
});
// Show selected language
document.querySelectorAll(`[data-lang="${lang}"]`).forEach(el => {
el.classList.add('active');
el.hidden = false;
});
// Save preference
localStorage.setItem('preferred-lang', lang);
}
langSelect.addEventListener('change', (e) => {
switchLanguage(e.target.value);
});
// Load saved preference
const savedLang = localStorage.getItem('preferred-lang') || 'en';
langSelect.value = savedLang;
switchLanguage(savedLang);
Show code changes:
<div class="code-diff">
<div class="diff-header">
<span class="diff-file">config.py</span>
<span class="diff-stats">+3 -1</span>
</div>
<div class="diff-content">
<div class="diff-line diff-old">
<span class="diff-line-num">-</span>
<code>timeout = 30</code>
</div>
<div class="diff-line diff-new">
<span class="diff-line-num">+</span>
<code>timeout = 60</code>
</div>
<div class="diff-line diff-context">
<span class="diff-line-num"> </span>
<code>retries = 3</code>
</div>
</div>
</div>
CSS:
.code-diff {
border: 1px solid var(--border);
border-radius: var(--r-md);
overflow: hidden;
font-family: var(--font-mono);
font-size: 0.85em;
margin: var(--sp-4) 0;
}
.diff-header {
background: var(--surface);
padding: var(--sp-2) var(--sp-3);
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
}
.diff-file {
font-weight: 600;
}
.diff-stats {
font-size: 0.85em;
}
.diff-stats::before {
content: "+";
color: var(--success);
}
.diff-stats::after {
content: " -";
color: var(--danger);
}
.diff-content {
background: var(--bg);
}
.diff-line {
padding: 2px var(--sp-3);
display: flex;
gap: var(--sp-2);
}
.diff-line-num {
width: 20px;
text-align: right;
user-select: none;
opacity: 0.5;
}
.diff-old {
background: var(--danger-light);
color: var(--danger);
}
.diff-new {
background: var(--success-light);
color: var(--success);
}
.diff-context {
color: var(--text-muted);
}
<!-- Responsive images -->
<img src="image-800.webp"
srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
alt="Description"
loading="lazy"
decoding="async">
<!-- Defer non-critical CSS -->
<link rel="preload" href="critical.css" as="style">
<link rel="stylesheet" href="critical.css">
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
<!-- Async JavaScript -->
<script src="analytics.js" async></script>
<script src="interactive.js" defer></script>
<!-- Lazy load images -->
<img loading="lazy" src="image.jpg" alt="...">
<!-- Lazy load iframes -->
<iframe loading="lazy" src="video.html"></iframe>
<!-- Lazy load sections -->
<section id="heavy-content" data-lazy="true">
<!-- Content loaded on scroll -->
</section>
<head>
<title>OceanBase Full Mode Technical Guide</title>
<meta name="description" content="Complete guide to OceanBase full mode implementation">
<meta name="keywords" content="OceanBase, full mode, technical guide">
<!-- Open Graph -->
<meta property="og:title" content="OceanBase Full Mode">
<meta property="og:description" content="Complete technical guide">
<meta property="og:image" content="https://example.com/preview.png">
<meta property="og:url" content="https://example.com/guide">
<meta property="og:type" content="article">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="OceanBase Full Mode">
<meta name="twitter:description" content="Complete technical guide">
<meta name="twitter:image" content="https://example.com/preview.png">
<!-- Canonical -->
<link rel="canonical" href="https://example.com/guide">
</head>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "OceanBase Full Mode Technical Guide",
"description": "Complete guide to implementation",
"author": {
"@type": "Person",
"name": "Author Name"
},
"datePublished": "2026-07-14",
"dateModified": "2026-07-14"
}
</script>
Use native HTML <details> for simple tabs:
<details class="tab-simple" open>
<summary>English</summary>
<div class="tab-content-simple">English content</div>
</details>
<details class="tab-simple">
<summary>中文</summary>
<div class="tab-content-simple">中文内容</div>
</details>
CSS (only one open at a time):
.tab-simple {
border: 1px solid var(--border);
border-radius: var(--r-sm);
margin: var(--sp-2) 0;
}
.tab-simple summary {
padding: var(--sp-2) var(--sp-3);
cursor: pointer;
font-weight: 600;
list-style: none;
}
.tab-simple summary::-webkit-details-marker {
display: none;
}
.tab-content-simple {
padding: var(--sp-3);
border-top: 1px solid var(--border);
}
/* Close others when one opens */
.tab-simple[open] ~ .tab-simple {
/* JavaScript needed for mutual exclusion */
}
JavaScript for mutual exclusion:
document.querySelectorAll('.tab-simple').forEach(details => {
details.addEventListener('toggle', () => {
if (details.open) {
document.querySelectorAll('.tab-simple').forEach(other => {
if (other !== details) other.open = false;
});
}
});
});
┌─────────────────────────────────┐
│ Header │
├─────────────────────────────────┤
│ TOC │
├─────────────────────────────────┤
│ Section 1 │
├─────────────────────────────────┤
│ Section 2 │
└─────────────────────────────────┘
<body>
<header class="doc-header">...</header>
<div class="container">
<section>...</section>
<section>...</section>
</div>
</body>
<body>
<!-- Hero section -->
<section class="hero">
<h1>Product Name</h1>
<p>Tagline</p>
<a href="#features" class="btn">Learn More</a>
</section>
<!-- Features -->
<section id="features">
<h2>Features</h2>
<div class="grid-3">
<div class="card">...</div>
<div class="card">...</div>
<div class="card">...</div>
</div>
</section>
<!-- CTA -->
<section class="cta">
<h2>Get Started</h2>
<a href="#" class="btn">Sign Up</a>
</section>
</body>
Hero CSS:
.hero {
min-height: 80vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background: linear-gradient(135deg, var(--brand-dark), var(--brand));
color: white;
}
.hero h1 {
font-size: 4em;
margin-bottom: var(--sp-4);
}
.btn {
display: inline-block;
padding: var(--sp-3) var(--sp-6);
background: white;
color: var(--brand);
border-radius: var(--r-md);
text-decoration: none;
font-weight: 600;
transition: transform var(--transition-fast);
}
.btn:hover {
transform: translateY(-2px);
}
This skill supports two primary content categories:
When generating a proposal, use this structural template:
┌─────────────────────────────────────────────────────────────────────┐
│ Header: 方案标题 · 日期 · 作者/团队 │
│ [eyebrow] PROJECT · TYPE · STATUS │
└─────────────────────────────────────────────────────────────────────┘
┌─ Executive Summary (执行摘要) ─────────────────────────────────────┐
│ 背景 · 目标 · 核心结论(3-5 个要点,卡片式布局) │
└────────────────────────────────────────────────────────────────────┘
┌─ Background (背景分析) ────────────────────────────────────────────┐
│ 现状 · 问题 · 机会 · 数据支撑(SVG 流程图) │
└────────────────────────────────────────────────────────────────────┘
┌─ Objectives (目标与指标) ──────────────────────────────────────────┐
│ SMART 目标 · KPIs · 成功标准(表格 + 徽章标记优先级) │
└────────────────────────────────────────────────────────────────────┘
┌─ Approach (方案概述) ──────────────────────────────────────────────┐
│ 总体思路 · 核心策略 · 关键决策(SVG 架构图 + 分栏卡片) │
└────────────────────────────────────────────────────────────────────┘
┌─ Implementation (实施计划) ────────────────────────────────────────┐
│ 分阶段计划 · 里程碑 · 时间线(SVG 甘特图/时间线) │
└────────────────────────────────────────────────────────────────────┘
┌─ Resources (资源需求) ─────────────────────────────────────────────┐
│ 团队 · 预算 · 工具 · 依赖(表格 + 成本汇总) │
└────────────────────────────────────────────────────────────────────┘
┌─ Risks & Mitigation (风险与应对) ──────────────────────────────────┐
│ 风险清单 · 概率 · 影响 · 应对策略(callout + 表格) │
└────────────────────────────────────────────────────────────────────┘
┌─ Alternatives (备选方案) ──────────────────────────────────────────┐
│ 对比分析 · 推荐方案 · 决策依据(对比表格 + SVG 决策树) │
└────────────────────────────────────────────────────────────────────┘
┌─ Conclusion (结论与下一步) ────────────────────────────────────────┐
│ 总结 · 审批请求 · 后续行动 · 联系信息 │
└────────────────────────────────────────────────────────────────────┘
<div class="grid-4">
<div class="stat-card">
<div class="num">3</div>
<div class="label">核心目标</div>
</div>
<div class="stat-card">
<div class="num">Q3</div>
<div class="label">启动时间</div>
</div>
<div class="stat-card">
<div class="num">6mo</div>
<div class="label">实施周期</div>
</div>
<div class="stat-card">
<div class="num">¥2M</div>
<div class="label">总预算</div>
</div>
</div>
<!-- Timeline arrow with phase markers -->
<line x1="100" y1="500" x2="1100" y2="500"
stroke="url(#g-arrow)" stroke-width="3"/>
<!-- Phase markers -->
<g>
<circle cx="200" cy="500" r="8" fill="url(#g-brand)"/>
<text x="200" y="530" text-anchor="middle" class="mono" font-size="11">
Phase 1
</text>
<text x="200" y="548" text-anchor="middle" class="body" font-size="10" fill="#6b7280">
调研 · 1mo
</text>
</g>
<div class="callout danger">
<div class="callout-title">⚠️ 高风险:数据迁移失败</div>
概率:中 · 影响:高<br>
<strong>应对策略:</strong>分批次灰度迁移,保留回滚能力
</div>
<table class="fields">
<thead>
<tr>
<th>维度</th>
<th>方案 A</th>
<th class="hl">方案 B (推荐)</th>
<th>方案 C</th>
</tr>
</thead>
<tbody>
<tr><td>实施成本</td><td>¥1.5M</td><td>¥2M</td><td>¥3M</td></tr>
<tr><td>实施周期</td><td>9mo</td><td>6mo</td><td>4mo</td></tr>
</tbody>
</table>
<span class="badge" style="background:#059669;">已批准</span>
<span class="badge" style="background:#d97706;">待审批</span>
<span class="badge" style="background:#dc2626;">已拒绝</span>
<span class="badge" style="background:#6b7280;">已归档</span>
| Signal | Generate |
|---|---|
| 用户提到"文档/说明/介绍/流程" | Documentation |
| 用户提到"方案/计划/提案/建议" | Proposal |
| 用户给出完整内容要求呈现 | Documentation |
| 用户描述问题并要求解决思路 | Proposal |
| 用户需要对比多个选项 | Proposal (with alternatives) |
| 用户需要详细解释现有系统 | Documentation |
Mixed content: If the request contains both elements, combine them — e.g., a technical proposal with architecture documentation sections.
A second design system distilled from the reference document 《Multi-ES Precise 模式流程详解》 — the house style for Chinese technical deep-dives that walk through a pipeline, an algorithm flow, or a system's internals node by node (流程详解 / 模式详解 / 链路图 / 节点拆解 / 参数说明).
Choose this style when: the doc is in Chinese AND explains a process/flow — a retrieval pipeline, an LLM chain, a multi-stage algorithm, a system workflow — with stages, steps, parameters and thresholds to document. Choose the International style (below) for English docs, API references, or content that is mostly prose rather than flow.
Full CSS lives in templates/css-system-cn.css — copy it into the <style> block and customize --purple/--blue accents if you want a different hue. The core shapes (cover hero, part dividers, vertical flow, funnel) are what give the style its identity; keep them intact.
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&family=Noto+Serif+SC:wght@400;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
| Role | Font | Usage |
|---|---|---|
| Body / UI | Noto Sans SC (300–700) | Paragraphs, lists, tables, callouts — CJK-optimized metrics |
| Headings / Display | Noto Serif SC (400/600/700) | Cover H1, part titles, h2.sec — serif gives 中文文档 gravitas |
| Code / Technical | JetBrains Mono (400/500) | Inline code, pre blocks, KPIs, numbers, badges |
Six accent families, each with a base + light tint used for bars, callouts, stage tags and badges:
--blue:#2563eb; --blt2:#dbeafe; /* 召回/入口 */
--purple:#7c3aed; --plt:#ede9fe; /* 增强/扩展 + 主强调色 */
--teal:#0891b2; --tlt:#cffafe; /* 存储/DB */
--green:#059669; --glt:#d1fae5; /* 输出/成功 */
--amber:#d97706; --alt:#fef3c7; /* LLM 调用点 / 警告 */
--red:#dc2626; --rlt:#fee2e2; /* 错误 / 易错点 */
Plus 4-level text scale --text:#1a1a2e → --text2 → --text3 → --text4 and surfaces --bgsub:#f8f9fc / --border:#e2e6ef. Page background is a light gray-blue gradient (#eef1f7 → #e8ecf4) so the white .doc card floats on top.
┌─ .doc (1200px white card, layered shadow) ─────────────────┐
│ ┌─ .cover ── dark purple gradient hero ─────────────────┐ │
│ │ [.tag pill] h1(serif) .sub .meta .kpis(4 个数字) │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌─ .body ──────────────────────────────────────────────┐ │
│ │ .exec 管理摘要 (紫色左边条渐变卡) │ │
│ │ .toc 分部分卡片网格 (Part 01 … 06) │ │
│ │ .part 分隔条 + h2.sec 小节 + h3.sub + p.lead │ │
│ │ .flow 纵向流程 / .funnel 数据漏斗 / 表格 / callout │ │
│ │ .footer │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
The cover is a 155deg dark-purple gradient (#1a0a2e → #2d1440 → #4a0d5e → #5b0e7d) with two radial glow circles (::before/::after) — the glow is what keeps it from looking flat. Layout: eyebrow .tag pill → serif h1 → .sub one-line subtitle → .meta version/module lines with <b> labels → .kpis row of mono numbers.
<div class="cover">
<span class="tag">Technical Spec · SAG-Benchmark</span>
<h1>Multi-ES Precise 模式流程详解</h1>
<p class="sub">高精度 LLM 精选的多元事项检索链路 — 一句话副标题</p>
<div class="meta">
<b>版本:</b>v1.0 — 2026 年 7 月<br>
<b>模块:</b>pipeline/modules/search/multi_vector.py<br>
<b>定位:</b>链路设计、阈值与机制说明
</div>
<div class="kpis">
<div class="kpi"><div class="v">1</div><div class="l">LLM 调用次数</div></div>
<div class="kpi"><div class="v">2</div><div class="l">召回通道</div></div>
<div class="kpi"><div class="v">8</div><div class="l">链路节点</div></div>
</div>
</div>
Long specs are split into numbered Parts — a Part 01-style divider with a 3px top rule and serif title, then h2.sec sub-sections each with a 48px purple underline accent, h3.sub for sub-points, and p.lead for gray intro paragraphs:
<div class="part" id="p1">
<span class="num">Part 01</span>
<div class="title">链路全景图</div>
</div>
<h2 class="sec">数据流量漏斗</h2>
<h3 class="sub">① 召回控制参数</h3>
<p class="lead">Precise 模式的行为由 … 控制。</p>
A purple-left-border gradient box right after the cover, summarizing the whole chain in 2–3 paragraphs with inline <code> and <b> emphasis:
<div class="exec">
<h2>管理摘要</h2>
<p>链路概要:<b>Step1</b> BM25 实体召回 → <b>Step2</b> … → <b>Step8</b> 获取 Chunk。</p>
</div>
This is the component that makes 流程详解 docs read like a pipeline. A .flow is a column of .fnodes; each node = a 5px colored .fbar + a .fcard with four fields — .fn (STEP label), .ft (bold title, usually the param name), .fd (description, may include <code>/<br>), .fb (output pill with <b> value). Nodes are joined by .arrow rows carrying ▼ and optional .badge count pills. Group stages under .stage-tag pills (st1 blue / st2 purple / st3 green).
<span class="stage-tag st1">阶段一 · 召回</span>
<div class="flow">
<div class="fnode"><div class="fbar" style="background:var(--blue)"></div>
<div class="fcard">
<div class="fn">STEP 1 · BM25 实体召回</div>
<div class="ft">entity_top_k = 20</div>
<div class="fd">ES 全文检索 · 按原始分排序截断,<b>无相似度阈值</b>。</div>
<div class="fb">输出 ≤ <b>20</b> 实体</div>
</div>
</div>
<div class="arrow">▼ <span class="badge">20 实体</span></div>
<div class="fnode"><div class="fbar" style="background:var(--green)"></div>
<div class="fcard">
<div class="fn">STEP 6 · 向量粗排</div>
<div class="ft">max_events = 100</div>
<div class="fd">kNN + <code>event_ids</code> 精确过滤,按相似度降序截断。</div>
<div class="fb">输出 ≤ <b>100</b> 候选</div>
</div>
</div>
</div>
Bar color semantics (keep consistent across the doc): --blue = 召回/入口, --purple = 增强/扩展, --green = 精选/输出, --amber = LLM 调用点, --teal = 存储/MySQL. Explain the mapping once in a .legend (swatch + label) below the flow:
<div class="legend">
<span class="lg"><span class="sw" style="background:var(--blue)"></span>召回节点</span>
<span class="lg"><span class="sw" style="background:var(--amber)"></span>LLM 调用点</span>
</div>
When the doc tracks how candidate counts shrink through stages (20 → 40 → ≤100 → ≤10), use a .funnel: each row = right-aligned .fstep label + a width-encoded .fbar2 bar (width % ≈ relative volume, color = stage family) + .fnote annotation. This makes capacity/throughput readable at a glance.
<div class="funnel">
<div class="frow"><div class="fstep">BM25 实体</div><div class="fbar2" style="width:18%;background:var(--blue)">20</div><div class="fnote">Step1</div></div>
<div class="frow"><div class="fstep">LLM 精选</div><div class="fbar2" style="width:14%;background:var(--amber)">≤10</div><div class="fnote">Step7</div></div>
</div>
.callout.info (teal, 细节), .callout.warn (amber, 提示/注意), .callout.err (red, 易错点), .callout.ok (green, 测试清单/结论). Put 易错点 lists in .err with numbered <br>-separated lines.<table> (no class), th on --bgsub with white-space:nowrap, zebra rows via tr:nth-child(even). First column is often a <code> param name.code is purple text on --bgsub; block pre is dark #0f1220 with colored spans .c (comment #5b6080), .k (keyword #c792ea), .s (string #a5d6a7), .n (number #82aaff). Highlight code comments with <span class="c"> and keywords with <span class="k">.<details open><summary>…</summary>…</details> for long prompt texts / full source excerpts; summary is bold 13.5px..feature-grid of .feature cards (.ic emoji + .t title + .d description) for "特点/能力" sections.Close with .footer — centered, faint gray, <b> for the doc name, a <br> for related-doc links.
| Doc type | Style | Reason |
|---|---|---|
| 中文流程详解 / 模式详解 / 链路图 / 节点拆解 / 参数表 | Chinese Technical Spec | Noto SC fonts + flow/funnel components are built for CJK pipeline docs |
| English doc / API reference / prose-heavy | International | Playfair/Inter system |
| 中文方案 / 提案 (proposal without flow) | International (purple) | Proposal structure components live there |
| Chinese research report with data & tables | Either — prefer CN style if it has a pipeline, else International | — |
Always use this three-font system for maximum polish:
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,700;0,800;0,900;1,400;1,700&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
| Role | Font | Usage |
|---|---|---|
| Display / Headings | Playfair Display (800 weight, -0.02em letter-spacing) | Page titles, section headers, emphasis words |
| Body / UI | Inter (400/500/600/700) | Paragraphs, lists, labels, metadata |
| Code / Technical | JetBrains Mono (500/600) | Inline code, code blocks, badges, tags, numbers |
Chinese fallback chain: 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif
Two brand palettes — pick one based on the topic:
Indigo (technical, analytical, professional):
--brand-dark: #1e3a8a;
--brand: #4338ca;
--brand-light: #e0e7ff;
--brand-soft: #eef2ff;
Use for: fast mode, performance analysis, data flows, architecture
Purple (creative, refined, premium):
--brand-dark: #5b21b6;
--brand: #7c3aed;
--brand-light: #ede9fe;
--brand-soft: #f5f3ff;
Use for: precise mode, LLM content, design topics, methodology
LLM / Emphasis accent (when LLM is involved):
--accent-llm: #be185d; /* rose */
--accent-llm-light: #fce7f3;
Semantic colors (always include):
--success: #059669; --success-light: #d1fae5;
--warning: #d97706; --warning-light: #fef3c7;
--danger: #dc2626; --danger-light: #fee2e2;
--info: #0284c7; --info-light: #e0f2fe;
Surfaces:
--bg: #fafaf9;
--surface: #ffffff;
--surface-raised: #fcfcfc;
--border: #e5e7eb;
--border-strong: #d1d5db;
--text: #111827;
--text-secondary: #374151;
--text-muted: #6b7280;
--text-faint: #9ca3af;
Always use the 8px-based scale:
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
--r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 4px 12px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.04);
--shadow-lg: 0 12px 32px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.04);
--shadow-xl: 0 24px 48px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.06);
--shadow-brand: 0 8px 24px rgba(BRAND_RGB, 0.20), 0 2px 8px rgba(BRAND_RGB, 0.10);
Every page uses three aligned sections at 1280px max-width:
┌────────── Header (1280px, rounded 20px, gradient bg) ──────────┐
│ [eyebrow label] │
│ Page Title · <em>Emphasis Word</em> │
│ Subtitle text │
│ 📅 Date · 📦 Project · 🔖 Config │
└─────────────────────────────────────────────────────────────────┘
┌────────── TOC (1280px, rounded 14px, left brand bar) ──────────┐
│ ● Chapter 1 │
│ ● Chapter 2 │
│ ... │
└─────────────────────────────────────────────────────────────────┘
┌────────── Section 1 (1280px, rounded 14px) ────────────────────┐
│ ## Title with <em>emphasis</em> │
│ Content... │
└─────────────────────────────────────────────────────────────────┘
Key rules:
--shadow-sm → --shadow-md--shadow-brand (colored shadow matching brand)<header class="doc-header">
<div class="header-inner">
<span class="eyebrow">CATEGORY · SERIES</span>
<h1>Title · <em>emphasis</em> word</h1>
<div class="subtitle">Descriptive subtitle</div>
<div class="meta">
<span>📅 2026-07-14</span>
<span>•</span>
<span>📦 Project-Name</span>
<span>•</span>
<span>🔖 <code>key = "value"</code></span>
</div>
</div>
</header>
The eyebrow is a frosted pill badge with mono font, uppercase, 0.25em letter-spacing.
Use nav.toc with an ::before left brand-gradient bar. Two-column columns: 2 for link list. Each link has a dot (::before) that scales on hover. Must set list-style: none on nav.toc ol — since dots come from a::before, default <ol> numbers would create ugly double markers (e.g. "1. ● Chapter 1").
For parallel content that should be shown one at a time (e.g., language versions, multiple examples), use tab-based switching:
<!-- Tab bar -->
<div class="tabs" data-group="unique-group-name">
<button class="tab active" data-tab="option1">选项 1</button>
<button class="tab" data-tab="option2">选项 2</button>
</div>
<!-- Tab content panels -->
<div class="tab-content active" data-group="unique-group-name" data-tab="option1">
选项 1 的内容
</div>
<div class="tab-content" data-group="unique-group-name" data-tab="option2">
选项 2 的内容
</div>
CSS (include in <style>):
.tabs {
display: flex;
gap: var(--sp-1);
border-bottom: 2px solid var(--border);
margin-bottom: var(--sp-4);
}
.tab {
padding: var(--sp-2) var(--sp-4);
background: transparent;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
cursor: pointer;
font-family: var(--font-mono);
font-size: 0.85em;
font-weight: 600;
color: var(--text-muted);
transition: all 0.2s var(--ease);
}
.tab:hover { color: var(--brand); }
.tab.active {
color: var(--brand);
border-bottom-color: var(--brand);
}
.tab-content { display: none; }
.tab-content.active { display: block; }
JavaScript (include before </body>):
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.tabs').forEach(function(tabBar) {
const tabs = tabBar.querySelectorAll('.tab');
const tabGroup = tabBar.dataset.group;
const contents = document.querySelectorAll(`.tab-content[data-group="${tabGroup}"]`);
tabs.forEach(function(tab) {
tab.addEventListener('click', function() {
const target = tab.dataset.tab;
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
contents.forEach(c => c.classList.remove('active'));
const targetContent = document.querySelector(`.tab-content[data-group="${tabGroup}"][data-tab="${target}"]`);
if (targetContent) targetContent.classList.add('active');
});
});
});
});
Use cases:
Nested Tab Isolation (CRITICAL):
When nesting tabs (e.g., language tabs inside example tabs), follow these rules:
<!-- ✓ CORRECT: Each nesting level has unique data-group -->
<div class="tab-content" data-group="examples" data-tab="ex1">
<div class="tabs" data-group="example1-lang"> <!-- Unique group name -->
<button class="tab active" data-tab="en">EN</button>
<button class="tab" data-tab="zh">中文</button>
</div>
<div class="tab-content active" data-group="example1-lang" data-tab="en">
English content
</div>
<div class="tab-content" data-group="example1-lang" data-tab="zh">
Chinese content
</div>
</div>
<div class="tab-content" data-group="examples" data-tab="ex2">
<div class="tabs" data-group="example2-lang"> <!-- Different group name -->
<button class="tab active" data-tab="en">EN</button>
<button class="tab" data-tab="zh">中文</button>
</div>
<div class="tab-content active" data-group="example2-lang" data-tab="en">
English content
</div>
<div class="tab-content" data-group="example2-lang" data-tab="zh">
Chinese content
</div>
</div>
✗ WRONG: Shared data-group across nesting levels:
<div class="tab-content" data-group="examples" data-tab="ex1">
<div class="tabs" data-group="lang"> <!-- ❌ Same group name -->
...
</div>
</div>
<div class="tab-content" data-group="examples" data-tab="ex2">
<div class="tabs" data-group="lang"> <!-- ❌ Conflicts with ex1's lang tabs -->
...
</div>
</div>
No Orphaned Content:
tab-content divstab-content before starting the next oneJavaScript Scope Isolation:
The JavaScript MUST use :scope > to select only direct children, preventing cross-tab contamination:
// ✓ CORRECT: Scoped to parent element
const parentContainer = tabBar.parentElement;
const contents = parentContainer.querySelectorAll(`:scope > .tab-content[data-group="${tabGroup}"]`);
// ✗ WRONG: Selects ALL matching elements in document
const contents = document.querySelectorAll(`.tab-content[data-group="${tabGroup}"]`);
Rules:
data-group namedata-group names at each levelclass="active"data-tab to link to contentdata-group + data-tab for matching:scope > in JavaScript to scope selection to parenttab-content divs properly before starting next groupdata-group names across nesting levelsdocument.querySelectorAll for tab-content (use scoped selection).activetab-content that should be controlled by tabsInclude Prism CDN:
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-sql.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
Code blocks must have:
#0f172a (slate-900) — not pure black::after { content: '● ● ●'; } in red/orange/green::before { content: attr(data-lang); } uppercase monodata-lang attribute on the <code> element (e.g., <code class="language-python" data-lang="python">)--shadow-lg<pre class="code" data-lang="python"><code class="language-python">def hello():
print("world")
</code></pre>
<div class="card">
<h5>Card Title</h5>
<p>Card description text</p>
</div>
Cards have:
--r-md border radius--shadow-sm default, --shadow-md on hover-2px translateY lift on hover<div class="callout"> <!-- brand color -->
<div class="callout warning"> <!-- yellow -->
<div class="callout success"> <!-- green -->
<div class="callout danger"> <!-- red -->
<div class="callout llm"> <!-- rose pink, for LLM content -->
Left border 4px + gradient background fading to transparent (90deg).
<span class="badge badge-brand">BRAND</span>
<span class="badge badge-llm">LLM</span>
<span class="tag tag-query">query</span>
Badges: --r-20 pill, mono font, 0.7em size, 600 weight
Tags: --r-sm rectangle, mono font, 0.72em size
<table class="fields">
<thead><tr><th>Column 1</th><th>Column 2</th></tr></thead>
<tbody>
<tr><td><code>key</code></td><td>value</td></tr>
<tr class="hl"><td>highlighted row</td><td>...</td></tr>
</tbody>
</table>
Tables have:
--r-md radius + overflow: hidden.hl class for red-tinted emphasis rowsFor flow diagrams and architecture visuals, create inline SVG with:
<defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;800&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap');
.title { font-family: 'Playfair Display', Georgia, serif; font-weight: 800; }
.body { font-family: 'Inter', -apple-system, sans-serif; }
.mono { font-family: 'JetBrains Mono', monospace; font-weight: 600; }
</style>
</defs>
Important: Use raw & in CSS URLs inside <style> blocks (NOT &). The & HTML entity encoding is only needed in HTML attributes and text content, not inside <style> tags (including those embedded in SVG). This applies to both parent HTML and embedded SVGs.
<linearGradient id="g-brand" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3730a3"/>
<stop offset="50%" stop-color="#4338ca"/>
<stop offset="100%" stop-color="#6366f1"/>
</linearGradient>
Use 3-stop gradients for richness. For precise mode, shift to purple (#5b21b6 → #7c3aed → #8b5cf6).
<filter id="shadow-soft" x="-20%" y="-20%" width="140%" height="160%">
<feDropShadow dx="0" dy="1" stdDeviation="1" flood-color="#1e3a8a" flood-opacity="0.08"/>
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#1e3a8a" flood-opacity="0.10"/>
<feDropShadow dx="0" dy="12" stdDeviation="16" flood-color="#1e3a8a" flood-opacity="0.06"/>
</filter>
<filter id="shadow-glow" x="-30%" y="-30%" width="160%" height="180%">
<feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="#4338ca" flood-opacity="0.35"/>
</filter>
Three-layer shadow = realistic depth. Glow filter for important/emphasis nodes.
<g filter="url(#shadow-soft)">
<rect x="..." y="..." width="..." height="..." rx="12"
fill="url(#g-brand-card)" stroke="#c7d2fe" stroke-width="1"/>
<!-- ⚠️ CRITICAL: Use SINGLE <path> for title bar — NEVER two stacked <rect> -->
<!-- Two stacked rects create a visible seam line at their boundary -->
<!-- Path formula for top-rounded rect: -->
<!-- M x y+h V y+r Q x y x+r y H x+w-r Q x+w y x+w y+r V y+h Z -->
<path d="M {x} {y+h} V {y+r} Q {x} {y} {x+r} {y} H {x+w-r}
Q {x+w} {y} {x+w} {y+r} V {y+h} Z" fill="url(#g-brand)"/>
<!-- Traffic light dots -->
<circle cx="..." cy="..." r="4" fill="#fbbf24"/>
<circle cx="..." cy="..." r="4" fill="#fb923c"/>
<circle cx="..." cy="..." r="4" fill="#34d399"/>
<text x="..." y="..." class="mono" font-size="11" fill="white"
letter-spacing="0.5">chapter · subtitle</text>
<!-- Content text -->
<text class="body" font-size="11.5" fill="#1f2937">...</text>
</g>
Concrete example (x=40, y=100, w=320, h=90, title-h=30, r=12):
<path d="M 40 130 V 112 Q 40 100 52 100 H 348 Q 360 100 360 112 V 130 Z"
fill="url(#g-brand)"/>
<!-- Base gradient -->
<linearGradient id="g-bg" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#fafaf9"/>
<stop offset="100%" stop-color="#ffffff"/>
</linearGradient>
### Background (Clean Approach)
```svg
<!-- ✓ PREFERRED: Clean solid background — NO grid patterns -->
<rect width="1200" height="520" fill="#fafaf9"/>
<!-- Subtle decorative halos (optional, very low opacity) -->
<circle cx="200" cy="200" r="300" fill="#4338ca" opacity="0.03"/>
<circle cx="1000" cy="900" r="400" fill="#6366f1" opacity="0.02"/>
Avoid grid patterns — they add visual noise and make diagrams look cluttered. Premium SVGs use clean backgrounds with subtle color halos only when needed.
#dc2626 for critical values (sizes, thresholds, counts)Text labels placed on or near arrows must always have a background — never bare text.
For diagonal arrows: keep labels horizontal and center them EXACTLY at the arrow's midpoint.
<!-- ✓ GOOD: horizontal label, rx=3 (nearly square), centered at arrow midpoint -->
<!-- Calculate midpoint: mx = (x1+x2)/2, my = (y1+y2)/2 -->
<!-- Rect positioned so its center is at (mx, my) -->
<rect x="546" y="175" width="78" height="20" rx="3"
fill="#d1fae5" stroke="#059669" stroke-width="1"/>
<text x="585" y="189" text-anchor="middle" class="mono" font-size="10"
fill="#059669">✓ Cache Hit</text>
<!-- ✗ BAD: pill/capsule shape (rx=10+) on diagonal arrow -->
<rect x="555" y="165" width="78" height="20" rx="10" fill="#d1fae5"/>
<!-- ✗ BAD: rotated label group (unnecessary, looks awkward) -->
<g transform="rotate(-32, 585, 185)">
<rect .../>
<text .../>
</g>
<!-- ✗ BAD: bare text on arrow -->
<text x="580" y="175" fill="#059669">✓ Cache Hit</text>
Positioning rules:
mx = (x1+x2)/2, my = (y1+y2)/2x = mx - width/2, y = my - height/2x = mx, y = my + 4 (baseline offset)text-anchor="middle" for centeringrx="3" (nearly square corners) — NOT rx="10" (capsule shape)Background color rules:
fill="#d1fae5" (success-light) with stroke="#059669"fill="#fee2e2" (danger-light) with stroke="#dc2626"fill="#e0f2fe" (info-light) with stroke="#0284c7"fill="var(--brand-soft)" with stroke="var(--brand)"The final arrow segment MUST be perpendicular to the edge it connects to. Diagonal arrows across the diagram are confusing and look unprofessional.
For connections between nodes at different vertical positions, use L-shaped paths:
<!-- ✗ BAD: diagonal line crossing the diagram -->
<line x1="1020" y1="260" x2="600" y2="360" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arr)"/>
<!-- ✓ GOOD: L-shaped path with perpendicular final arrow -->
<path d="M1020 260 L1020 300 L600 300 L600 360" stroke="#7c3aed" stroke-width="2.5" marker-end="url(#arr)" fill="none"/>
L-shape path formula:
Example routing:
Node A bottom (1020, 260)
│
│ (vertical down to y=300)
▼
(1020, 300)
│
│ (horizontal left to x=600)
├──────────────
│
(600, 300)
│
│ (vertical down to Node B top)
▼
Node B top (600, 360) ← arrow perpendicular to horizontal edge
Rules:
<path> with multiple L commands for L-shapesorient="auto"<line> elements between distant nodesC, Q) for simple connections — they're unpredictableFor phase-based timelines (3+ phases), use this premium layout structure:
┌─ 标题区 (Phase 卡片) ─┐ ┌─ ... ─┐ ┌─ ... ─┐
│ Phase N · Week X-Y │
│ 阶段标题 │
│ 关键任务 │
└──────────┬────────────┘
│
─────────●═════════════════════════● ✓
渐变进度条 完成节点
──────────────────────────────────────
┌─ 交付物卡片 ─┐ ┌─ ... ─┐ ┌─ ... ─┐
│ 交付物 1 │
│ 交付物 2 │
└──────────────┘
图例
Key design rules:
rx="10" rounded rect with brand gradient title bar:<g filter="url(#shadow-sm)">
<rect x="130" y="80" width="180" height="70" rx="10"
fill="url(#g-muted)" stroke="#c7d2fe" stroke-width="1"/>
<!-- Title bar using path for top-rounded corners only -->
<path d="M130 104 V 90 Q 130 80 140 80 H 300 Q 310 80 310 90 V 104 Z"
fill="url(#g-brand)"/>
<text x="145" y="97" class="mono" font-size="11" fill="white">Phase 1 · Week 1-2</text>
<text x="145" y="124" class="title" font-size="15" fill="#1e3a8a">阶段标题</text>
<text x="145" y="143" class="body" font-size="10.5" fill="#374151">任务描述</text>
</g>
<!-- Background (entire timeline, muted) -->
<rect x="100" y="178" width="1000" height="6" rx="3" fill="#e0e7ff"/>
<!-- Progress (completed portion, gradient from brand to success) -->
<rect x="100" y="178" width="850" height="6" rx="3" fill="url(#g-timeline)"/>
<circle cx="220" cy="181" r="10" fill="url(#g-brand)" stroke="white" stroke-width="3"/>
<text x="220" y="185" text-anchor="middle" class="mono" font-size="10" fill="white">1</text>
<circle cx="1090" cy="181" r="14" fill="url(#g-success)"
stroke="white" stroke-width="3" filter="url(#shadow-sm)"/>
<text x="1090" y="186" text-anchor="middle" class="mono" font-size="12" fill="white">✓</text>
<rect x="140" y="238" width="160" height="56" rx="6"
fill="#f1f5f9" stroke="#e2e8f0" stroke-width="1"/>
<text x="150" y="254" class="mono" font-size="10" fill="#4338ca" font-weight="600">交付物</text>
<text x="150" y="270">• 交付物 1</text>
<text x="150" y="286">• 交付物 2</text>
<g transform="translate(100, 325)">
<rect x="0" y="-10" width="12" height="12" rx="3" fill="url(#g-brand)"/>
<text x="18" y="0">进行中</text>
<!-- ... more legend items -->
</g>
Timeline gradient for progress bar:
<linearGradient id="g-timeline" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#e0e7ff"/>
<stop offset="50%" stop-color="#4338ca"/>
<stop offset="100%" stop-color="#059669"/>
</linearGradient>
When coloring text inside SVG code blocks, use these VS Code-inspired colors:
Keywords (.kw): #c084fc (purple)
Functions (.fn): #fde68a (yellow)
Types (.tp): #67e8f9 (cyan)
Strings (.st): #86efac (green)
Comments (.cm): #64748b (slate, italic)
Variables (.var): #7dd3fc (light blue)
Numbers (.num): #fda4af (pink)
templates/css-system-cn.css, cover + part dividers + flow/funnel); otherwise International style (Indigo for technical/analytical, Purple for creative/LLM)stat-card components| Proposal Type | Recommended Palette | Reasoning |
|---|---|---|
| 技术方案 | Indigo | 专业、可信赖、强调数据 |
| 项目方案 | Indigo | 结构化、严肃、重视里程碑 |
| 架构方案 | Purple | 创意、设计感、视觉层次 |
| 设计方案 | Purple | 审美、风格、用户体验导向 |
| 实施方案 | Indigo | 严谨、时间线、可执行性 |
| 研究报告 | Indigo | 数据驱动、客观、分析性 |
<!-- Technical proposal -->
<header class="doc-header">
<div class="header-inner">
<span class="eyebrow">Technical Proposal · v2.1 · DRAFT</span>
<h1>系统架构升级方案 · <em>微服务化改造</em></h1>
<div class="subtitle">将单体架构拆分为领域驱动的微服务架构,提升系统可扩展性</div>
<div class="meta">
<span>📅 2026-07-14</span>
<span>•</span>
<span>👥 Architecture Team</span>
<span>•</span>
<span>🔖 <code>Status: Pending Review</code></span>
</div>
</div>
</header>
Before delivering:
.tag + serif h1 + .sub + .meta + .kpisPart 01… with .part dividers; h2.sec has 48px purple underline.exec 管理摘要 present for pipeline docs& (NOT &)list-style: none (no double number+dot markers)<path> (NOT two stacked <rect> — causes seam line)<rect height="4">) on nodes — they create unwanted seam lines<em> with brand/rose color❌ System fonts (Arial, Helvetica) as display type
❌ Pure black (#000) backgrounds for code — use slate-900 (#0f172a)
❌ Flat design with no shadows — always use layered shadows
❌ Generic purple gradients on white — our brand purple is deeper/sophisticated
❌ Inconsistent widths between header, TOC, and content
❌ Missing data-lang attribute on code blocks
❌ SVG with different fonts than the page
❌ Flat callout backgrounds (use gradient fade)
❌ Emoji-only icons without context
❌ Generic card layouts without hover effects
❌ & in CSS URLs inside <style> blocks (use raw &)
❌ TOC with both list numbers AND bullet dots (use list-style: none on <ol>)
❌ Bare text labels on SVG arrows without background boxes
❌ Capsule-shaped labels (rx="10"+) on arrows — use rx="3" (nearly square) instead
❌ Rotated label groups on diagonal arrows — keep labels horizontal, center at arrow midpoint
❌ Diagonal <line> elements between distant nodes — use L-shaped <path> instead
❌ Bezier curves (C, Q) for simple arrow connections — unpredictable rendering
❌ Arrow final segment not perpendicular to target edge
❌ Arrow paths crossing through cards — route around instead
❌ SVG title bars made of two stacked <rect> elements (causes visible seam line — use single <path> with top-rounded corners only)
❌ Thin decorative accent bars (<rect height="4" opacity="0.3-0.6">) on top of nodes — they create visible horizontal seam lines
❌ Grid pattern backgrounds on SVG diagrams (<pattern> with lines) — adds visual noise, makes diagrams look cluttered
❌ Timeline with only bare circles on a line — use phase cards + gradient bar + deliverable cards + legend
❌ Timeline with a single solid color progress bar — use TWO stacked rects (light background + gradient progress)
| File | Purpose |
|---|---|
templates/css-system.css | International style — complete CSS with all components |
templates/css-system-cn.css | Chinese Technical Spec style (Multi-ES) — cover hero, part dividers, vertical flow, funnel, tinted callouts |
templates/svg-components.svg | Reusable SVG card templates |