/* =============================================================
穿山甲 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 登记(登记簿单源)。
使用前先在页面
引入: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 — 危险动作(断开 / 移除设备)。文字用语义 --danger(对齐线上 c.danger,随主题翻色) */
.btn-danger { background: var(--danger-subtle); color: var(--danger); }
.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-lg + shadow-sm
圆角对齐线上 cardTheme(PangolinRadius.lg=14);阴影保留 shadow-sm(暖阴影铁律)。
============================================================= */
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
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(--bg-subtle);
color: var(--fg2);
}
.pill .dot {
width: 7px;
height: 7px;
border-radius: var(--radius-full);
background: var(--fg3);
flex-shrink: 0;
}
/* alias:.badge === .pill(语义等价,命名习惯不同) */
.badge { }
/* 状态色对齐线上 status_pill.dart:文字用语义档 --success/--warning/--danger(非 -600 深档),
neutral 用 --bg-subtle/--fg3(原 sand-100/sand-400 硬编码在深色不翻色,已修)。 */
.pill.is-success { background: var(--success-subtle); color: var(--success); }
.pill.is-success .dot { background: var(--success); }
.pill.is-warning { background: var(--warning-subtle); color: var(--warning); }
.pill.is-warning .dot { background: var(--warning); }
.pill.is-danger { background: var(--danger-subtle); color: var(--danger); }
.pill.is-danger .dot { background: var(--danger); }
.pill.is-neutral { background: var(--bg-subtle); color: var(--fg2); }
.pill.is-neutral .dot { background: var(--fg3); }
/* 实心 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 的自定义下拉:
不依赖原生