侧栏从 sticky 改 fixed:sticky 的位置跟着 scrollTop 走,滚到底时 scrollTop 是 scrollHeight 减 clientHeight,在 Windows 显示缩放下是 小数,部分图标会跳 1px;fixed 按视口摆放,不参与这个运算。网格第一列 只当占位,主区显式站到第二列。 错误提示是拿去粘贴排查的,点击改成复制而不是关闭,关闭交给自动消失; 局域网 http 下没有 clipboard API,退回 execCommand。撤回/重做各加 2.5 秒冷却,窗内连点算一次,顺带避开重做备份名的同秒覆盖。弹窗打开 时锁住背景滚动,滚动条位置由 scrollbar-gutter 常驻,不横向跳。
288 lines
6.4 KiB
CSS
288 lines
6.4 KiB
CSS
/*
|
||
* 设计基调。
|
||
*
|
||
* 三条规则,其余都是它们的推论:
|
||
* 1. 层次靠底色深浅和一像素描边,不靠投影
|
||
* 2. 不要卡片套卡片。一屏最多两层容器,再深就换成分割线
|
||
* 3. 信息密度偏低。行高、间距给足,宁可多滚动也不要挤
|
||
*
|
||
* 颜色只留一个强调色,用在「当前选中」和「主要操作」上。状态色(成功/警告/危险)
|
||
* 只在真的表示状态时用,不拿来装饰。
|
||
*
|
||
* 唯一的例外是链式代理那一组色(--c-chain-*)。同一屏上有两种「当前选中」——流量最后
|
||
* 从哪出去,和流量先经过哪——它们必须一眼分得开,这是含义不是装饰。挑靛蓝是因为它在
|
||
* 这套色板里不表示任何状态,红绿琥珀都被状态占了。别把它当成通用的第二强调色。
|
||
*/
|
||
|
||
:root {
|
||
/* 中性色阶。带一点点冷调,避免纯灰的死板 */
|
||
--c-bg: #fbfbfc;
|
||
--c-surface: #ffffff;
|
||
--c-surface-sunken: #f4f5f7;
|
||
--c-border: #e4e6ea;
|
||
--c-border-strong: #d0d3d9;
|
||
|
||
--c-text: #1c1e21;
|
||
--c-text-soft: #5c6169;
|
||
--c-text-faint: #8b9098;
|
||
|
||
/* 强调色:偏青的蓝,冷静,不抢戏 */
|
||
--c-accent: #2b7a78;
|
||
--c-accent-soft: #e6f2f1;
|
||
--c-accent-text: #1f5c5a;
|
||
|
||
/* 链式代理的「当前选中」。靛蓝,和强调色的青绿色相拉得够开 */
|
||
--c-chain: #5145cd;
|
||
--c-chain-soft: #eeedfb;
|
||
--c-chain-text: #4038a8;
|
||
|
||
--c-danger: #b3261e;
|
||
--c-danger-soft: #fdedec;
|
||
--c-warn: #8a6100;
|
||
--c-warn-soft: #fdf5e3;
|
||
--c-ok: #1f6b3a;
|
||
--c-ok-soft: #e9f5ed;
|
||
|
||
/* 间距。低密度的来源,别调小 */
|
||
--s-1: 4px;
|
||
--s-2: 8px;
|
||
--s-3: 12px;
|
||
--s-4: 16px;
|
||
--s-5: 24px;
|
||
--s-6: 32px;
|
||
--s-7: 48px;
|
||
|
||
--r-sm: 6px;
|
||
--r-md: 10px;
|
||
--r-lg: 14px;
|
||
|
||
--f-sans:
|
||
system-ui, -apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC',
|
||
sans-serif;
|
||
--f-mono: ui-monospace, 'Cascadia Code', 'JetBrains Mono', Consolas, monospace;
|
||
|
||
--t-xs: 12px;
|
||
--t-sm: 13px;
|
||
--t-base: 14px;
|
||
--t-lg: 16px;
|
||
--t-xl: 20px;
|
||
--t-2xl: 26px;
|
||
|
||
--line: 1.7;
|
||
}
|
||
|
||
/*
|
||
* 显式选择的主题(web/prefs.ts 在 <html> 上写 data-theme)。两个方向都要写:
|
||
* 深色系统上选浅色也要生效。选择器权重高于 :root 和上面媒体查询里的 :root,
|
||
* 显式选择压过系统偏好;不标 data-theme 时这两块不命中,媒体查询说了算。
|
||
* 只覆盖颜色变量,间距、字号与主题无关。
|
||
*/
|
||
:root[data-theme='light'] {
|
||
/* 变量之外还要告诉浏览器本身:滚动条、select 弹层这些 UA 部件不看变量,只看 color-scheme */
|
||
color-scheme: light;
|
||
|
||
--c-bg: #fbfbfc;
|
||
--c-surface: #ffffff;
|
||
--c-surface-sunken: #f4f5f7;
|
||
--c-border: #e4e6ea;
|
||
--c-border-strong: #d0d3d9;
|
||
|
||
--c-text: #1c1e21;
|
||
--c-text-soft: #5c6169;
|
||
--c-text-faint: #8b9098;
|
||
|
||
--c-accent: #2b7a78;
|
||
--c-accent-soft: #e6f2f1;
|
||
--c-accent-text: #1f5c5a;
|
||
|
||
--c-chain: #5145cd;
|
||
--c-chain-soft: #eeedfb;
|
||
--c-chain-text: #4038a8;
|
||
|
||
--c-danger: #b3261e;
|
||
--c-danger-soft: #fdedec;
|
||
--c-warn: #8a6100;
|
||
--c-warn-soft: #fdf5e3;
|
||
--c-ok: #1f6b3a;
|
||
--c-ok-soft: #e9f5ed;
|
||
}
|
||
|
||
:root[data-theme='dark'] {
|
||
color-scheme: dark;
|
||
|
||
--c-bg: #16181c;
|
||
--c-surface: #1d2025;
|
||
--c-surface-sunken: #121417;
|
||
--c-border: #2b2f36;
|
||
--c-border-strong: #3a3f48;
|
||
|
||
--c-text: #e6e8eb;
|
||
--c-text-soft: #9ba1a9;
|
||
--c-text-faint: #6c727a;
|
||
|
||
--c-accent: #4db6ac;
|
||
--c-accent-soft: #1b3330;
|
||
--c-accent-text: #7fd4cb;
|
||
|
||
--c-chain: #8b83f0;
|
||
--c-chain-soft: #241f4a;
|
||
--c-chain-text: #a9a2f7;
|
||
|
||
--c-danger: #f2b8b5;
|
||
--c-danger-soft: #3a1f1e;
|
||
--c-warn: #e8c37a;
|
||
--c-warn-soft: #33290f;
|
||
--c-ok: #86d6a0;
|
||
--c-ok-soft: #16301f;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--c-bg: #16181c;
|
||
--c-surface: #1d2025;
|
||
--c-surface-sunken: #121417;
|
||
--c-border: #2b2f36;
|
||
--c-border-strong: #3a3f48;
|
||
|
||
--c-text: #e6e8eb;
|
||
--c-text-soft: #9ba1a9;
|
||
--c-text-faint: #6c727a;
|
||
|
||
--c-accent: #4db6ac;
|
||
--c-accent-soft: #1b3330;
|
||
--c-accent-text: #7fd4cb;
|
||
|
||
--c-chain: #8b83f0;
|
||
--c-chain-soft: #241f4a;
|
||
--c-chain-text: #a9a2f7;
|
||
|
||
--c-danger: #f2b8b5;
|
||
--c-danger-soft: #3a1f1e;
|
||
--c-warn: #e8c37a;
|
||
--c-warn-soft: #33290f;
|
||
--c-ok: #86d6a0;
|
||
--c-ok-soft: #16301f;
|
||
}
|
||
}
|
||
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html,
|
||
body,
|
||
#app {
|
||
height: 100%;
|
||
}
|
||
|
||
/* 页面高度跨过视口阈值时滚动条出现消失,内容会横向抖一下;把滚动条的位置常驻留出来 */
|
||
html {
|
||
scrollbar-gutter: stable;
|
||
}
|
||
|
||
/* 模态框开着时锁住背后的页面滚动。:modal 只匹配 showModal() 打开的 dialog;
|
||
滚动条的位置由上面的 scrollbar-gutter 常驻留出,锁定解锁都不会横向跳动 */
|
||
html:has(:modal) {
|
||
overflow: hidden;
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
background: var(--c-bg);
|
||
color: var(--c-text);
|
||
font-family: var(--f-sans);
|
||
font-size: var(--t-base);
|
||
line-height: var(--line);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
p,
|
||
figure {
|
||
margin: 0;
|
||
}
|
||
|
||
button,
|
||
input,
|
||
select,
|
||
textarea {
|
||
font: inherit;
|
||
color: inherit;
|
||
}
|
||
|
||
code,
|
||
kbd,
|
||
.mono {
|
||
font-family: var(--f-mono);
|
||
font-size: 0.92em;
|
||
}
|
||
|
||
:focus-visible {
|
||
outline: 2px solid var(--c-accent);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* 文本输入的焦点态统一为描边变色,不叠外圈(订阅选择器的形态)。按钮保留外圈,那是键盘可达性 */
|
||
input:focus-visible,
|
||
select:focus-visible,
|
||
textarea:focus-visible {
|
||
outline: none;
|
||
border-color: var(--c-accent);
|
||
}
|
||
|
||
/* 交互控件的颜色过渡。只过渡颜色类属性,不碰布局;时长以「感知得到但不等待」为准 */
|
||
button,
|
||
input,
|
||
select {
|
||
transition:
|
||
color 0.15s ease,
|
||
background-color 0.15s ease,
|
||
border-color 0.15s ease;
|
||
}
|
||
|
||
/* 对话框入场:快速淡入加一点上移 */
|
||
dialog[open] {
|
||
animation: dialog-in 0.15s ease;
|
||
}
|
||
|
||
dialog[open]::backdrop {
|
||
animation: backdrop-in 0.15s ease;
|
||
}
|
||
|
||
/* 对话框退场:单纯的快速淡出。display/overlay 的 allow-discrete 让元素在
|
||
过渡结束前留在 top layer;不支持的浏览器退化为立即消失,无害 */
|
||
dialog,
|
||
dialog::backdrop {
|
||
transition:
|
||
opacity 0.12s ease,
|
||
display 0.12s allow-discrete,
|
||
overlay 0.12s allow-discrete;
|
||
}
|
||
|
||
dialog:not([open]),
|
||
dialog:not([open])::backdrop {
|
||
opacity: 0;
|
||
}
|
||
|
||
@keyframes dialog-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(6px);
|
||
}
|
||
}
|
||
|
||
@keyframes backdrop-in {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
::selection {
|
||
background: var(--c-accent-soft);
|
||
color: var(--c-accent-text);
|
||
}
|