Files
blog/src/styles/global.css
T
wait4xandGLM-5.3 2cab243891 feat(typography): 可视化排版选择器(Sveltia inline 组件)+ 颜色扩充 + 方案 10
- 可视化选择(核心痛点解决):Sveltia 原生支持 registerEditorComponent
  的 inline 形态且 onInsert 能拿到 selectedText → 实现「选中文字 → Insert ▸
  排版 → 选字体/颜色 → 自动包 span class」完整链路;因 inline 组件只能进
  Insert 菜单(field-based 虽可上工具栏但拿不到 selectedText),两个独立组件
  会互相抢 pattern 匹配,故收敛为单入口 + 18 项分组面板(键盘可达/Esc 取消/
  onEdit 回填当前类)
- public/admin/typography-classes.js 为单一事实源(扩展与文档共用)
- 颜色扩充 4 类:ink-warm / ink-cool / ink-faint / ink-accent-soft,全部亮暗
  双态实测 ≥4.5:1(ink-faint 取白底达标的最淡值 4.69/4.75)
- 字体:体积账否决 webfont 新增(@fontsource zcool-xiaowei 用到即 +647.7KB,
  远超 150KB 阈值;smiley-sans 无可用版本、sarasa 包为空壳),改为落地 4 个
  **系统回退类**(font-song/font-kai-sys/font-hei-sys/font-fangsong,零下载,
  文档注明依赖访客设备)
- 白名单 17 类同步;docs/写作指南.md 三表更新
- docs/research/10-页面后台化方案.md:五方案对比 + 推荐方案 E(页面骨架 blocks
  / 区块字段 / 局部自由区 / 样式即数据四层)+ 关于页九类区块 + 消毒与验收断言
  + 分阶段清单(约 7.2d)

验证: 16/16 颜色 computed 与对比度实测;选择器 7/7(桩 CMS 覆盖注册/插入/
      编辑三层);构建 48 页 exit 0;E2E 37/37

Spec: none (排版能力 + 方案文档)

Co-authored-by: GLM-5.3
2026-11-02 16:57:54 +08:00

