Compare commits
2 Commits
805bf8e2ca
...
6def3edaed
| Author | SHA1 | Date | |
|---|---|---|---|
| 6def3edaed | |||
| 56f673791e |
@@ -0,0 +1,272 @@
|
||||
/* =============================================================
|
||||
穿山甲 VPN · Pangolin VPN — Prototype Atoms
|
||||
design/prototype/atoms.css
|
||||
-------------------------------------------------------------
|
||||
本文件是「原型公用组件原子层」的唯一真相源(single source)。
|
||||
canonical CSS 原子类,沉淀自 design/preview/*.html 组件规格、
|
||||
web/usercenter/components/shared.tsx、web/website/src/styles/website.css
|
||||
与 client/lib/widgets/*.dart 的交互态语义。
|
||||
|
||||
铁律(写/改本文件必守,对齐 design/CLAUDE.md §1):
|
||||
1. 颜色 / 圆角 / 间距 / 字号 / 阴影 / 字体 一律走 var(--token)
|
||||
(token 定义在 tokens.css)。禁任何硬编码 hex / rgb / 魔法数。
|
||||
2. 明暗双主题「自动」适配:原子类只引语义 token(--surface / --fg1 …),
|
||||
主题切换由 tokens.css 的 [data-theme="dark"] 负责——
|
||||
本文件里不写任何 [data-theme] 分支。
|
||||
3. 暖大地色 · 大圆角(按钮全胶囊 radius-full / 卡片 lg–xl / 输入 md)·
|
||||
柔和暖阴影 · 状态用「色点 + 文字胶囊」非 emoji。
|
||||
4. 交互态语义:hover 主色加深一档 · press 缩放 .97 · focus clay 光环
|
||||
(shadow-focus)· disabled sand-200 底 + sand-400 字。
|
||||
5. 每新增一个原子,须在 design/prototype/index.html 登记(登记簿单源)。
|
||||
|
||||
使用前先在页面 <head> 引入:tokens.css(令牌)+ 本文件(原子)。
|
||||
============================================================= */
|
||||
|
||||
|
||||
/* =============================================================
|
||||
BUTTONS · .btn + variants
|
||||
全胶囊圆角 · hover 加深 · press 收缩 .97 · focus clay 光环
|
||||
变体:.btn-primary(实心 clay) · .btn-ghost(描边/幽灵) ·
|
||||
.btn-subtle(次要描边) · .btn-danger · 尺寸 .btn-lg · .btn-icon
|
||||
============================================================= */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-sans);
|
||||
font-weight: 600;
|
||||
font-size: var(--text-sm);
|
||||
line-height: 1;
|
||||
border: none;
|
||||
border-radius: var(--radius-full);
|
||||
padding: var(--space-3) var(--space-5);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background-color var(--dur-fast) var(--ease-out),
|
||||
color var(--dur-fast) var(--ease-out),
|
||||
border-color var(--dur-fast) var(--ease-out),
|
||||
transform var(--dur-fast) var(--ease-out),
|
||||
box-shadow var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.btn > svg { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
.btn:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-focus);
|
||||
}
|
||||
.btn:active { transform: scale(0.97); }
|
||||
.btn:disabled,
|
||||
.btn.is-disabled {
|
||||
background: var(--sand-200);
|
||||
color: var(--sand-400);
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* Primary — 实心黏土铜 */
|
||||
.btn-primary { background: var(--accent); color: var(--fg-on-accent); }
|
||||
.btn-primary:hover { background: var(--accent-hover); }
|
||||
.btn-primary:active { background: var(--accent-press); }
|
||||
|
||||
/* Ghost — 透明底、clay 字,hover 上 accent-subtle */
|
||||
.btn-ghost { background: transparent; color: var(--accent); }
|
||||
.btn-ghost:hover { background: var(--accent-subtle); }
|
||||
|
||||
/* Subtle — surface 底 + 强描边(次要动作) */
|
||||
.btn-subtle {
|
||||
background: var(--surface);
|
||||
color: var(--fg1);
|
||||
border: 1.5px solid var(--border-strong);
|
||||
}
|
||||
.btn-subtle:hover { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
/* Danger — 危险动作(断开 / 移除设备) */
|
||||
.btn-danger { background: var(--danger-subtle); color: var(--red-600); }
|
||||
.btn-danger:hover { background: var(--danger); color: var(--fg-on-accent); }
|
||||
|
||||
/* 尺寸 / 形态修饰 */
|
||||
.btn-lg { padding: var(--space-4) var(--space-6); font-size: var(--text-body); }
|
||||
.btn-icon { padding: var(--space-3); } /* 正方形图标按钮(配 radius-full 成圆) */
|
||||
.btn-block { width: 100%; }
|
||||
|
||||
|
||||
/* =============================================================
|
||||
CARD · .card
|
||||
surface 底 + border + radius-xl + shadow-sm
|
||||
============================================================= */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-xl);
|
||||
box-shadow: var(--shadow-sm);
|
||||
padding: var(--space-5);
|
||||
}
|
||||
.card-flush { padding: 0; } /* 列表容器(server-row 组)用,内容自带 padding */
|
||||
|
||||
|
||||
/* =============================================================
|
||||
INPUT · .input (+ .field / .flabel)
|
||||
强描边 + radius-md + focus clay 光环
|
||||
============================================================= */
|
||||
.field { display: block; }
|
||||
.flabel {
|
||||
display: block;
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-caption);
|
||||
font-weight: 600;
|
||||
color: var(--fg2);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
.input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-sm);
|
||||
color: var(--fg1);
|
||||
background: var(--surface);
|
||||
border: 1.5px solid var(--border-strong);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
transition: border-color var(--dur-fast) var(--ease-out),
|
||||
box-shadow var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.input::placeholder { color: var(--fg3); }
|
||||
.input:focus,
|
||||
.input.is-focused {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: var(--shadow-focus);
|
||||
}
|
||||
.input:disabled {
|
||||
background: var(--bg-subtle);
|
||||
color: var(--fg3);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.input.is-error { border-color: var(--danger); }
|
||||
.input.is-error:focus { box-shadow: 0 0 0 4px var(--danger-subtle); }
|
||||
|
||||
|
||||
/* =============================================================
|
||||
BADGE / PILL · .pill (+ status modifiers)
|
||||
状态胶囊:色点 + 文字(非 emoji)。全胶囊圆角。
|
||||
状态:.is-success / .is-warning / .is-danger / .is-neutral
|
||||
变体:.pill-accent(实心 clay,如 PRO) · .pill-outline(accent 描边,如节点属性)
|
||||
============================================================= */
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-caption);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--sand-100);
|
||||
color: var(--fg2);
|
||||
}
|
||||
.pill .dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--sand-400);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* alias:.badge === .pill(语义等价,命名习惯不同) */
|
||||
.badge { }
|
||||
|
||||
.pill.is-success { background: var(--success-subtle); color: var(--green-600); }
|
||||
.pill.is-success .dot { background: var(--success); }
|
||||
.pill.is-warning { background: var(--warning-subtle); color: var(--amber-600); }
|
||||
.pill.is-warning .dot { background: var(--warning); }
|
||||
.pill.is-danger { background: var(--danger-subtle); color: var(--red-600); }
|
||||
.pill.is-danger .dot { background: var(--danger); }
|
||||
.pill.is-neutral { background: var(--sand-100); color: var(--fg2); }
|
||||
.pill.is-neutral .dot { background: var(--sand-400); }
|
||||
|
||||
/* 实心 accent(PRO 会员等强调标签) */
|
||||
.pill-accent { background: var(--accent); color: var(--fg-on-accent); }
|
||||
/* accent 描边(节点属性:流媒体 / P2P 等) */
|
||||
.pill-outline {
|
||||
background: var(--accent-subtle);
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent-border);
|
||||
}
|
||||
|
||||
|
||||
/* =============================================================
|
||||
LANGUAGE SELECT · .langsel (触发药丸) + .menu (绝对定位菜单)
|
||||
对齐 usercenter shared.tsx::LangSeg / website 的自定义下拉:
|
||||
不依赖原生 <select>(会漂移),按钮 + 自控绝对定位菜单。
|
||||
============================================================= */
|
||||
.langsel {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
/* 触发按钮:药丸形,bg-subtle 底 + 细描边 */
|
||||
.langsel-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-caption);
|
||||
font-weight: 700;
|
||||
color: var(--fg2);
|
||||
background: var(--bg-subtle);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
cursor: pointer;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: border-color var(--dur-fast) var(--ease-out),
|
||||
color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.langsel-trigger:hover { border-color: var(--border-strong); color: var(--fg1); }
|
||||
.langsel-trigger:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
|
||||
.langsel-trigger > svg { width: 11px; height: 11px; }
|
||||
/* 展开态:caret 翻转(配合 .is-open 或 [aria-expanded="true"]) */
|
||||
.langsel-trigger[aria-expanded="true"] > svg,
|
||||
.langsel.is-open .langsel-trigger > svg { transform: rotate(180deg); }
|
||||
|
||||
/* 绝对定位菜单(下拉列表容器) */
|
||||
.menu {
|
||||
position: absolute;
|
||||
top: calc(100% + var(--space-2));
|
||||
right: 0;
|
||||
min-width: 132px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
padding: var(--space-1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
z-index: 60;
|
||||
}
|
||||
.menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-sans);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 500;
|
||||
color: var(--fg1);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border-radius: var(--radius-sm);
|
||||
white-space: nowrap;
|
||||
transition: background-color var(--dur-fast) var(--ease-out);
|
||||
}
|
||||
.menu-item:hover { background: var(--bg-subtle); }
|
||||
/* 选中项:accent-subtle 底 + clay 字 */
|
||||
.menu-item.is-selected,
|
||||
.menu-item[aria-selected="true"] {
|
||||
background: var(--accent-subtle);
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
// icons.js — SVG sprite 单源(Lucide 细线条,viewBox 0 0 24 24 / stroke-width 2 / 圆角端点,ISC 许可)。
|
||||
// 用法:<svg class="ic"><use href="#i-power"/></svg>;.ic{ width/height/color } 即可控制尺寸与颜色。
|
||||
// 收敛三处分散的 Lucide 图标为一份权威 sprite:
|
||||
// · usercenter —— web/usercenter/components/icons.tsx 的内联路径(权威副本,逐字照抄)。
|
||||
// · lucide-react —— web/website/node_modules/lucide-react(usercenter 没有的从包源提取)。
|
||||
// 新增图标先在此登记 symbol,再用 <use> 引用;勿手写/瞎猜路径。
|
||||
(function () {
|
||||
// key = kebab-name,value = 该图标的 lucide 子元素串(照搬权威来源)。
|
||||
var ICONS = {
|
||||
// ── 源自 usercenter(web/usercenter/components/icons.tsx 内联,逐字照抄)──
|
||||
'layout-dashboard': '<rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/>',
|
||||
'link': '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
|
||||
'ticket': '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
|
||||
'users': '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
||||
'copy': '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
|
||||
'refresh-cw': '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
|
||||
'qr-code': '<rect width="5" height="5" x="3" y="3" rx="1"/><rect width="5" height="5" x="16" y="3" rx="1"/><rect width="5" height="5" x="3" y="16" rx="1"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>',
|
||||
'download': '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>',
|
||||
'check': '<path d="M20 6 9 17l-5-5"/>',
|
||||
'check-circle': '<path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/>',
|
||||
'zap': '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||||
'clock': '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
||||
'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||||
'shield': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>',
|
||||
'crown': '<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
|
||||
'credit-card': '<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>',
|
||||
'send': '<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/>',
|
||||
'message-circle': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
|
||||
'mail': '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||||
'shopping-bag': '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
||||
'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
|
||||
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
|
||||
'gift': '<rect x="3" y="8" width="18" height="4" rx="1"/><path d="M12 8v13"/><path d="M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7"/><path d="M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5"/>',
|
||||
'smartphone': '<rect width="14" height="20" x="5" y="2" rx="2" ry="2"/><path d="M12 18h.01"/>',
|
||||
'lock': '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
||||
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
||||
'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
|
||||
'settings': '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
|
||||
'trash-2': '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>',
|
||||
'monitor': '<rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/>',
|
||||
'key': '<path d="m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4"/><path d="m21 2-9.6 9.6"/><circle cx="7.5" cy="15.5" r="5.5"/>',
|
||||
'sun': '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
|
||||
'moon': '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
|
||||
'alert-triangle': '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
|
||||
'x': '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
||||
|
||||
// ── 源自 lucide-react 包(web/website/node_modules/lucide-react v0.469.0,usercenter 未含)──
|
||||
'power': '<path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.77.04"/>',
|
||||
'loader-circle': '<path d="M21 12a9 9 0 1 1-6.219-8.56"/>',
|
||||
'globe': '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
|
||||
'user': '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
|
||||
'search': '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
||||
'chevron-down': '<path d="m6 9 6 6 6-6"/>',
|
||||
'arrow-left': '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
|
||||
'arrow-up': '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
|
||||
'arrow-right': '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
|
||||
'compass': '<path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/>',
|
||||
'chart-no-axes-column': '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
|
||||
'map-pin': '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/>',
|
||||
'laptop': '<path d="M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16"/>',
|
||||
'monitor-smartphone': '<path d="M18 8V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h8"/><path d="M10 19v-3.96 3.15"/><path d="M7 19h5"/><rect width="6" height="10" x="16" y="12" rx="2"/>',
|
||||
'play-circle': '<circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/>',
|
||||
'wifi': '<path d="M12 20h.01"/><path d="M2 8.82a15 15 0 0 1 20 0"/><path d="M5 12.859a10 10 0 0 1 14 0"/><path d="M8.5 16.429a5 5 0 0 1 7 0"/>',
|
||||
'eye': '<path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/>',
|
||||
'eye-off': '<path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/>',
|
||||
'more-vertical': '<circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/>',
|
||||
'pencil': '<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/>',
|
||||
'terminal': '<polyline points="4 17 10 11 4 5"/><line x1="12" x2="20" y1="19" y2="19"/>',
|
||||
'menu': '<line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/>'
|
||||
};
|
||||
|
||||
// 组装隐藏 sprite:每个图标一个 <symbol>,符号上带 lucide 标准描边属性,故 .ic 只需管 width/height/color。
|
||||
function buildSprite() {
|
||||
var parts = ['<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;width:0;height:0;overflow:hidden" aria-hidden="true">'];
|
||||
for (var name in ICONS) {
|
||||
if (!Object.prototype.hasOwnProperty.call(ICONS, name)) continue;
|
||||
parts.push(
|
||||
'<symbol id="i-' + name + '" viewBox="0 0 24 24" fill="none" stroke="currentColor" ' +
|
||||
'stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + ICONS[name] + '</symbol>'
|
||||
);
|
||||
}
|
||||
parts.push('</svg>');
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function inject() {
|
||||
if (document.getElementById('pangolin-icon-sprite')) return;
|
||||
var d = document.createElement('div');
|
||||
d.id = 'pangolin-icon-sprite';
|
||||
d.style.cssText = 'position:absolute;width:0;height:0';
|
||||
d.innerHTML = buildSprite();
|
||||
document.body.insertBefore(d, document.body.firstChild);
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') inject();
|
||||
else document.addEventListener('DOMContentLoaded', inject);
|
||||
|
||||
// 便于调试/校验:暴露原始映射。
|
||||
if (typeof window !== 'undefined') window.PANGOLIN_ICONS = ICONS;
|
||||
})();
|
||||
@@ -41,9 +41,9 @@
|
||||
- [x] 1.2 `design/prototype/tokens.css`:从现有 `colors_and_type.css` 迁移/规整为
|
||||
「基础 `:root`(主题无关标量:间距/圆角/字号/字体/阴影/动效)+ `[data-theme=dark]` 颜色覆盖块」结构。
|
||||
**保持数值不变**,只重排为 ds-flow 结构;`colors_and_type.css` 作为兼容别名或迁移为薄封装(不破坏现有 codegen)
|
||||
- [ ] 1.3 `design/prototype/atoms.css`:把按钮/卡片/输入/**语言下拉**/徽章/状态药丸等公用原子类沉淀为
|
||||
- [x] 1.3 `design/prototype/atoms.css`:把按钮/卡片/输入/**语言下拉**/徽章/状态药丸等公用原子类沉淀为
|
||||
只引 `var(--token)` 的 CSS(镜像 `design/preview/` 现有规格 + client widgets 实现语义)
|
||||
- [ ] 1.4 `design/prototype/icons.js`:SVG sprite 单源(`<symbol id="i-*">`),
|
||||
- [x] 1.4 `design/prototype/icons.js`:SVG sprite 单源(`<symbol id="i-*">`),
|
||||
收敛现有分散图标(website Icon.astro / usercenter icons.tsx / Flutter pangolin_icons.dart 三处的图标集)
|
||||
- [ ] 1.5 `design/prototype/index.html`:活登记页——三…两主题(light/dark)切换 + `data-swatches` 声明式色板 +
|
||||
字号梯度 + 圆角/间距/阴影 + 全部公用组件原子展示卡 + 图标库全展示。**每个 atom 必须在此登记**
|
||||
|
||||
Reference in New Issue
Block a user