fa9a5c2d5e
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
944 lines
68 KiB
HTML
944 lines
68 KiB
HTML
<!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-y:auto; overscroll-behavior:contain; border-right:1px solid var(--border);
|
||
padding:var(--space-5) var(--space-4) calc(var(--space-16) * 2);
|
||
transition:width var(--dur-base) var(--ease-out); }
|
||
/* 收起态(宽度由 JS 置 0,此处收内边距/边框) */
|
||
.doc.nav-collapsed .doc-nav{ padding-left:0; padding-right:0; border-right:none; overflow:hidden; }
|
||
.doc.nav-collapsed .nav-resize{ display:none; }
|
||
/* 拖拽把手(改侧栏宽度) */
|
||
.nav-resize{ flex:none; align-self:stretch; position:sticky; top:60px; height:calc(100vh - 60px);
|
||
width:9px; margin-left:-5px; cursor:col-resize; z-index:40; background:transparent; }
|
||
.nav-resize::after{ content:""; position:absolute; left:4px; top:0; bottom:0; width:2px; border-radius:var(--radius-full);
|
||
background:transparent; transition:background-color var(--dur-fast) var(--ease-out); }
|
||
.nav-resize:hover::after, .nav-resize.dragging::after{ background:var(--accent); }
|
||
body.nav-dragging{ cursor:col-resize; user-select:none; }
|
||
/* 顶栏收起按钮 */
|
||
.nav-toggle{ display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px; flex:none;
|
||
border:1px solid var(--border); border-radius:var(--radius-md); background:var(--bg-subtle);
|
||
color:var(--fg2); cursor:pointer; transition:color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out); }
|
||
.nav-toggle:hover{ color:var(--fg1); border-color:var(--border-strong); }
|
||
.nav-toggle svg{ width:18px; height:18px; }
|
||
|
||
/* L1 分组头 —— 分隔线 + 提亮加粗,与二级明显区分 */
|
||
.dgroup{ font-size:var(--text-caption); font-weight:700; color:var(--fg2); letter-spacing:var(--tracking-caps);
|
||
text-transform:uppercase; padding:var(--space-2) var(--space-2);
|
||
margin-top:var(--space-5); border-top:1px solid var(--border); }
|
||
.doc-nav .dgroup:first-child{ margin-top:var(--space-1); border-top:none; }
|
||
/* L2 条目 —— 缩进内嵌于分组下 */
|
||
.dnav{ display:block; padding:var(--space-2) var(--space-3) var(--space-2) var(--space-5); 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; }
|
||
.nav-resize{ display:none; }
|
||
.nav-toggle{ display:none; }
|
||
.doc-main{ padding:var(--space-6) var(--space-5) var(--space-12); }
|
||
}
|
||
|
||
/* ── 品牌 / 字体 specimen 展示用(登记页 chrome,非 atoms;全走 token) ── */
|
||
.brand-appicon{ display:flex; align-items:flex-end; gap:var(--space-8); flex-wrap:wrap; }
|
||
.brand-appicon .unit{ display:flex; flex-direction:column; align-items:center; gap:var(--space-2); }
|
||
.brand-appicon .unit .n{ font-family:var(--font-mono); font-size:11px; color:var(--fg3); }
|
||
.brand-appicon img{ display:block; box-shadow:var(--shadow-md); border-radius:22%; }
|
||
.lockup{ display:flex; align-items:center; gap:var(--space-3); }
|
||
.lockup img{ display:block; }
|
||
.lockup .tw{ display:flex; flex-direction:column; line-height:1; }
|
||
.lockup .cn{ font-family:var(--font-display); font-weight:700; font-size:22px; letter-spacing:var(--tracking-snug); color:var(--fg1); }
|
||
.lockup .en{ font-family:var(--font-display); font-weight:600; font-size:10px; letter-spacing:0.22em; color:var(--accent); margin-top:4px; }
|
||
.lockup-panel{ flex:1; min-width:220px; display:flex; flex-direction:column; align-items:center; gap:var(--space-3);
|
||
padding:var(--space-8); border-radius:var(--radius-lg); border:1px solid var(--border); }
|
||
.lockup-panel .plab{ font-family:var(--font-mono); font-size:10px; color:var(--fg3); }
|
||
.spec-mono-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--space-5); width:100%; }
|
||
.spec-mono-grid .k{ font-family:var(--font-mono); font-size:11px; color:var(--fg3); margin-bottom:4px; }
|
||
.spec-mono-grid .v{ font-family:var(--font-mono); font-size:22px; font-weight:500; letter-spacing:var(--tracking-snug);
|
||
color:var(--fg1); font-feature-settings:'tnum' 1; }
|
||
.spec-mono-grid .v .u{ font-size:12px; color:var(--accent); font-weight:400; }
|
||
@media (max-width:560px){ .spec-mono-grid{ grid-template-columns:repeat(2,1fr); } }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="ds-top">
|
||
<button class="nav-toggle" id="navToggle" aria-label="收起 / 展开侧栏" title="收起 / 展开侧栏"><svg class="ic"><use href="#i-menu"/></svg></button>
|
||
<div class="ds-brand"><span class="mk"><img src="../assets/app-icon.svg" alt="穿山甲" style="width:100%;height:100%;object-fit:contain;border-radius:inherit"></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">UI Kit · 交互原型</div>
|
||
<a class="dnav" href="screens/ui-mobile.html" style="color:var(--accent);font-weight:600">↗ 移动版 App</a>
|
||
<a class="dnav" href="screens/ui-desktop.html" style="color:var(--accent);font-weight:600">↗ 桌面版</a>
|
||
<a class="dnav" href="screens/site.html" style="color:var(--accent);font-weight:600">↗ 官网 Site</a>
|
||
<div class="dgroup">品牌 Brand</div>
|
||
<a class="dnav" data-s="b-appicon">App 图标家族</a>
|
||
<a class="dnav" data-s="b-logo">Logo 锁版</a>
|
||
<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>
|
||
<a class="dnav" data-s="c-dark">暗色主题表面</a>
|
||
<div class="dgroup">字体 Type</div>
|
||
<a class="dnav" data-s="t-body">正文字体 Manrope</a>
|
||
<a class="dnav" data-s="t-cjk">中文字体 Noto Sans SC</a>
|
||
<a class="dnav" data-s="t-display">Display 字体 Sora</a>
|
||
<a class="dnav" data-s="t-mono">等宽数据字体 JetBrains Mono</a>
|
||
<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-viewbody">内容容器 .view-body</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>
|
||
<a class="dnav" data-s="a-connect">核心连接按钮 .connect-btn</a>
|
||
<a class="dnav" data-s="a-server">服务器列表行 .server-row</a>
|
||
<a class="dnav" data-s="a-badge">状态徽章 .pill 场景</a>
|
||
<div class="dgroup">组件 · 线上对齐</div>
|
||
<a class="dnav" data-s="a-formctl">复合输入 · 开关 · 图标框</a>
|
||
<a class="dnav" data-s="a-quota">额度卡 · 指标卡</a>
|
||
<a class="dnav" data-s="a-rows">设备行 · 设置行</a>
|
||
<a class="dnav" data-s="a-planbn">套餐横幅 · 会员卡</a>
|
||
<a class="dnav" data-s="a-appchrome">更新条 · Tab · 侧栏项</a>
|
||
<div class="dgroup">购买 Purchase</div>
|
||
<a class="dnav" data-s="a-plan">套餐卡 .plan-card</a>
|
||
<a class="dnav" data-s="a-paym">支付方式 .pay-method</a>
|
||
<a class="dnav" data-s="a-kv">明细行 .kv-row</a>
|
||
<a class="dnav" data-s="a-result">结果态 .result</a>
|
||
<div class="dgroup">图标 Icons</div>
|
||
<a class="dnav" data-s="icons">Lucide 图标库</a>
|
||
</nav>
|
||
|
||
<div class="nav-resize" id="navResize" title="拖动调整侧栏宽度"></div>
|
||
|
||
<main class="doc-main">
|
||
<!-- ══════════ 品牌 Brand ══════════ -->
|
||
<section class="section" id="b-appicon">
|
||
<h2>App 图标家族</h2>
|
||
<p class="desc">母题=行走穿山甲(实心鳞甲剪影,朝右)置于 clay 渐变圆角方。矢量单源 <code>design/assets/app-icon.svg</code>,多尺寸直接引用同一文件(零漂移,非内联副本)。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>多尺寸</h3><span class="tk">104 · 64 · 40 · 28</span></div><div class="pcard-b"><div class="brand-appicon">
|
||
<div class="unit"><img src="../assets/app-icon.svg" width="104" height="104" alt="穿山甲 app 图标 104"><span class="n">104 · 主图标</span></div>
|
||
<div class="unit"><img src="../assets/app-icon.svg" width="64" height="64" alt="app 图标 64"><span class="n">64</span></div>
|
||
<div class="unit"><img src="../assets/app-icon.svg" width="40" height="40" alt="app 图标 40"><span class="n">40</span></div>
|
||
<div class="unit"><img src="../assets/app-icon.svg" width="28" height="28" alt="app 图标 28"><span class="n">28</span></div>
|
||
</div></div></div>
|
||
</section>
|
||
|
||
<section class="section" id="b-logo">
|
||
<h2>Logo 锁版</h2>
|
||
<p class="desc">图标 mark + 字标水平锁版。浅色背景用主锁版 <code>logo-mark.svg</code>,深色背景用反白锁版 <code>logo-mark-white.svg</code>;整体横版字标 <code>logo-wordmark.svg</code>。矢量单源 <code>design/assets/</code>。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>主锁版 / 反白锁版</h3><span class="tk">logo-mark.svg · logo-mark-white.svg</span></div><div class="pcard-b" style="align-items:stretch">
|
||
<div class="lockup-panel" style="background:var(--sand-50)">
|
||
<div class="lockup"><img src="../assets/logo-mark.svg" width="46" height="46" alt="穿山甲 mark"><div class="tw"><span class="cn">穿山甲</span><span class="en">PANGOLIN</span></div></div>
|
||
<span class="plab">主锁版 · 浅色背景</span>
|
||
</div>
|
||
<div class="lockup-panel" data-theme="dark" style="background:var(--sand-950)">
|
||
<div class="lockup"><img src="../assets/logo-mark-white.svg" width="46" height="46" alt="穿山甲 mark 反白"><div class="tw"><span class="cn">穿山甲</span><span class="en">PANGOLIN</span></div></div>
|
||
<span class="plab">反白锁版 · 深色背景</span>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-h"><h3>横版字标</h3><span class="tk">logo-wordmark.svg · 240×56</span></div><div class="pcard-b" style="background:var(--sand-50)">
|
||
<img src="../assets/logo-wordmark.svg" width="240" height="56" alt="穿山甲 Pangolin 字标">
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 色彩 ══════════ -->
|
||
<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="c-dark">
|
||
<h2>暗色主题表面</h2>
|
||
<p class="desc">暗色画布=暖 espresso <code>--sand-950</code>(非纯黑)。表面层级 <code>--bg</code>(应用画布)→ <code>--surface</code>(卡片/面板)→ <code>--surface-2</code>(嵌套层),逐级提亮保持暖调。此卡固定演示暗色,不随右上主题切换。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>表面层级(固定暗色演示)</h3><span class="tk">--bg · --surface · --surface-2</span></div>
|
||
<div class="pcard-b" data-theme="dark" style="background:var(--sand-950); gap:var(--space-3)">
|
||
<div class="card" style="flex:1; min-width:150px; background:var(--bg)"><div class="t-h3" style="color:var(--fg1); font-size:15px">bg</div><div class="t-caption" style="color:var(--fg2); margin-top:4px">应用画布</div><div class="t-mono" style="color:var(--fg3); font-size:11px; margin-top:2px">#14110E</div></div>
|
||
<div class="card" style="flex:1; min-width:150px; background:var(--surface)"><div class="t-h3" style="color:var(--fg1); font-size:15px">surface</div><div class="t-caption" style="color:var(--fg2); margin-top:4px">卡片 / 面板</div><div class="t-mono" style="color:var(--fg3); font-size:11px; margin-top:2px">#221E19</div></div>
|
||
<div class="card" style="flex:1; min-width:150px; background:var(--surface-2)"><div class="t-h3" style="color:var(--fg1); font-size:15px">surface-2</div><div class="t-caption" style="color:var(--fg2); margin-top:4px">嵌套层</div><div class="t-mono" style="color:var(--fg3); font-size:11px; margin-top:2px">#2A251F</div><span class="pill pill-accent" style="margin-top:var(--space-3)">Accent</span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ══════════ 字体 specimen ══════════ -->
|
||
<section class="section" id="t-body">
|
||
<h2>正文字体 · Manrope</h2>
|
||
<p class="desc">界面与正文主力(<code>--font-sans</code>,权重 400–700)。人文几何,中英混排稳定。示例中英分行、单句不并排。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>Body · Manrope</h3><span class="tk">--font-sans · 400 / 500 / 600 / 700</span></div><div class="pcard-b col">
|
||
<p class="t-h3" style="margin:0 0 var(--space-2)">一键连接,极速畅连</p>
|
||
<p class="t-body-lg" style="margin:0 0 var(--space-2); color:var(--fg2)">轻盈、亲和、即开即用的网络加速。</p>
|
||
<p class="t-body" style="margin:0 0 var(--space-2)">Body 16px — 全球 80+ 加速线路,智能选择最快节点,稳定不掉线。</p>
|
||
<p class="t-sm" style="margin:0; color:var(--fg2)">Small 14px — 适用于次要说明、列表副标题与表单提示文字。</p>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="t-cjk">
|
||
<h2>中文字体 · Noto Sans SC</h2>
|
||
<p class="desc">中文专用伴随字体(<code>--font-cjk</code>,权重 400 / 500 / 700)。与 Manrope / Sora 同框时保证中文字形与字重一致。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>CJK · Noto Sans SC</h3><span class="tk">--font-cjk · 400 / 500 / 700</span></div><div class="pcard-b col" style="font-family:var(--font-cjk)">
|
||
<p style="margin:0; font-size:34px; font-weight:700; letter-spacing:var(--tracking-snug)">穿山甲 · 守护每一次连接</p>
|
||
<p style="margin:var(--space-3) 0 0; font-size:18px; font-weight:500; color:var(--fg2)">无需配置,打开即用。智能分流,海外应用秒开。</p>
|
||
<p style="margin:var(--space-2) 0 0; font-size:14px; color:var(--fg3)">隐私保护 · 我们不记录你的浏览数据 · 端到端加密</p>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="t-display">
|
||
<h2>Display 字体 · Sora</h2>
|
||
<p class="desc">标题、营销大字、Logo 字标(<code>--font-display</code>,权重 600 / 700)。几何友好,字距收紧 <code>-0.02em</code>。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>Display · Sora</h3><span class="tk">--font-display · 600 / 700</span></div><div class="pcard-b col">
|
||
<p class="t-display-xl" style="margin:0 0 var(--space-3); line-height:1.05">极速畅连 Smooth</p>
|
||
<p class="t-display" style="margin:0; line-height:1.05; color:var(--accent)">极速 · 稳定 · 省心</p>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="t-mono">
|
||
<h2>等宽数据字体 · JetBrains Mono</h2>
|
||
<p class="desc">IP / 速率 / 时长 / 密钥等数据读数(<code>--font-mono</code>,开 tabular nums 等宽对齐,数字不跳动)。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>Mono · JetBrains Mono</h3><span class="tk">--font-mono · tnum</span></div><div class="pcard-b"><div class="spec-mono-grid">
|
||
<div><div class="k">DOWNLOAD</div><div class="v">86.4<span class="u"> Mb/s</span></div></div>
|
||
<div><div class="k">PING</div><div class="v">24<span class="u"> ms</span></div></div>
|
||
<div><div class="k">UPTIME</div><div class="v">02:14:08</div></div>
|
||
<div><div class="k">EXIT IP</div><div class="v" style="font-size:16px">103.27.18.204</div></div>
|
||
<div><div class="k">PROTOCOL</div><div class="v" style="font-size:16px">REALITY</div></div>
|
||
<div><div class="k">DATA</div><div class="v">1.82<span class="u"> GB</span></div></div>
|
||
</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 & 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>,卡片 lg–xl,输入 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-viewbody">
|
||
<h2>内容容器 · .view-body</h2>
|
||
<p class="desc">桌面客户端内容视图统一宽度容器:默认居中 <code>600px</code>(账户/设置/联系我们/统计/订单/通知/邀请等单栏页),<code>.is-wide</code> 修饰 <code>840px</code>(购买套餐等多列网格页)。padding 固定 <code>space-5 space-6 space-6</code>。对齐 Flutter <code>PageBody</code>(<code>client/lib/widgets/page_body.dart</code>)。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>默认 · 600px</h3><span class="tk">.view-body</span></div><div class="pcard-b col">
|
||
<div style="border:1px dashed var(--border-strong); border-radius:var(--radius-md); background:var(--bg-subtle)">
|
||
<div class="view-body" style="border:1px solid var(--accent-border); background:var(--surface); border-radius:var(--radius-md)">
|
||
<div class="t-overline" style="color:var(--fg3)">.view-body</div>
|
||
<div class="t-h3" style="margin:var(--space-2) 0 var(--space-1)">单栏内容视图</div>
|
||
<div class="t-sm" style="color:var(--fg2)">max-width:600px · margin:0 auto 居中 · padding 5/6/6</div>
|
||
</div>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-h"><h3>宽 · 840px</h3><span class="tk">.view-body.is-wide</span></div><div class="pcard-b col">
|
||
<div style="border:1px dashed var(--border-strong); border-radius:var(--radius-md); background:var(--bg-subtle)">
|
||
<div class="view-body is-wide" style="border:1px solid var(--accent-border); background:var(--surface); border-radius:var(--radius-md)">
|
||
<div class="t-overline" style="color:var(--fg3)">.view-body.is-wide</div>
|
||
<div class="t-h3" style="margin:var(--space-2) 0 var(--space-1)">多列网格页(购买套餐)</div>
|
||
<div class="t-sm" style="color:var(--fg2)">max-width:840px · padding 沿用默认 5/6/6</div>
|
||
</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><select></code>:触发药丸 <code>.langsel-trigger</code> + 自控绝对定位菜单 <code>.menu</code> / <code>.menu-item</code>(选中态 <code>.is-selected</code>)。点触发按钮可实际展开/收起。</p>
|
||
<div class="pcard" style="overflow:visible"><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" style="left:0; right:auto">
|
||
<button class="menu-item is-selected" aria-selected="true">简体中文</button>
|
||
<button class="menu-item">English</button>
|
||
<button class="menu-item">日本語</button>
|
||
<button class="menu-item">한국어</button>
|
||
<button class="menu-item">Русский</button>
|
||
<button class="menu-item">Español</button>
|
||
</div>
|
||
</div>
|
||
<span class="t-caption" style="color:var(--fg3)">↖ 默认展开演示,可点击触发按钮收起 / 展开</span>
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 核心连接按钮 ══════════ -->
|
||
<section class="section" id="a-connect">
|
||
<h2>核心连接按钮 · .connect-btn</h2>
|
||
<p class="desc">产品<strong>招牌组件</strong>:圆形一键连接键,三态 <code>.is-off</code> / <code>.is-connecting</code> / <code>.is-on</code>。背景在渐变↔纯色间切换仅过渡 <code>box-shadow</code>/<code>background-color</code>(<strong>禁</strong> <code>transition:all</code>,否则背景计算失效)。轨道环用内联 <code><svg></code>,圆内为图标 + 标签 / 计时。镜像 <code>client/lib/widgets/connect_button.dart</code>。</p>
|
||
<div class="pcard"><div class="pcard-b" style="justify-content:center; gap:var(--space-10); padding:var(--space-8) var(--space-5)">
|
||
|
||
<div style="display:flex; flex-direction:column; align-items:center; gap:var(--space-3)">
|
||
<button class="connect-btn is-off">
|
||
<svg class="cb-ring" viewBox="0 0 140 140"><circle class="cb-track" cx="70" cy="70" r="64"/></svg>
|
||
<svg class="cb-icon"><use href="#i-power"/></svg>
|
||
<span class="cb-label">点击连接</span>
|
||
<span class="cb-sub">TAP TO CONNECT</span>
|
||
</button>
|
||
<span class="t-caption" style="color:var(--fg3)">未连接 Off</span>
|
||
</div>
|
||
|
||
<div style="display:flex; flex-direction:column; align-items:center; gap:var(--space-3)">
|
||
<button class="connect-btn is-connecting">
|
||
<svg class="cb-ring cb-spin" viewBox="0 0 140 140">
|
||
<circle class="cb-arc-track" cx="70" cy="70" r="64"/>
|
||
<circle class="cb-arc" cx="70" cy="70" r="64"/>
|
||
</svg>
|
||
<svg class="cb-icon cb-spin"><use href="#i-loader-circle"/></svg>
|
||
<span class="cb-label">连接中</span>
|
||
<span class="cb-sub">CONNECTING…</span>
|
||
</button>
|
||
<span class="t-caption" style="color:var(--fg3)">建立隧道 Connecting</span>
|
||
</div>
|
||
|
||
<div style="display:flex; flex-direction:column; align-items:center; gap:var(--space-3)">
|
||
<button class="connect-btn is-on">
|
||
<svg class="cb-ring" viewBox="0 0 140 140"><circle class="cb-ring-full" cx="70" cy="70" r="64"/></svg>
|
||
<svg class="cb-icon"><use href="#i-shield-check"/></svg>
|
||
<span class="cb-timer">02:14:08</span>
|
||
<span class="cb-sub">已加密 · SECURE</span>
|
||
</button>
|
||
<span class="t-caption" style="color:var(--fg3)">已连接 Connected</span>
|
||
</div>
|
||
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 服务器列表行 ══════════ -->
|
||
<section class="section" id="a-server">
|
||
<h2>服务器列表行 · .server-row</h2>
|
||
<p class="desc">节点列表一行:<strong>国家码块</strong>(2 字母,绝不用 emoji 国旗)+ 名称/子名 + 属性胶囊(<code>.pill-outline</code>)+ 延迟(<code>--font-mono</code>·按值变色)+ 信号条 + 选中/推荐/不可用态。镜像 <code>client/lib/widgets/server_tile.dart</code>。放进 <code>.card.card-flush</code> 列表容器。</p>
|
||
|
||
<div class="pcard"><div class="pcard-h"><h3>列表行</h3><span class="tk">.server-row · .is-selected · .is-down</span></div><div class="pcard-b col">
|
||
<div class="card card-flush" style="max-width:460px; width:100%">
|
||
<div class="server-row is-selected">
|
||
<div class="sr-cc">HK</div>
|
||
<div class="sr-body">
|
||
<div class="sr-title">香港 · 加速线路</div>
|
||
<div class="sr-tags"><span class="pill pill-outline">流媒体</span></div>
|
||
</div>
|
||
<span class="sr-latency is-good">18ms</span>
|
||
<span class="sr-check"><svg class="ic"><use href="#i-check"/></svg></span>
|
||
</div>
|
||
<div class="server-row">
|
||
<div class="sr-cc">JP</div>
|
||
<div class="sr-body">
|
||
<div class="sr-title">日本 东京</div>
|
||
<div class="sr-sub">Tokyo</div>
|
||
</div>
|
||
<span class="sr-latency is-good">32ms</span>
|
||
<span class="sr-signal"><i></i><i></i><i></i></span>
|
||
</div>
|
||
<div class="server-row">
|
||
<div class="sr-cc">SG</div>
|
||
<div class="sr-body">
|
||
<div class="sr-title">新加坡</div>
|
||
<div class="sr-tags"><span class="pill pill-outline">P2P</span></div>
|
||
</div>
|
||
<span class="sr-latency is-warn">54ms</span>
|
||
<span class="sr-signal"><i></i><i></i><i class="is-off"></i></span>
|
||
</div>
|
||
<div class="server-row is-down">
|
||
<div class="sr-cc">US</div>
|
||
<div class="sr-body">
|
||
<div class="sr-title">美国 洛杉矶</div>
|
||
<div class="sr-sub">Los Angeles</div>
|
||
</div>
|
||
<span class="sr-unavail">不可用</span>
|
||
</div>
|
||
</div>
|
||
</div></div>
|
||
|
||
<div class="pcard"><div class="pcard-h"><h3>智能选择推荐行</h3><span class="tk">.server-row.is-smart · zap + 推荐胶囊</span></div><div class="pcard-b col">
|
||
<div class="card card-flush" style="max-width:460px; width:100%">
|
||
<div class="server-row is-smart">
|
||
<div class="sr-smart-ic"><svg class="ic"><use href="#i-zap"/></svg></div>
|
||
<div class="sr-body">
|
||
<div class="sr-title">智能选择</div>
|
||
<div class="sr-sub">根据当前网络环境,自动选择最优节点</div>
|
||
</div>
|
||
<span class="pill pill-outline">推荐</span>
|
||
<span class="sr-check"><svg class="ic"><use href="#i-check"/></svg></span>
|
||
</div>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 状态徽章 ══════════ -->
|
||
<section class="section" id="a-badge">
|
||
<h2>状态徽章 · .pill 场景</h2>
|
||
<p class="desc">徽章复用胶囊原子 <code>.pill</code>(见「胶囊 .pill / .badge」),此处按业务场景分组:<strong>连接态</strong>(色点 + 文字,非 emoji · 铁律 #7)· <strong>套餐</strong> · <strong>节点属性</strong>。镜像 <code>client/lib/widgets/status_pill.dart</code>。</p>
|
||
|
||
<div class="pcard"><div class="pcard-h"><h3>连接态</h3><span class="tk">.pill.is-success/-warning/-danger/-neutral</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>
|
||
</div></div>
|
||
|
||
<div class="pcard"><div class="pcard-h"><h3>套餐</h3><span class="tk">.pill-accent · .pill.is-neutral</span></div><div class="pcard-b">
|
||
<span class="pill pill-accent">PRO 会员</span>
|
||
<span class="pill is-neutral">免费版</span>
|
||
</div></div>
|
||
|
||
<div class="pcard"><div class="pcard-h"><h3>节点属性</h3><span class="tk">.pill-outline · .pill.is-neutral(数据)</span></div><div class="pcard-b">
|
||
<span class="pill pill-outline">流媒体优化</span>
|
||
<span class="pill pill-outline">P2P</span>
|
||
<span class="pill is-neutral">延迟 24ms</span>
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 组件 · 线上对齐 ══════════ -->
|
||
<section class="section" id="a-formctl">
|
||
<h2>复合输入 · 开关 · 图标框 · Toast</h2>
|
||
<p class="desc">对齐线上 <code>pangolin_field.dart</code> / <code>dtoggle</code> / <code>pangolin_toast.dart</code>。<code>.field-box</code> 复合输入(前置图标 + 裸 input + 后置槽),<code>.toggle</code> 开关,<code>.segswitch</code> 二选一段控开关(支付渠道/币种),<code>.icon-box</code> 图标框,<code>.status-dot</code> 状态点,<code>.toast</code> 轻提示。</p>
|
||
<div class="pcard"><div class="pcard-b" style="align-items:flex-start; gap:var(--space-4)">
|
||
<label class="field-box" style="width:240px"><svg class="fb-lead"><use href="#i-search"/></svg><input class="fb-input" placeholder="搜索国家或城市"></label>
|
||
<label class="field-box" style="width:240px"><svg class="fb-lead"><use href="#i-lock"/></svg><input class="fb-input" type="password" value="123456"><button class="fb-trailing" style="border:none;background:none;cursor:pointer"><svg style="width:16px;height:16px"><use href="#i-eye"/></svg></button></label>
|
||
<label class="field-box is-plain" style="width:240px"><svg class="fb-lead"><use href="#i-search"/></svg><input class="fb-input" placeholder="无边框搜索变体"></label>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-b">
|
||
<span class="toggle is-on"><i></i></span>
|
||
<span class="toggle"><i></i></span>
|
||
<span class="icon-box"><svg><use href="#i-smartphone"/></svg></span>
|
||
<span class="icon-box is-danger"><svg><use href="#i-trash-2"/></svg></span>
|
||
<span style="display:inline-flex;align-items:center;gap:8px"><span class="status-dot is-online"></span>在线</span>
|
||
<span style="display:inline-flex;align-items:center;gap:8px"><span class="status-dot is-offline"></span>离线</span>
|
||
<span class="toast"><svg><use href="#i-check-circle"/></svg>已复制</span>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-b" style="flex-direction:column; align-items:stretch; gap:var(--space-3); max-width:320px">
|
||
<div class="segswitch">
|
||
<button class="segswitch-opt is-active"><svg><use href="#i-globe"/></svg>USDT</button>
|
||
<button class="segswitch-opt"><svg><use href="#i-credit-card"/></svg>人民币</button>
|
||
</div>
|
||
<div class="segswitch">
|
||
<button class="segswitch-opt">USDT · 加密货币</button>
|
||
<button class="segswitch-opt is-active">人民币 · 支付宝</button>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-quota">
|
||
<h2>额度卡 · 指标卡</h2>
|
||
<p class="desc">对齐线上 <code>quota_card.dart</code>(免费版今日额度:倒计时收缩条 + 「免费版」短标签 + 三档阈值;<strong>无绿色「已解锁」态</strong>,线上以倒计时收缩条表达)与统计 <code>.metric</code> 指标瓦片。</p>
|
||
<div class="pcard"><div class="pcard-b" style="align-items:flex-start; gap:var(--space-4)">
|
||
<div class="quota-card" style="width:280px">
|
||
<div class="quota-head"><svg><use href="#i-clock"/></svg><span class="quota-label">今日剩余</span><span class="quota-val">10 分钟</span><span class="quota-tag">免费版</span></div>
|
||
<div class="quota-bar"><i style="width:100%"></i></div>
|
||
</div>
|
||
<div class="quota-card" style="width:280px">
|
||
<div class="quota-head"><svg><use href="#i-clock"/></svg><span class="quota-label">剩余</span><span class="quota-val">02:18</span><span class="quota-tag">免费版</span></div>
|
||
<div class="quota-bar is-low"><i style="width:23%"></i></div>
|
||
</div>
|
||
<div class="quota-card is-exhausted" style="width:280px">
|
||
<div class="quota-head"><svg><use href="#i-clock"/></svg><span class="quota-label">今日已用完</span><span class="quota-tag">免费版</span></div>
|
||
<div class="quota-bar is-exhausted"><i style="width:100%"></i></div>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-b" style="gap:var(--space-3)">
|
||
<div class="metric" style="width:150px"><span class="m-ic"><svg><use href="#i-arrow-down"/></svg></span><div class="m-val">12.4G</div><div class="m-label">本周流量</div></div>
|
||
<div class="metric" style="width:150px"><span class="m-ic"><svg><use href="#i-clock"/></svg></span><div class="m-val">6.2h</div><div class="m-label">在线时长</div></div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-rows">
|
||
<h2>设备行 · 设置行</h2>
|
||
<p class="desc">对齐线上 <code>DevicesScreen</code> / <code>settings_page.dart</code>。<code>.device-row</code>(图标框 + 名 + 最后在线 + 本机/移除/⋯)与 <code>.setting-row</code>(图标框 + 标题 + 副标 + trailing:chevron / 开关 / 值)。</p>
|
||
<div class="pcard"><div class="pcard-h"><h3>设备管理</h3><span class="tk">.device-row</span></div><div class="pcard-b col">
|
||
<div class="card card-flush" style="max-width:460px">
|
||
<div class="device-row"><span class="icon-box"><svg><use href="#i-smartphone"/></svg></span><div class="dv-body"><div class="dv-name">iPhone 15</div><div class="dv-sub"><span class="status-dot is-online"></span>当前设备 · 刚刚在线</div></div><span class="dv-trail"><span class="pill is-success"><span class="dot"></span>本机</span></span></div>
|
||
<div class="device-row"><span class="icon-box"><svg><use href="#i-laptop"/></svg></span><div class="dv-body"><div class="dv-name">MacBook Pro</div><div class="dv-sub"><span class="status-dot is-offline"></span>最后在线 2 小时前</div></div><span class="dv-trail"><button class="btn btn-icon btn-subtle" aria-label="更多"><svg class="ic" style="width:16px;height:16px"><use href="#i-more-vertical"/></svg></button></span></div>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-h"><h3>设置</h3><span class="tk">.setting-row</span></div><div class="pcard-b col">
|
||
<div class="card card-flush" style="max-width:460px">
|
||
<div class="setting-row"><span class="icon-box"><svg><use href="#i-zap"/></svg></span><div class="st-body"><div class="st-title">智能分流</div><div class="st-sub">海外应用走加速,国内直连</div></div><span class="st-trail"><span class="toggle is-on"><i></i></span></span></div>
|
||
<div class="setting-row"><span class="icon-box"><svg><use href="#i-globe"/></svg></span><div class="st-body"><div class="st-title">语言</div></div><span class="st-trail"><span class="st-val">简体中文</span></span></div>
|
||
<div class="setting-row is-tap"><span class="icon-box"><svg><use href="#i-shield"/></svg></span><div class="st-body"><div class="st-title">协议</div></div><span class="st-trail"><span class="st-val">REALITY</span><svg><use href="#i-chevron-right"/></svg></span></div>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-planbn">
|
||
<h2>套餐横幅 · 侧栏会员卡</h2>
|
||
<p class="desc">对齐线上 <code>_PlanBanner</code>(账户页)与 <code>plan_badge_card.dart</code>(桌面侧栏底部)。免费=surface 卡;<code>.is-pro</code>=clay 渐变。</p>
|
||
<div class="pcard"><div class="pcard-b" style="align-items:stretch; gap:var(--space-4)">
|
||
<div class="plan-banner" style="width:300px">
|
||
<div class="pbn-row"><span class="pbn-badge"><svg><use href="#i-gift"/></svg></span><div style="flex:1"><div class="pbn-name">免费版</div><div class="pbn-meta">注册享 7 天免费试用</div></div><button class="btn btn-primary"><svg><use href="#i-crown"/></svg>升级</button></div>
|
||
</div>
|
||
<div class="plan-banner is-pro" style="width:300px">
|
||
<div class="pbn-row"><span class="pbn-badge"><svg><use href="#i-crown"/></svg></span><div style="flex:1"><div class="pbn-name">PRO 会员</div><div class="pbn-meta">有效期至 2027-07-11</div></div><button class="btn btn-primary">续费</button></div>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-h"><h3>侧栏会员卡</h3><span class="tk">.plan-badge</span></div><div class="pcard-b">
|
||
<div class="plan-badge is-active" style="width:200px"><span class="plb-crown"><svg><use href="#i-crown"/></svg></span><div class="plb-body"><span class="plb-name">PRO 会员</span><span class="plb-sub">至 2027-07-11</span></div><svg class="plb-chev"><use href="#i-chevron-right"/></svg></div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-appchrome">
|
||
<h2>更新条 · 底部 Tab · 侧栏项</h2>
|
||
<p class="desc">app-chrome 组件。<code>.update-banner</code>(jiu 式更新顶部横条,对齐 <code>UpdateBanner</code>)、<code>.tabbar</code>/<code>.tab</code>(移动底部 4-Tab,对齐 <code>bottom_tab_bar.dart</code>)、<code>.nav-item</code>(桌面/iPad 侧栏项,对齐 <code>nav_sidebar.dart</code>)。</p>
|
||
<div class="pcard"><div class="pcard-b col" style="padding:0">
|
||
<div class="update-banner"><svg class="ub-ic"><use href="#i-zap"/></svg><span class="ub-title">发现新版本 v1.1.0,建议更新</span><button class="ub-cta">立即更新</button><button class="ub-close"><svg><use href="#i-x"/></svg></button></div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-b col" style="padding:0">
|
||
<div class="tabbar">
|
||
<button class="tab is-active"><svg><use href="#i-power"/></svg>连接</button>
|
||
<button class="tab"><svg><use href="#i-globe"/></svg>节点</button>
|
||
<button class="tab"><svg><use href="#i-chart-no-axes-column"/></svg>统计</button>
|
||
<button class="tab"><svg><use href="#i-user"/></svg>我的</button>
|
||
</div>
|
||
</div></div>
|
||
<div class="pcard"><div class="pcard-b col">
|
||
<div style="max-width:220px; display:flex; flex-direction:column; gap:var(--space-1)">
|
||
<div class="nav-item is-active"><svg><use href="#i-power"/></svg>连接</div>
|
||
<div class="nav-item"><svg><use href="#i-globe"/></svg>节点</div>
|
||
<div class="nav-item"><svg><use href="#i-chart-no-axes-column"/></svg>统计</div>
|
||
<div class="nav-item"><svg><use href="#i-settings"/></svg>设置</div>
|
||
<div class="nav-item"><svg><use href="#i-message-circle"/></svg>联系</div>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<!-- ══════════ 购买 Purchase ══════════ -->
|
||
<section class="section" id="a-plan">
|
||
<h2>套餐卡 · .plan-card</h2>
|
||
<p class="desc">购买套餐档位卡。常规档 surface 底;<code>.is-featured</code> 走 clay 渐变高亮(年付/最优惠),内含反色 CTA,顶部可挂 <code>.plan-pop</code>「最受欢迎」浮标。镜像 <code>client/lib/widgets/plan_card.dart</code>。金额仅展示,扣款以 pay 服务为准。</p>
|
||
<div class="pcard"><div class="pcard-b" style="align-items:stretch; gap:var(--space-4)">
|
||
<div class="plan-card" style="width:230px">
|
||
<div class="plan-name">按月</div>
|
||
<div class="plan-price-row"><span class="plan-price">¥29.99</span><span class="plan-per">/ 月</span></div>
|
||
<div class="plan-feats">
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>不限时长 · 不限流量</div>
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>80+ 全球加速线路</div>
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>5 台设备同时在线</div>
|
||
</div>
|
||
<button class="btn btn-primary btn-block">立即购买</button>
|
||
</div>
|
||
<div class="plan-card is-featured" style="width:230px">
|
||
<span class="plan-pop">最受欢迎</span>
|
||
<div class="plan-name">按年</div>
|
||
<div class="plan-price-row"><span class="plan-price">¥199.99</span><span class="plan-per">/ 年 · ¥16.7/月</span></div>
|
||
<div class="plan-feats">
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>不限时长 · 不限流量</div>
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>80+ 全球加速线路</div>
|
||
<div class="plan-feat"><svg><use href="#i-check"/></svg>5 台设备同时在线</div>
|
||
</div>
|
||
<button class="btn btn-primary btn-block">立即购买</button>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-paym">
|
||
<h2>支付方式 · .pay-method</h2>
|
||
<p class="desc">底部弹层选择支付渠道,一行 = leading 图标 + 标题 + trailing chevron。资金外流(拉起支付宝 / 展示加密地址),<strong>App 内无卡号录入表单</strong>(设计铁律 #10)。生产渠道:哪吒聚合(支付宝)+ 加密货币 USDT。</p>
|
||
<div class="pcard"><div class="pcard-b col">
|
||
<div class="card card-flush" style="max-width:360px; padding:var(--space-2)">
|
||
<button class="pay-method"><svg class="pm-ic"><use href="#i-credit-card"/></svg><span class="pm-title">支付宝</span><svg class="pm-chev"><use href="#i-chevron-right"/></svg></button>
|
||
<button class="pay-method"><svg class="pm-ic"><use href="#i-globe"/></svg><span class="pm-title">加密货币 · USDT (TRC20)</span><svg class="pm-chev"><use href="#i-chevron-right"/></svg></button>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-kv">
|
||
<h2>明细行 · .kv-row</h2>
|
||
<p class="desc">可复制键值行,用于加密支付态展示「网络 / 收款地址 / 精确金额」。值用等宽字(<code>--font-mono</code> + tabular nums),右侧复制按钮。</p>
|
||
<div class="pcard"><div class="pcard-b col">
|
||
<div class="card" style="max-width:420px">
|
||
<div class="kv-row"><span class="kv-label">网络</span><span class="kv-val">USDT · TRC20</span><button class="kv-copy"><svg><use href="#i-copy"/></svg></button></div>
|
||
<div class="kv-row"><span class="kv-label">收款地址</span><span class="kv-val">TB1e7q1333iuLMLc2iQaoaqeuDF2U7cxan</span><button class="kv-copy"><svg><use href="#i-copy"/></svg></button></div>
|
||
<div class="kv-row"><span class="kv-label">精确金额</span><span class="kv-val">4.990001</span><button class="kv-copy"><svg><use href="#i-copy"/></svg></button></div>
|
||
</div>
|
||
</div></div>
|
||
</section>
|
||
|
||
<section class="section" id="a-result">
|
||
<h2>结果态 · .result</h2>
|
||
<p class="desc">支付终态:居中大图标 + 标题 + 说明。成功用 <code>--success</code>,失败加 <code>.is-fail</code> 走 <code>--danger</code>。</p>
|
||
<div class="pcard"><div class="pcard-b" style="gap:var(--space-6)">
|
||
<div class="result" style="width:280px">
|
||
<div class="result-icon"><svg><use href="#i-check-circle"/></svg></div>
|
||
<div class="result-title">支付成功</div>
|
||
<div class="result-sub">有效期至 2027-07-11</div>
|
||
</div>
|
||
<div class="result is-fail" style="width:280px">
|
||
<div class="result-icon"><svg><use href="#i-alert-triangle"/></svg></div>
|
||
<div class="result-title">支付失败</div>
|
||
<div class="result-sub">订单已超时,请重试</div>
|
||
</div>
|
||
</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><svg class="ic"><use href="#i-<name>"/></svg></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)=>{ if(!a.dataset.s) return; /* 外链(如屏预览)正常跳转 */ 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));
|
||
|
||
/* ── 侧栏:收起 + 拖拽改宽(持久化 localStorage) ── */
|
||
(function(){
|
||
const doc = document.querySelector('.doc');
|
||
const nav = document.querySelector('.doc-nav');
|
||
const handle = document.getElementById('navResize');
|
||
const toggle = document.getElementById('navToggle');
|
||
if(!doc || !nav || !handle || !toggle) return;
|
||
const MIN = 180, MAX = 460, DEF = 238;
|
||
let curW = parseInt(localStorage.getItem('pangolin-ds-navw') || '', 10);
|
||
if (!(curW >= MIN && curW <= MAX)) curW = DEF;
|
||
let collapsed = localStorage.getItem('pangolin-ds-navcollapsed') === '1';
|
||
function apply(){
|
||
if (collapsed) { doc.classList.add('nav-collapsed'); nav.style.width = '0px'; }
|
||
else { doc.classList.remove('nav-collapsed'); nav.style.width = curW + 'px'; }
|
||
}
|
||
apply();
|
||
// 收起 / 展开
|
||
toggle.onclick = () => {
|
||
collapsed = !collapsed;
|
||
localStorage.setItem('pangolin-ds-navcollapsed', collapsed ? '1' : '0');
|
||
apply();
|
||
};
|
||
// 拖拽改宽(纯 JS 内联宽度,避免自定义 CSS 变量触发闸误报)
|
||
let dragging = false;
|
||
handle.addEventListener('mousedown', (e) => {
|
||
if (collapsed) return;
|
||
dragging = true; handle.classList.add('dragging'); document.body.classList.add('nav-dragging');
|
||
e.preventDefault();
|
||
});
|
||
window.addEventListener('mousemove', (e) => {
|
||
if (!dragging) return;
|
||
const rect = nav.getBoundingClientRect();
|
||
curW = Math.max(MIN, Math.min(MAX, e.clientX - rect.left));
|
||
nav.style.width = curW + 'px';
|
||
});
|
||
window.addEventListener('mouseup', () => {
|
||
if (!dragging) return;
|
||
dragging = false; handle.classList.remove('dragging'); document.body.classList.remove('nav-dragging');
|
||
localStorage.setItem('pangolin-ds-navw', curW);
|
||
});
|
||
})();
|
||
|
||
/* ── init(sprite 注入可能在 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>
|