329 lines
15 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
全局样式 —— 建立在 tokens.css 之上
约束:零运行时框架;动效全部包 prefers-reduced-motion
============================================================ */
@import "./tokens.css";
/* ---------- 轻量 reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
font-family: var(--font-sans);
font-size: 16px;
line-height: 1.6;
color: var(--ink-2);
background: var(--bg);
}
img, svg, video { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
/* 键盘焦点可见但克制 */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* 跳过导航(无障碍动线) */
.skip-link {
position: absolute; left: -9999px; top: var(--space-2);
padding: var(--space-2) var(--space-3); background: var(--bg); z-index: 100;
}
.skip-link:focus { left: var(--space-2); }
/* ---------- 链接(全站唯一强调色纪律) ---------- */
a { color: inherit; text-decoration: none; }
.link, .prose a {
color: var(--accent);
text-decoration: underline;
text-decoration-color: color-mix(in oklab, var(--accent) 40%, transparent);
text-underline-offset: 0.2em;
transition: text-decoration-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.link:hover, .prose a:hover { text-decoration-color: var(--accent); }
/* ---------- 版心 ---------- */
.container { max-width: var(--measure); margin-inline: auto; padding-inline: var(--space-3); }
.container-wide { max-width: calc(var(--measure) + 200px); margin-inline: auto; padding-inline: var(--space-3); }
/* ============================================================
正文分轨(丙路线核心:书评纸墨 × 技术灰阶)
============================================================ */
/* ---- 书评轨:纸面板 + 衬线 + 20/1.75 ---- */
.prose-reading {
font-family: var(--font-serif);
font-size: var(--prose-size-reading);
line-height: var(--leading-reading);
color: var(--paper-ink);
background: var(--paper);
max-width: var(--measure);
padding: var(--space-5) var(--space-4);
border-block: 1px solid var(--paper-line);
overflow-wrap: break-word;
}
.prose-reading p + p { margin-top: 0.9em; } /* 段间距制,无首行缩进 */
.prose-reading h2, .prose-reading h3 { line-height: var(--leading-heading); text-wrap: balance; }
.prose-reading h2 { font-size: var(--text-h2); margin-top: 1.8em; }
.prose-reading h3 { font-size: var(--text-h3); margin-top: 1.5em; }
/* ---- 技术轨:无衬线 + 19/1.7 ---- */
.prose-tech {
font-family: var(--font-sans);
font-size: var(--prose-size-tech);
line-height: var(--leading-tech);
color: var(--ink-2);
max-width: var(--measure);
overflow-wrap: break-word; /* 移动端审计 M1:裸长 URL 曾撑破 88px(只给了书评轨) */
}
.prose-tech p + p { margin-top: 0.9em; }
.prose-tech h2 { font-size: var(--text-h2); margin-top: 1.8em; line-height: var(--leading-heading); text-wrap: balance; }
.prose-tech h3 { font-size: var(--text-h3); margin-top: 1.5em; line-height: var(--leading-heading); }
/* ---------- 引文组件(报告 §3:A 墨线长书摘 / D 金句) ---------- */
/* 裸 blockquote(markdown 原生输出无类名)默认即模式 A;显式类可覆盖 */
.prose-reading blockquote, blockquote.quote-block {
border-left: 1px solid var(--paper-line);
border-left-width: 2px;
padding-left: 1.35em;
margin-block: 1.2em;
color: color-mix(in oklab, var(--paper-ink) 72%, transparent);
font-size: 1em;
font-style: normal; /* 反例修正:higan 整段加粗弃用 */
font-weight: 400;
}
.quote-cite {
display: block;
margin-top: 0.6em;
font-size: 0.875rem;
font-family: var(--font-sans);
color: var(--ink-3);
}
/* 模式 D:金句(62px 巨型引号水印) */
.pull-quote {
font-family: var(--font-kai);
position: relative;
margin-block: 1.6em;
padding-left: 2.2em;
font-size: 0.96em;
}
.pull-quote::before {
content: "\201C";
position: absolute; left: 0; top: -0.12em;
font-family: Georgia, serif;
font-size: 62px;
line-height: 1;
opacity: 0.18;
color: var(--mood);
}
/* ---------- 代码(终端风,亮暗同底) ---------- */
code, kbd, samp { font-family: var(--font-mono); }
:not(pre) > code {
font-size: 0.875em;
background: var(--bg-soft);
padding: 0.15em 0.4em;
border-radius: 4px;
border: 1px solid var(--line);
}
pre {
background: var(--code-bg);
color: var(--code-ink);
font-size: 16px;
line-height: 1.6;
padding: var(--space-3) var(--space-4);
border-radius: 8px;
overflow-x: auto; /* 禁 break-all:正文横向滚动绝不发生 */
tab-size: 2;
}
pre code { background: none; border: none; padding: 0; font-size: inherit; }
/* 终端窗点缀(圆点由组件层加,此处只管字色纪律) */
.term-dots { display: flex; gap: 6px; margin-bottom: var(--space-2); }
.term-dots span { width: 10px; height: 10px; border-radius: 50%; background: #30363d; }
/* ---------- 正文媒体(图/视频/音频,Phase 5 媒体支持) ---------- */
/* 图片:markdown 相对路径经 Astro 图片服务优化;版心内圆角限宽 */
.prose-reading img, .prose-tech img {
border-radius: 10px;
margin-block: 1.2em;
max-width: 100%;
height: auto;
}
/* 图注:**显式类驱动**(`<p class="fig-cap">说明</p>`)。此前按 `p > em:only-child` 自动命中,
把正文里任何独占成段的斜体都变成块级居中——用户实测的排版 bug(斜体单独一行并居中)由此而来。 */
.prose-reading p.fig-cap, .prose-tech p.fig-cap,
.prose-reading p.fig-cap em, .prose-tech p.fig-cap em {
display: block;
text-align: center;
font-size: 0.85em;
font-family: var(--font-sans);
color: var(--ink-3);
margin-top: -0.6em;
margin-bottom: 1.2em;
font-style: normal;
}
/* 行内斜体基线:不换行、不居中(正常强调语义) */
.prose-reading em, .prose-tech em,
.prose-reading i, .prose-tech i {
display: inline;
font-style: italic;
}
/* ── 受限排版类(文章内通过 <span class> 使用;rehype 白名单只放行这些类名) ──
颜色一律走 tokens,亮暗双态都可读;禁任意 hex/内联样式(保持灰阶骨架)。 */
.prose-reading .ink-1, .prose-tech .ink-1 { color: var(--ink-1); }
.prose-reading .ink-2, .prose-tech .ink-2 { color: var(--ink-2); }
.prose-reading .ink-3, .prose-tech .ink-3 { color: var(--ink-3); }
.prose-reading .ink-muted, .prose-tech .ink-muted { color: var(--ink-4); }
.prose-reading .ink-accent, .prose-tech .ink-accent { color: var(--accent); }
.prose-reading .ink-accent-soft, .prose-tech .ink-accent-soft { color: var(--accent-soft-text); }
.prose-reading .ink-warm, .prose-tech .ink-warm { color: var(--ink-warm); }
.prose-reading .ink-cool, .prose-tech .ink-cool { color: var(--ink-cool); }
.prose-reading .ink-faint, .prose-tech .ink-faint { color: var(--ink-faint); }
/* 语义色板:彩色只作用于正文强调(站点外壳保持灰阶)。
每色相两档:标准 / soft(阈值内最淡);亮暗各取独立值,均实测 ≥4.5:1。 */
.prose-reading .c-blue, .prose-tech .c-blue { color: var(--c-blue); }
.prose-reading .c-blue-soft, .prose-tech .c-blue-soft { color: var(--c-blue-soft); }
.prose-reading .c-teal, .prose-tech .c-teal { color: var(--c-teal); }
.prose-reading .c-teal-soft, .prose-tech .c-teal-soft { color: var(--c-teal-soft); }
.prose-reading .c-green, .prose-tech .c-green { color: var(--c-green); }
.prose-reading .c-green-soft, .prose-tech .c-green-soft { color: var(--c-green-soft); }
.prose-reading .c-yellow, .prose-tech .c-yellow { color: var(--c-yellow); }
.prose-reading .c-yellow-soft, .prose-tech .c-yellow-soft { color: var(--c-yellow-soft); }
.prose-reading .c-orange, .prose-tech .c-orange { color: var(--c-orange); }
.prose-reading .c-orange-soft, .prose-tech .c-orange-soft { color: var(--c-orange-soft); }
.prose-reading .c-red, .prose-tech .c-red { color: var(--c-red); }
.prose-reading .c-red-soft, .prose-tech .c-red-soft { color: var(--c-red-soft); }
.prose-reading .c-pink, .prose-tech .c-pink { color: var(--c-pink); }
.prose-reading .c-pink-soft, .prose-tech .c-pink-soft { color: var(--c-pink-soft); }
.prose-reading .c-purple, .prose-tech .c-purple { color: var(--c-purple); }
.prose-reading .c-purple-soft, .prose-tech .c-purple-soft { color: var(--c-purple-soft); }
.prose-reading .font-serif, .prose-tech .font-serif { font-family: var(--font-serif); }
.prose-reading .font-kai, .prose-tech .font-kai { font-family: var(--font-kai); }
.prose-reading .font-sans, .prose-tech .font-sans { font-family: var(--font-sans); }
.prose-reading .font-mono, .prose-tech .font-mono { font-family: var(--font-mono); font-size: 0.92em; }
/* 系统字族回退类:不下载字体,命中访客本机字体——跨设备不保证一致(docs 已注明) */
/* 自托管优先 + 系统兜底(2026-11-02 口径):跨设备一致性由站内 webfont 保证,
系统字族只作最后兜底;font-fangsong 暂无 OFL 自托管包,仍为纯系统回退(docs 已注明)。 */
.prose-reading .font-song, .prose-tech .font-song {
font-family: "Noto Serif SC Variable", "Noto Serif SC", "Songti SC", SimSun, serif;
}
.prose-reading .font-kai-sys, .prose-tech .font-kai-sys {
font-family: "LXGW WenKai Screen", "Kaiti SC", STKaiti, KaiTi, serif;
}
.prose-reading .font-hei-sys, .prose-tech .font-hei-sys {
font-family: "Noto Sans SC Variable", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
}
.prose-reading .font-fangsong, .prose-tech .font-fangsong {
font-family: FangSong, STFangsong, "FangSong_GB2312", serif;
}
/* 霞鹜文楷 Light(自托管 @fontsource,OFL-1.1):更轻的金句字重 */
.prose-reading .font-kai-light, .prose-tech .font-kai-light {
font-family: 'LXGW WenKai', var(--font-kai);
font-weight: 300;
}
/* 文学常用:首字下沉(书评开篇)与金句块(引文式的独立强调段) */
.prose-reading p.drop-cap::first-letter, .prose-tech p.drop-cap::first-letter {
float: left;
font-family: var(--font-serif);
font-size: 3.1em;
line-height: 1;
padding-right: 0.08em;
margin-top: 0.05em;
color: var(--paper-ink);
}
.prose-reading .quote-line, .prose-tech .quote-line {
display: block;
font-family: var(--font-kai);
color: var(--paper-ink);
border-left: 2px solid var(--paper-line);
padding-left: 0.9em;
margin: 1.4em 0;
}
/* 视频与音频:原生标签,markdown raw HTML 直书(指南有模板) */
.prose-reading video, .prose-tech video,
.prose-reading audio, .prose-tech audio {
display: block;
width: 100%;
margin-block: 1.2em;
}
.prose-reading video, .prose-tech video {
border-radius: 10px;
background: #000;
max-height: 72dvh;
}
.prose-reading audio, .prose-tech audio {
border-radius: 999px;
}
/* ---------- 列表/分隔 ---------- */
hr { border: none; border-top: 1px solid var(--line); margin-block: var(--space-4); }
/* ---------- 选区(半透明强调,避免刺眼实心) ---------- */
::selection { background: var(--accent-soft); }
/* ---------- 触屏与安全区(§4e:交互组件显式 .tap 类达 44px 目标) ---------- */
.tap { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
.safe-area { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }
/* ---------- 移动端排版降档(≤560px:书评 20→18) ---------- */
@media (max-width: 560px) {
:root { --prose-size-reading: 18px; --prose-size-tech: 18px; --text-h1: 24px; }
.prose-reading { padding: var(--space-4) var(--space-3); }
}
/* ---------- 动效总闸(唯一曲线已入 tokens) ---------- */
/* ===== 动效层(Phase 5e):背景辉光 + 入场 + 微交互 ===== */
:root { --mx: 50%; --my: 30%; }
.bg-glow {
position: fixed; inset: 0; z-index: -1; pointer-events: none;
background: radial-gradient(600px circle at var(--mx) var(--my),
color-mix(in oklab, var(--ink-1) 4%, transparent), transparent 70%);
opacity: 0; transition: opacity 0.6s ease;
}
@media (pointer: fine) {
.bg-glow { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .bg-glow { opacity: 0; } }
}
/* 正文入场:一次性轻上浮(VT 换页内容替换自然重放,克制 12px/0.4s) */
#main > * { animation: rise-in 0.4s ease both; }
@keyframes rise-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
#main > * { animation: none; }
}
/* 列表行微交互(桌面悬停语言:ink 系已有变色,补 1px 上浮) */
@media (pointer: fine) {
.postlist .row { transition: transform 0.15s ease; }
.postlist .row:hover { transform: translateY(-1px); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* ===== Phase 5f T7:流距变量应用(标题前后距/p+p 统一双轨) ===== */
.prose-reading h2, .prose-tech h2 { margin-top: var(--flow-h2-pre); margin-bottom: var(--flow-h2-post); }
.prose-reading h3, .prose-tech h3 { margin-top: var(--flow-h3-pre); margin-bottom: var(--flow-h3-post); }
.prose-reading p + p, .prose-tech p + p { margin-top: var(--flow-p); }
/* 页面标题字重 700→600(视觉裁定 #6:要轻减字重不减号;!important 压组件 scoped 默认) */
h1.page-title { font-weight: 600 !important; }
/* focus ring 收尾(O 卡:统一可键盘巡览性) */
a:focus-visible, button:focus-visible, summary:focus-visible {
outline: 2px solid var(--ink-3); outline-offset: 2px; border-radius: 4px;
}
/* 列表行距微增(裁定 #7:行距 +2~4px 优先于删线) */
.postlist .row { padding-block: calc(var(--space-2) + 2px); }
/* ===== Phase 5f T4:reader 纯文本模式(08 文档 §4.4 清单,视觉语义=去装饰非去内容) ===== */
html.reader .bg-glow { display: none !important; }
html.reader .site-header .nav-actions { display: none; } /* 收图标钮:保品牌+导航词 */
html.reader .site-footer .fine > *:not(a[href*="beian"]):not(.icp-sep) { display: none; }
html.reader .atk-pv-line { display: none; } /* 元信息让位正文 */
html.reader .prose-reading, html.reader .prose-tech {
font-size: calc(var(--prose-size-reading, 1.05rem) + 0.0625rem);
max-width: 56ch; /* 行宽收窄 */
}
html.reader #main > * { animation: none; } /* 入场动效关闭 */
/* ICP 备案链接永不隐藏(合规底线);分隔点号随之保留必要最小集 */