Files
wangjia ad845328fb feat(ds-flow): Phase 1.5 — 原型活登记页 index.html
design/prototype/index.html:设计系统组件登记簿(照 jiu 骨架,light/dark 双主题)。
顶栏主题切换(持久化 localStorage)+ 左侧 dnav scrollspy 导航 + 分区:
① 色板(clay/sand 色阶 + 语义 token,data-swatches 声明式随主题刷新)
② 字号梯度(display-xl→caption,中英分行不并排)
③ 圆角/间距/阴影比例尺
④ 公用组件(atoms.css 每个 class 一张展示卡,全变体全态含 disabled)
⑤ 图标库(icons.js 全 57 图标网格)

链 tokens.css + atoms.css + icons.js(相对路径,自包含)。
验证:每个 atom 类均登记、57 图标全展示、主题切换正常、文案脱敏无红线词。
评审:node design/prototype/serve.mjs → http://localhost:5180/

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 00:16:47 +08:00

408 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>穿山甲 Pangolin · 设计系统</title>
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="atoms.css">
<script src="icons.js"></script>
<style>
/* ── 登记页自身骨架(chrome)— 尽量走 token,登记页外壳可适度宽松 ── */
body{ margin:0; background:var(--bg); color:var(--fg1); font-family:var(--font-sans);
-webkit-font-smoothing:antialiased; transition:background-color var(--dur-base) var(--ease-out); overflow-x:hidden; }
.ic{ width:22px; height:22px; color:currentColor; }
/* 顶栏 */
.ds-top{ position:sticky; top:0; z-index:50; height:60px; background:var(--surface);
border-bottom:1px solid var(--border); display:flex; align-items:center; gap:14px;
padding:0 var(--space-6); box-shadow:var(--shadow-sm); }
.ds-brand{ display:flex; align-items:center; gap:var(--space-3); font-family:var(--font-display);
font-weight:700; color:var(--fg1); font-size:var(--text-body); }
.ds-brand .mk{ width:30px; height:30px; border-radius:var(--radius-md);
background:linear-gradient(135deg,var(--clay-500),var(--clay-800));
color:#fff; display:flex; align-items:center; justify-content:center; }
.ds-brand .mk svg{ width:17px; height:17px; }
.ds-tag{ font-size:var(--text-caption); color:var(--fg3); padding-left:var(--space-3);
border-left:1px solid var(--border); font-weight:500; }
.theme-toggle{ margin-left:auto; display:inline-flex; align-items:center; gap:var(--space-2);
background:var(--bg-subtle); border:1px solid var(--border); color:var(--fg2);
border-radius:var(--radius-full); padding:var(--space-2) var(--space-4);
font-family:var(--font-sans); font-weight:600; font-size:var(--text-caption);
cursor:pointer; transition:border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.theme-toggle:hover{ border-color:var(--border-strong); color:var(--fg1); }
.theme-toggle svg{ width:15px; height:15px; }
/* 三栏文档布局 */
.doc{ display:flex; align-items:flex-start; max-width:1360px; margin:0 auto; }
.doc-nav{ width:238px; flex:none; position:sticky; top:60px; height:calc(100vh - 60px);
overflow:auto; border-right:1px solid var(--border); padding:var(--space-5) var(--space-4); }
.dgroup{ font-size:var(--text-caption); font-weight:700; color:var(--fg3); letter-spacing:var(--tracking-caps);
text-transform:uppercase; padding:var(--space-4) var(--space-2) var(--space-2); }
.dnav{ display:block; padding:var(--space-2) var(--space-3); border-radius:var(--radius-md);
font-size:var(--text-sm); color:var(--fg2); text-decoration:none; cursor:pointer;
transition:background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.dnav:hover{ background:var(--bg-subtle); color:var(--fg1); }
.dnav.on{ background:var(--accent-subtle); color:var(--accent); font-weight:600; }
.doc-main{ flex:1; min-width:0; padding:var(--space-8) var(--space-10) var(--space-16); max-width:1080px; }
.section{ margin-bottom:var(--space-12); scroll-margin-top:76px; }
.section > h2{ margin:0 0 var(--space-1); font-family:var(--font-display); font-size:var(--text-h1);
font-weight:700; letter-spacing:var(--tracking-tight); color:var(--fg1); }
.section > .desc{ margin:0 0 var(--space-5); font-size:var(--text-sm); color:var(--fg2); line-height:var(--leading-normal); }
.section > .desc code{ font-family:var(--font-mono); font-size:0.85em; color:var(--accent);
background:var(--accent-subtle); padding:1px 6px; border-radius:var(--radius-sm); }
/* 展示卡(页面 chrome,非 atoms.card 以免混淆语义) */
.pcard{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-lg);
margin-bottom:var(--space-4); overflow:hidden; }
.pcard-h{ display:flex; align-items:baseline; gap:var(--space-3); padding:var(--space-3) var(--space-5);
border-bottom:1px solid var(--border); }
.pcard-h h3{ margin:0; font-family:var(--font-sans); font-size:var(--text-h3); font-weight:600; color:var(--fg1); }
.pcard-h .tk{ font-size:var(--text-caption); color:var(--fg3); font-family:var(--font-mono); }
.pcard-b{ padding:var(--space-5); display:flex; flex-wrap:wrap; gap:var(--space-4); align-items:center; }
.pcard-b.col{ display:block; }
.cap{ width:100%; font-size:var(--text-caption); color:var(--fg3); font-family:var(--font-mono);
margin:var(--space-4) 0 var(--space-2); }
.cap:first-child{ margin-top:0; }
/* 色板 */
.sw-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:var(--space-3); width:100%; }
.sw{ border:1px solid var(--border); border-radius:var(--radius-md); overflow:hidden; background:var(--surface); }
.sw .band{ display:block; width:100%; height:56px; box-shadow:inset 0 0 0 1px rgba(45,30,20,.08); }
.sw .m{ padding:var(--space-2) var(--space-3); }
.sw .m .n{ font-size:var(--text-caption); color:var(--fg1); font-weight:600; }
.sw .m .v{ font-size:11px; color:var(--fg3); font-family:var(--font-mono); }
/* 字号梯度 */
.type-row{ display:flex; align-items:baseline; gap:var(--space-5); padding:var(--space-3) 0;
border-bottom:1px solid var(--border); width:100%; }
.type-row:last-child{ border-bottom:none; }
.type-row .tk{ width:170px; font-size:var(--text-caption); color:var(--fg3);
font-family:var(--font-mono); flex:none; }
.type-row .spec{ display:flex; flex-direction:column; gap:2px; min-width:0; }
/* 圆角 / 间距 / 阴影 比例尺 */
.scale{ display:flex; flex-wrap:wrap; gap:var(--space-5); align-items:flex-end; width:100%; }
.scale .box{ display:flex; flex-direction:column; align-items:center; gap:var(--space-2);
font-size:var(--text-caption); color:var(--fg2); font-family:var(--font-mono); }
.scale .rq{ width:60px; height:60px; background:var(--accent-subtle); border:1.5px solid var(--accent); }
.scale .sq{ height:38px; background:var(--accent); border-radius:2px; }
.scale .sh{ width:72px; height:52px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); }
/* 图标库网格 */
.icon-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(108px,1fr)); gap:var(--space-2); width:100%; }
.icell{ display:flex; flex-direction:column; align-items:center; gap:var(--space-2);
padding:var(--space-4) var(--space-2); border:1px solid var(--border);
border-radius:var(--radius-md); background:var(--surface); color:var(--fg1); }
.icell .ic{ width:24px; height:24px; }
.icell span{ font-size:11px; color:var(--fg2); font-family:var(--font-mono); text-align:center; word-break:break-all; line-height:1.3; }
.count-pill{ font-family:var(--font-sans); }
@media (max-width:840px){
.doc-nav{ display:none; }
.doc-main{ padding:var(--space-6) var(--space-5) var(--space-12); }
}
</style>
</head>
<body>
<div class="ds-top">
<div class="ds-brand"><span class="mk"><svg class="ic"><use href="#i-shield-check"/></svg></span>穿山甲 Pangolin</div>
<span class="ds-tag">设计系统登记簿 · Prototype</span>
<button class="theme-toggle" id="themeBtn" aria-label="切换主题">
<svg class="ic"><use href="#i-sun" id="themeIcon"/></svg><span id="themeLabel">浅色</span>
</button>
</div>
<div class="doc">
<nav class="doc-nav" id="docNav">
<div class="dgroup">色彩 Colors</div>
<a class="dnav" data-s="c-clay">clay 黏土铜色阶</a>
<a class="dnav" data-s="c-sand">sand 沙米中性阶</a>
<a class="dnav" data-s="c-hue">语义色相</a>
<a class="dnav" data-s="c-sem">语义 token</a>
<div class="dgroup">字体 Type</div>
<a class="dnav" data-s="type">字号梯度</a>
<div class="dgroup">尺度 Scale</div>
<a class="dnav" data-s="radius">圆角</a>
<a class="dnav" data-s="space">间距</a>
<a class="dnav" data-s="shadow">阴影</a>
<div class="dgroup">组件 Atoms</div>
<a class="dnav" data-s="a-btn">按钮 .btn</a>
<a class="dnav" data-s="a-card">卡片 .card</a>
<a class="dnav" data-s="a-input">输入 .input</a>
<a class="dnav" data-s="a-pill">胶囊 .pill / .badge</a>
<a class="dnav" data-s="a-lang">语言下拉 .langsel</a>
<div class="dgroup">图标 Icons</div>
<a class="dnav" data-s="icons">Lucide 图标库</a>
</nav>
<main class="doc-main">
<!-- ══════════ 色彩 ══════════ -->
<section class="section" id="c-clay">
<h2>clay · 黏土铜色阶</h2>
<p class="desc">品牌主色阶(穿山甲鳞甲的暖大地色)。<code>--clay-500</code> 为品牌主色,浅色主题 accent 取 500、深色取 400。</p>
<div class="pcard"><div class="pcard-b"><div class="sw-grid" data-swatches="--clay-50,--clay-100,--clay-200,--clay-300,--clay-400,--clay-500,--clay-600,--clay-700,--clay-800,--clay-900"></div></div></div>
</section>
<section class="section" id="c-sand">
<h2>sand · 沙米中性阶</h2>
<p class="desc">暖中性色阶。浅色画布 <code>--sand-50</code>,深色画布暖 espresso <code>--sand-950</code>(非纯黑)。</p>
<div class="pcard"><div class="pcard-b"><div class="sw-grid" data-swatches="--sand-50,--sand-100,--sand-200,--sand-300,--sand-400,--sand-500,--sand-600,--sand-700,--sand-800,--sand-900,--sand-950"></div></div></div>
</section>
<section class="section" id="c-hue">
<h2>语义色相</h2>
<p class="desc">地色调校过的三组状态色相:绿=连接/安全,琥珀=连接中/提示,红=断开/错误。</p>
<div class="pcard"><div class="pcard-b"><div class="sw-grid" data-swatches="--green-400,--green-500,--green-600,--amber-400,--amber-500,--amber-600,--red-400,--red-500,--red-600"></div></div></div>
</section>
<section class="section" id="c-sem">
<h2>语义 token</h2>
<p class="desc">界面只引用这一层(浅/深自动切换)。切右上主题看色值实时变化。</p>
<div class="pcard"><div class="pcard-h"><h3>背景 / 表面</h3><span class="tk">--bg · --surface · --overlay</span></div><div class="pcard-b"><div class="sw-grid" data-swatches="--bg,--bg-subtle,--surface,--surface-2,--overlay"></div></div></div>
<div class="pcard"><div class="pcard-h"><h3>前景 / 文本</h3><span class="tk">--fg1/2/3 · --fg-on-accent</span></div><div class="pcard-b"><div class="sw-grid" data-swatches="--fg1,--fg2,--fg3,--fg-on-accent"></div></div></div>
<div class="pcard"><div class="pcard-h"><h3>品牌 accent</h3><span class="tk">--accent(+hover/press/subtle/border)</span></div><div class="pcard-b"><div class="sw-grid" data-swatches="--accent,--accent-hover,--accent-press,--accent-subtle,--accent-border"></div></div></div>
<div class="pcard"><div class="pcard-h"><h3>描边 / 光环</h3><span class="tk">--border · --border-strong · --ring</span></div><div class="pcard-b"><div class="sw-grid" data-swatches="--border,--border-strong,--ring"></div></div></div>
<div class="pcard"><div class="pcard-h"><h3>状态语义</h3><span class="tk">success / warning / danger (+ -subtle)</span></div><div class="pcard-b"><div class="sw-grid" data-swatches="--success,--success-subtle,--warning,--warning-subtle,--danger,--danger-subtle"></div></div></div>
</section>
<!-- ══════════ 字号 ══════════ -->
<section class="section" id="type">
<h2>字号梯度</h2>
<p class="desc">Display 用 Sora,正文/UI 用 Manrope,中文伴随 Noto Sans SC,数据用 JetBrains Mono。示例单语显示(中英分行,非并排同句)。</p>
<div class="pcard"><div class="pcard-b col">
<div class="type-row"><span class="tk">--text-display-xl 48</span><span class="spec"><span class="t-display-xl">极速畅连</span><span class="t-display-xl">Fast &amp; Stable</span></span></div>
<div class="type-row"><span class="tk">--text-display 36</span><span class="spec"><span class="t-display">网络加速</span><span class="t-display">Network Boost</span></span></div>
<div class="type-row"><span class="tk">--text-h1 30</span><span class="spec"><span class="t-h1">一键连接</span><span class="t-h1">One-tap Connect</span></span></div>
<div class="type-row"><span class="tk">--text-h2 24</span><span class="spec"><span class="t-h2">智能选线</span><span class="t-h2">Smart Routing</span></span></div>
<div class="type-row"><span class="tk">--text-h3 20</span><span class="spec"><span class="t-h3">隐私保护</span><span class="t-h3">Privacy First</span></span></div>
<div class="type-row"><span class="tk">--text-body-lg 18</span><span class="spec"><span class="t-body-lg">稳定加速线路,即开即用。</span><span class="t-body-lg">Stable links, ready instantly.</span></span></div>
<div class="type-row"><span class="tk">--text-body 16</span><span class="spec"><span class="t-body">正文与界面主力字号,兼顾中英排版。</span><span class="t-body">Body copy for UI and long-form reading.</span></span></div>
<div class="type-row"><span class="tk">--text-sm 14</span><span class="spec"><span class="t-sm">次要说明与表单标签。</span><span class="t-sm">Secondary hints and form labels.</span></span></div>
<div class="type-row"><span class="tk">--text-caption 12</span><span class="spec"><span class="t-caption">辅助注释与胶囊文字。</span><span class="t-caption">Captions and pill text.</span></span></div>
<div class="type-row"><span class="tk">.t-overline</span><span class="spec"><span class="t-overline">连接状态</span><span class="t-overline">Connection Status</span></span></div>
<div class="type-row"><span class="tk">.t-mono 数据</span><span class="spec"><span class="t-mono">103.119.13.48 · 42 ms · 1.28 GB</span></span></div>
</div></div>
</section>
<!-- ══════════ 尺度 ══════════ -->
<section class="section" id="radius">
<h2>圆角 Radius</h2>
<p class="desc">偏大圆角传递亲和。按钮全胶囊 <code>--radius-full</code>,卡片 lgxl,输入 md。</p>
<div class="pcard"><div class="pcard-b"><div class="scale">
<div class="box"><div class="rq" style="border-radius:var(--radius-sm)"></div>sm · 6</div>
<div class="box"><div class="rq" style="border-radius:var(--radius-md)"></div>md · 10</div>
<div class="box"><div class="rq" style="border-radius:var(--radius-lg)"></div>lg · 14</div>
<div class="box"><div class="rq" style="border-radius:var(--radius-xl)"></div>xl · 20</div>
<div class="box"><div class="rq" style="border-radius:var(--radius-2xl)"></div>2xl · 28</div>
<div class="box"><div class="rq" style="border-radius:var(--radius-full)"></div>full · 999</div>
</div></div></div>
</section>
<section class="section" id="space">
<h2>间距 Spacing</h2>
<p class="desc">4px 基准网格。</p>
<div class="pcard"><div class="pcard-b"><div class="scale">
<div class="box"><div class="sq" style="width:var(--space-1)"></div>1 · 4</div>
<div class="box"><div class="sq" style="width:var(--space-2)"></div>2 · 8</div>
<div class="box"><div class="sq" style="width:var(--space-3)"></div>3 · 12</div>
<div class="box"><div class="sq" style="width:var(--space-4)"></div>4 · 16</div>
<div class="box"><div class="sq" style="width:var(--space-5)"></div>5 · 20</div>
<div class="box"><div class="sq" style="width:var(--space-6)"></div>6 · 24</div>
<div class="box"><div class="sq" style="width:var(--space-8)"></div>8 · 32</div>
<div class="box"><div class="sq" style="width:var(--space-10)"></div>10 · 40</div>
<div class="box"><div class="sq" style="width:var(--space-12)"></div>12 · 48</div>
<div class="box"><div class="sq" style="width:var(--space-16)"></div>16 · 64</div>
</div></div></div>
</section>
<section class="section" id="shadow">
<h2>阴影 Shadow</h2>
<p class="desc">柔和暖调(<code>rgba(45,30,20,…)</code>),绝不用冷灰阴影。</p>
<div class="pcard"><div class="pcard-b"><div class="scale">
<div class="box"><div class="sh" style="box-shadow:var(--shadow-sm)"></div>sm</div>
<div class="box"><div class="sh" style="box-shadow:var(--shadow-md)"></div>md</div>
<div class="box"><div class="sh" style="box-shadow:var(--shadow-lg)"></div>lg</div>
<div class="box"><div class="sh" style="box-shadow:var(--shadow-xl)"></div>xl</div>
<div class="box"><div class="sh" style="box-shadow:var(--shadow-focus)"></div>focus</div>
</div></div></div>
</section>
<!-- ══════════ 组件 ══════════ -->
<section class="section" id="a-btn">
<h2>按钮 · .btn</h2>
<p class="desc">全胶囊圆角 · hover 主色加深 · press 缩放 .97 · focus clay 光环。变体 <code>.btn-primary/-ghost/-subtle/-danger</code>,尺寸 <code>.btn-lg/-icon/-block</code></p>
<div class="pcard"><div class="pcard-h"><h3>变体</h3><span class="tk">.btn + .btn-primary/-ghost/-subtle/-danger</span></div><div class="pcard-b">
<button class="btn btn-primary"><svg class="ic"><use href="#i-power"/></svg>点击连接</button>
<button class="btn btn-ghost"><svg class="ic"><use href="#i-refresh-cw"/></svg>刷新延迟</button>
<button class="btn btn-subtle"><svg class="ic"><use href="#i-settings"/></svg>偏好设置</button>
<button class="btn btn-danger"><svg class="ic"><use href="#i-power"/></svg>断开连接</button>
</div></div>
<div class="pcard"><div class="pcard-h"><h3>尺寸 / 形态</h3><span class="tk">.btn-lg · .btn-icon · .btn-block</span></div><div class="pcard-b">
<button class="btn btn-primary btn-lg"><svg class="ic"><use href="#i-zap"/></svg>智能选择</button>
<button class="btn btn-primary btn-icon" aria-label="更多"><svg class="ic"><use href="#i-more-vertical"/></svg></button>
<button class="btn btn-subtle btn-icon" aria-label="搜索"><svg class="ic"><use href="#i-search"/></svg></button>
<div style="width:100%"><button class="btn btn-primary btn-block"><svg class="ic"><use href="#i-download"/></svg>立即下载</button></div>
</div></div>
<div class="pcard"><div class="pcard-h"><h3>禁用态</h3><span class="tk">:disabled · .is-disabled</span></div><div class="pcard-b">
<button class="btn btn-primary" disabled>已连接</button>
<button class="btn btn-subtle is-disabled">不可用</button>
<button class="btn btn-danger" disabled>断开连接</button>
</div></div>
</section>
<section class="section" id="a-card">
<h2>卡片 · .card</h2>
<p class="desc">surface 底 + border + radius-xl + shadow-sm。<code>.card-flush</code> 用于列表容器(内容自带 padding)。</p>
<div class="pcard"><div class="pcard-b">
<div class="card" style="max-width:280px">
<div class="t-overline" style="color:var(--fg3)">当前节点</div>
<div class="t-h3" style="margin:var(--space-2) 0 var(--space-1)">香港 · HK 加速线路</div>
<div class="t-sm" style="color:var(--fg2)">稳定 · 无日志 · 隐私保护</div>
</div>
<div class="card card-flush" style="max-width:280px; width:100%">
<div style="padding:var(--space-4) var(--space-5); border-bottom:1px solid var(--border)" class="t-sm">HK · 香港</div>
<div style="padding:var(--space-4) var(--space-5); border-bottom:1px solid var(--border)" class="t-sm">JP · 东京</div>
<div style="padding:var(--space-4) var(--space-5)" class="t-sm">SG · 新加坡</div>
</div>
</div></div>
</section>
<section class="section" id="a-input">
<h2>输入 · .input / .field / .flabel</h2>
<p class="desc">强描边 + radius-md + focus clay 光环。状态 <code>.is-focused</code> / <code>.is-error</code> / <code>:disabled</code></p>
<div class="pcard"><div class="pcard-b" style="align-items:flex-start">
<label class="field" style="width:220px"><span class="flabel">邮箱</span><input class="input" placeholder="me@pangolin.app"></label>
<label class="field" style="width:220px"><span class="flabel">焦点态</span><input class="input is-focused" value="已聚焦"></label>
<label class="field" style="width:220px"><span class="flabel">错误态</span><input class="input is-error" value="激活码无效"></label>
<label class="field" style="width:220px"><span class="flabel">禁用态</span><input class="input" value="不可编辑" disabled></label>
</div></div>
</section>
<section class="section" id="a-pill">
<h2>胶囊 · .pill / .badge</h2>
<p class="desc">状态用「色点 + 文字胶囊」,非 emoji。状态修饰 <code>.is-success/-warning/-danger/-neutral</code>,变体 <code>.pill-accent</code> / <code>.pill-outline</code><code>.badge</code><code>.pill</code> 语义别名。</p>
<div class="pcard"><div class="pcard-h"><h3>状态修饰</h3><span class="tk">.pill.is-*(含 .dot</span></div><div class="pcard-b">
<span class="pill is-success"><span class="dot"></span>已连接</span>
<span class="pill is-warning"><span class="dot"></span>连接中</span>
<span class="pill is-danger"><span class="dot"></span>已断开</span>
<span class="pill is-neutral"><span class="dot"></span>未连接</span>
<span class="pill"><span class="dot"></span>默认</span>
</div></div>
<div class="pcard"><div class="pcard-h"><h3>变体 / 别名</h3><span class="tk">.pill-accent · .pill-outline · .badge</span></div><div class="pcard-b">
<span class="pill pill-accent">PRO 会员</span>
<span class="pill pill-outline">流媒体</span>
<span class="pill pill-outline">P2P</span>
<span class="badge is-success"><span class="dot"></span>badge 别名</span>
</div></div>
</section>
<section class="section" id="a-lang">
<h2>语言下拉 · .langsel</h2>
<p class="desc">不依赖原生 <code>&lt;select&gt;</code>:触发药丸 <code>.langsel-trigger</code> + 自控绝对定位菜单 <code>.menu</code> / <code>.menu-item</code>(选中态 <code>.is-selected</code>)。点触发按钮可实际展开/收起。</p>
<div class="pcard"><div class="pcard-b" style="min-height:180px; align-items:flex-start">
<div class="langsel" id="langselDemo">
<button class="langsel-trigger" id="langTrigger" aria-expanded="true" aria-haspopup="true">
简体中文 <svg class="ic"><use href="#i-chevron-down"/></svg>
</button>
<div class="menu" id="langMenu">
<button class="menu-item is-selected" aria-selected="true">简体中文</button>
<button class="menu-item">English</button>
<button class="menu-item">繁體中文</button>
</div>
</div>
<span class="t-caption" style="color:var(--fg3)">↖ 默认展开演示,可点击触发按钮收起 / 展开</span>
</div></div>
</section>
<!-- ══════════ 图标 ══════════ -->
<section class="section" id="icons">
<h2>Lucide 图标库 <span class="pill pill-outline count-pill" id="iconCount"></span></h2>
<p class="desc">细线条 · 2px stroke · 圆角端点。sprite 单源 <code>icons.js</code>,用法 <code>&lt;svg class="ic"&gt;&lt;use href="#i-&lt;name&gt;"/&gt;&lt;/svg&gt;</code></p>
<div class="pcard"><div class="pcard-b"><div class="icon-grid" id="iconGrid"></div></div></div>
</section>
</main>
</div>
<script>
/* ── 主题切换(light / dark),持久化 localStorage ── */
function applyTheme(t){
if(t==='dark'){ document.documentElement.dataset.theme='dark'; }
else { delete document.documentElement.dataset.theme; }
document.getElementById('themeIcon').setAttribute('href', t==='dark' ? '#i-moon' : '#i-sun');
document.getElementById('themeLabel').textContent = t==='dark' ? '深色' : '浅色';
localStorage.setItem('pangolin-ds-theme', t);
renderSwatches();
}
document.getElementById('themeBtn').onclick = () => {
const cur = document.documentElement.dataset.theme==='dark' ? 'dark' : 'light';
applyTheme(cur==='dark' ? 'light' : 'dark');
};
/* ── 色板:读实际 CSS 变量(换主题刷新色值文本) ── */
function renderSwatches(){
const cs = getComputedStyle(document.documentElement);
document.querySelectorAll('[data-swatches]').forEach(grid=>{
grid.innerHTML = grid.dataset.swatches.split(',').map(tok=>{
const v = cs.getPropertyValue(tok).trim();
return `<div class="sw"><div class="band" style="background:var(${tok})"></div>`+
`<div class="m"><div class="n">${tok.replace('--','')}</div><div class="v">${v||'—'}</div></div></div>`;
}).join('');
});
}
/* ── 图标库:遍历 sprite 全量渲染 ── */
function renderIcons(){
const map = window.PANGOLIN_ICONS || {};
const names = Object.keys(map);
document.getElementById('iconCount').textContent = names.length + ' 个';
document.getElementById('iconGrid').innerHTML = names.map(n=>
`<div class="icell"><svg class="ic"><use href="#i-${n}"/></svg><span>${n}</span></div>`
).join('');
}
/* ── 语言下拉演示:点触发展开 / 收起 ── */
(function(){
const trigger = document.getElementById('langTrigger');
const sel = document.getElementById('langselDemo');
const menu = document.getElementById('langMenu');
sel.classList.add('is-open');
trigger.onclick = (e)=>{
e.stopPropagation();
const open = trigger.getAttribute('aria-expanded')==='true';
trigger.setAttribute('aria-expanded', String(!open));
sel.classList.toggle('is-open', !open);
menu.style.display = open ? 'none' : 'flex';
};
menu.querySelectorAll('.menu-item').forEach(mi=>{
mi.onclick = ()=>{
menu.querySelectorAll('.menu-item').forEach(x=>{ x.classList.remove('is-selected'); x.removeAttribute('aria-selected'); });
mi.classList.add('is-selected'); mi.setAttribute('aria-selected','true');
trigger.childNodes[0].nodeValue = mi.textContent + ' ';
};
});
})();
/* ── 导航 scrollspy ── */
const navLinks=[...document.querySelectorAll('.dnav')];
navLinks.forEach(a=>a.onclick=(e)=>{ e.preventDefault(); const el=document.getElementById(a.dataset.s); if(el) el.scrollIntoView({behavior:'smooth',block:'start'}); });
const obs=new IntersectionObserver(es=>{ es.forEach(en=>{ if(en.isIntersecting){ const id=en.target.id; navLinks.forEach(a=>a.classList.toggle('on',a.dataset.s===id)); } }); },{rootMargin:'-70px 0px -75% 0px'});
document.querySelectorAll('.section').forEach(s=>obs.observe(s));
/* ── initsprite 注入可能在 DOMContentLoaded,稍等确保 PANGOLIN_ICONS 就绪) ── */
function boot(){
applyTheme(localStorage.getItem('pangolin-ds-theme') || 'light');
renderIcons();
}
if(document.readyState!=='loading') boot(); else document.addEventListener('DOMContentLoaded', boot);
</script>
</body>
</html>
</content>
</invoke>