Files
wordpress-kratos-yang/assets/css/site.css
T
laoyang 05b4b56bf6 4.0.0 引用块首尾加引号,让引用文字与正文段落对齐 #32
用户诉求(原话):引用块有色块和左侧宽度,导致块内文字比正文段落更靠右,
缩进不一致。方案:色块保留,在引用首尾各加一个引号,用引号占住那段缩进。

改前实测(1440 视口,线上真实文章):
  正文段落 p  文字起点 99
  blockquote  框内沿 89,块内 p 文字起点 121
  ⇒ 引用文字比正文靠右 22px,且块内段落还叠了一层 32px 首行缩进

修法:
  · border-left 6 + padding-left 26 = 32 = 正文 text-indent:2em(16px×2)
  · 块内 > p 的 text-indent 归零,消除「块内缩 + 段首缩进」双层叠加
  · 左引号 position:absolute 悬浮在 padding 区(inline 会被 block 级 p
    挤成匿名块行,跑到段落上方)
  · 右引号挂 > p:last-of-type,display:inline-block + line-height:0
    + height:0 + overflow:visible —— 行盒贡献 0,不撑高(否则块高 89→121)
  · 引号放大到 60/56px,淡色 #c6d2dd 做装饰;暗色降到 #3d4b58

两个踩到的坑(都只有像素级测量能发现):
  ① CJK 全角字形左侧 side bearing ≈ 0.62em。30px 字号下伪元素盒在
     x=12(完全正确),但引号墨迹在 30.5~40,正压在首字 32.5 上 ——
     「盒对了」≠「字形画在那儿」。指定 Georgia 等拉丁衬线字体后
     side bearing 降到 ≈0.03em,再用 left:2px/top:15px/font-size:60px。
  ② 窄屏(≤768px)kratos.css 把 .content p 降到 15px ⇒ 2em=30,
     硬编码的 32 就多 2px。不能写 calc(2em - 6px)(em 基数是 blockquote
     自己的 15px,不随视口变),只能在同一个断点覆盖;且这个 @media
     必须放在 ::before 基础规则之后,否则同特异性被后出现的 left:2px 压掉。

版本号 3.2.64 → 4.0.0(用户要求跳大版本:引用块渲染方式变了,
第三方 CSS 若针对 blockquote 内部做微调会受影响)。

验证:
  _geom32.js          48/0(3 篇 × 5 视口,对齐差全部 0.00px)
  _pix400.py          76/0(像素差分:墨迹落在缩进空白内、不压字、不撑高)
  _mkzip400.py        61/0
  _mkzip400_falsify   20/0(18 组注入全部报红在预期判据)
  _pix400_falsify      8/0
  _readme400.py       36/0(readme 15 节,181195 字节)
  php -l              97 文件 / 0 失败

包 kratos_yang-4.0.0.zip  md5 bc23f478d5f5170524aec3921b10e6ff
已发 /theme-update/,update.json 指向 4.0.0,PUC 实测 getUpdate=4.0.0。
主题目录未覆盖(仍是 3.2.64),等用户在后台点升级。
2026-10-28 12:15:38 +08:00

671 lines
35 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.
@charset "UTF-8";
/**
* 站点级公共样式(3.2.29 起)
*
* 这些规则原来是站长手填在「主题设置 → 自定义代码 → 自定义 CSS」里的,
* 3.2.29 把它们并入主题公共样式,配置项因此留空。
*
* ⚠️ 选择器与声明**必须与原来的自定义 CSS 逐字一致**:
* 自定义 CSS 是以 `<style>` 形式紧跟在 kratos.min.css 之后输出的,
* 所以「排在最后 + 同选择器」才等价于原来的层叠结果。
* 本文件的加载顺序同样是 kratos.min.css 之后(enqueue 依赖 'kratos'),
* 改选择器范围(例如把 ul 收窄到 .article .content ul)会改变实际渲染。
*
* @author Yongquan Yang <yangyq.net>
* @license MIT License
*/
/* 正文段落首行缩进两字 */
.article .content p {
text-indent: 2em;
}
/* ── 正文插图:不吃段落的首行缩进,自成一行、左右都不缩进(3.2.60 / 工单 #28)
*
* 🔴 为什么必须有这一段:`text-indent` 是**可继承属性**。
*
* WP 把图片插进段落时,DOM 是 `<p><img></p>` 或 `<p><a><img></a></p>`。
* 图片(或包着它的 <a>)作为**行内级盒子**落在段落首行
* ⇒ 继承 32px 缩进,整体右移。
* 而 `max-width: 100%` 里的 100% 是**包含块(p)的内容宽度**,
* 不是"缩进之后剩下的宽度" ⇒ 图片仍按全宽铺开。
*
* 结果:左边缩进多少,右边就溢出多少,**二者永远相等**。
* 线上实测(2022/05/set-font-in-latex):左缩进 32px、右溢出 32px,
* 正文可绘制区 111~902.33,图片却是 143~934.33。
*
* ── ⚠️ 只给 img 清零 text-indent 是**不够**的(第一版就这么错)────────
*
* 实测:`.article .content img{ text-indent:0; display:block }` 之后,
* 图片自己的 computed text-indent 确实是 0px、display 也是 block,
* 但几何仍停在 143(照样偏 32px)。
*
* CDP `CSS.getMatchedStylesForNode` 抓到的真相:
* WP 会把图片包进 `<a class="yang-lb-item">`(灯箱),
* 而 lightbox.css 给它设了 `display: inline-block`。
* **inline-block 盒子的起点同样受父级 text-indent 影响** ——
* 它继承了 p 的 32px,于是整个「链接 + 图片」盒子被推进去 32px。
* 图片在盒子内部再怎么清零,也改不了盒子自己的起点。
*
* ⇒ 缩进必须清在**真正参与父级行内排版的那个盒子**上,
* 也就是 a.yang-lb-item / 裸 img 两者都要清。
*
* 修法:
* ① `text-indent: 0` 切断继承(不改 p 上的规则本身 ——
* 段落首行缩进是站长期望的效果,去掉会改变全站观感);
* ② `display: block` 自成一行,不参与首行的行内排版;
* ③ 图片用 `margin: … auto` 居中。
*
* ⚠️ 只看 `.content img`(后代)而不是 `> img`:图片外面常套一层
* `<a class="yang-lb-item">`(灯箱),直接子元素选择器量不到。
*
* ⚠️ 对齐类图片用 `:where()` 把特异性降到 0,让 kratos 的
* `.alignright{float:right}` / `.aligncenter` 等**照常生效**:
* 浮动的图本来就不该吃缩进,也不该被强制成块级居中。
* `:where()` 是 CSS4,Chrome 88+ / Safari 14+ / Firefox 78+ 支持;
* 更老的浏览器会整条规则失效 —— 退化成"维持现状",不会更坏。 */
.article .content img {
text-indent: 0;
display: block;
margin-left: auto;
margin-right: auto;
}
/* 🔴 灯箱链接:必须一起清零。它是 inline-block,本身就是参与父级
* 行内排版的盒子 —— 只清 img 无论用,位置由它决定。
* 见上面实测说明。 */
.article .content a.yang-lb-item {
text-indent: 0;
display: block;
line-height: 0;
margin-left: auto;
margin-right: auto;
}
/* 表情图必须保持行内:正文里它是夹在文字中的小图,
* 变 block 会换行、把句子拆断。特异性 (0,2,1) 压过上面那条。 */
.article .content img.wp-smiley,
.article .content img.emoji {
display: inline;
margin: 0;
vertical-align: middle;
text-indent: 0;
}
/* 对齐类图片交还给 kratos 的浮动/居中语义(特异性 0,不参与竞争)。
* 连包裹它的灯箱链接一起处理 —— 否则链接是 block、图片却 float,
* 会空出一整行。 */
:where(.article .content) img.alignright {
display: inline;
float: right;
margin: 0 0 8px 16px;
}
:where(.article .content) img.alignleft {
display: inline;
float: left;
margin: 0 16px 8px 0;
}
:where(.article .content) img.aligncenter {
display: block;
margin-left: auto;
margin-right: auto;
}
:where(.article .content) a.yang-lb-item:has(img.alignright) {
display: inline;
float: right;
margin: 0;
}
:where(.article .content) a.yang-lb-item:has(img.alignleft) {
display: inline;
float: left;
margin: 0;
}
/* 正文二级标题:左侧蓝条 + 浅灰底,横向铺满(负 margin 吃掉正文的 16px 内距)
*
* 3.2.61(工单 #29 追加):字号 20→24、字重 500→700。
* 🔴 这是 3.2.60 留下的层级倒挂的修复:3.2.60 把 h3 提到 20px/600,
* 而 h2 一直是 20px/500 —— 结果 h2 和 h3 同字号、h3 还更粗 100,
* 三级标题比二级标题更抢眼。用户一眼就看出"h3 比 h2 还大"。
* 根因是当时只动了 h3,没把 h2 一起拉起来,而且判据里
* **没有任何一条断言 h2 > h3**,所以 52 条全绿也照样漏了。
* ⇒ 往后调任一标题级别,必须同时断言相邻两级的**字号 + 字重**
* 都单调递减(见 _geom361_route.js 的 G 组)。
*
* 行高 41→46:24px 字配 41px 行高,上下各只剩 8.5px,灰底看着发扁;
* 46px 让上下各留 11px。这个 11px 是**算出来的**:
* 24px 字的实际墨迹高约 24×0.88 ≈ 21px(中文黑体 ascent+descent 之内),
* 行框 46px 减 21px 再除以 2 ≈ 12.5px,与 3.2.60 的 20px/41px
* (20×0.88=17.6,41-17.6=23.4,/2≈11.7px)基本持平。 */
.k-main .details .article .content h2 {
margin: 16px -16px;
padding: 0 10px;
border-left: 5px solid #00a2ff;
background-color: rgba(243, 243, 243, 0.7);
font-size: 24px;
font-weight: 700;
line-height: 46px;
}
/* ── 正文三级标题:左侧细竖线 + 底部细线(3.2.60 / 工单 #29)
*
* 3.2.60 之前 h3 只有 `font-size:18px; font-weight:500`,没有任何装饰,
* 而正文是 16px/350 —— **只差 2px 字号、150 字重**,放在长文里根本分不出来。
*
* 层级梯度(3.2.61 定稿,三级 + 四级同步调整):
* h2 24px / 700 / 左 5px 蓝条 + 浅灰底 ← 最重,分节
* h3 20px / 600 / 左 3px 蓝条 + 底 1px 细线,无底色
* h4 17px / 500 / 纯文字,无装饰
* p 16px / 350
*
* 🔴 两条铁律(3.2.60 违反第 1 条,导致 h3 比 h2 还抢眼):
* ① 字号与字重**同时**严格递减:24>20>17>16、700>600>500>350;
* ② 装饰强度也递减:蓝条+灰底 > 蓝条+细线 > 无 > 无。
* 任一级调了,必须回头核对相邻两级的下行关系,别只盯自己那一级。
*
* 为什么 h3 竖线用 3px 而不是 h2 的 5px:同族但更轻,一眼能看出次一级。
* 为什么 h3 不加底色:h2 已经有灰底,h3 再铺一层整篇会碎成一块块。
* 为什么 h4 用 500 而不是 600:600 与 h3 同字重,就只剩字号 3px 的差别,
* 视觉上"像同一个级别被缩了一点"。降到 500 后,字重也参与分层。
* 500 仍然明显重于正文的 350(差 150),不会糊回正文里去。
*
* ⚠️ 特异性必须是 (0,4,1) 起步、并把字号/字重一起写死:
* kratos 的 `.k-main .details .article .content h3` 是 (0,4,1),
* 只写 `.article .content h3`(0,2,1)会被它压掉 —— 这在本项目
* 踩过一次(archives.css 里 22px/700 写了两版都没生效,实际一直是 18px/500)。
* 所以这里与 h2 用同一套前缀,并且 font-size / font-weight 都显式声明。 */
.k-main .details .article .content h3 {
margin: 24px 0 16px;
padding: 2px 0 2px 10px;
border-left: 3px solid #00a2ff;
border-bottom: 1px solid #eaedf2;
font-size: 20px;
font-weight: 600;
line-height: 32px;
}
/* ── 正文四级标题:17px / 500(3.2.61 定稿)
*
* 字号 17px 保持不动(h3 20px 之下、正文 16px 之上的唯一合理档位,
* 再往下压到 16px 就与正文同大、层级直接断层)。
* 字重 600 → 500:600 与 h3 完全同重,等于只有 3px 字号差,
* 看着像"同一级被缩小",不像下一级。降到 500 让字重也参与分层。
*
* 不加任何装饰 —— 到第四级再加线就满屏都是线了。 */
.k-main .details .article .content h4 {
margin: 20px 0 12px;
font-size: 17px;
font-weight: 500;
line-height: 30px;
}
/* 🔴 归档页的年份标题也是 `<h3 class="ar-year">`,而且它就在
* `.article .content` 里面([Archives] 是正文短代码)。
* 上面那条 h3 会命中它 —— 于是年份会多出一条左竖线和一条底细线,
* 而 archives.css 里 `.ar-head` 已经有自己的底边线,会变成**两条线**。
*
* 这不是"顺手加个补丁",是必须的:archives.css 的 `.ar-year`
* 靠 (0,5,0) 才压过 kratos,本文件若也用 (0,5,0) 且排在它后面,
* 谁赢就变成"看入队顺序",非常脆。
*
* 这里主动让路:把 ar-year 的装饰清零,尺寸仍交给 archives.css 管
* (它写 26px/700,本规则不碰字号,避免两边抢)。 */
.k-main .details .article .content h3.ar-year {
padding: 0;
border-left: 0;
border-bottom: 0;
margin: 0;
}
/* ── 正文标题的自动编号(3.2.62 / 工单 #30)
*
* 结构由 PHP 插入:<h2 id="x"><span class="yang-h-num">1.1</span>标题</h2>
* 编号是**独立 span**,不是改标题文本 —— 所以不进编辑器、不进 RSS、
* 不进搜索结果、不进摘要。作者手写编号时压根不插这个 span。
*
* ── 为什么用灰色(用户选定的方案)──────────────────────────────
* 标题的核心是**文字本身**,编号只是索引辅助。若与标题同色同粗,
* 视觉上"1.1"会和标题正文抢注意力,长文章里满屏都是数字。
* 灰色 + 字重 400 让编号退到背景,眼睛扫标题时能忽略它、需要定位时
* 又能看见 —— 这也与目录里 `.yang-toc-num` 的处理一致,
* 正文和目录的编号看起来是同一套东西。
*
* ── 字号是 1em(与标题完全同大,3.2.63 起)────────────────────
* 🔴 0.62em 那一版**用户否掉了**:「字体不能太小,要不然很不协调,看着难受」。
* 实测 0.62em 在 h2 上是 14.88px、而标题 24px —— 差 9px,比例失衡,
* 编号像"贴在标题边上的小注脚",不像标题的一部分。
* 改成 1em 之后:h2 编号 24px、h3 20px、h4 17px,与标题逐级同大。
* 弱化只靠**颜色 + 字重**(灰 #a8abb2 / 400),不靠缩字号。
*
* ⚠️ 换成 1em 有个连带项:`margin-right` 原来是 0.35em,
* 而 em 是相对**编号自身**字号的。字号从 14.88 → 24 后,
* 同一个 0.35em 会从 5.2px 变成 8.4px,间距明显偏大。
* 所以同步收到 **0.25em**(24px ⇒ 6.0px、20 ⇒ 5.0px、17 ⇒ 4.25px),
* 与原来 5.2px 的观感基本一致。
*
* ── font-weight / font-style 必须显式重置 ────────────────────
* span 会继承标题的 font-weight(700/600)和可能的斜体。
* 不重置的话"灰色但加粗"仍然很重,弱化就白做了。
*
* ── 🔴 为什么仍保留 `display: inline-block` ────────────────────
* 这一条最初是为了修「小字号撑高行盒」,字号改成 1em 之后
* **同字号本身已经不撑高了**(实测 inline + inherit 也是 46px)。
* 但仍然保留,两个理由:
* ① 防回归 —— 以后谁把字号改回小号,不会立刻复发 3px;
* ② 编号成为原子盒,永远不跨行断开,始终贴在标题第一行开头。
*
* 下面是当时那个 3px 问题的完整记录,留着是因为**修法不直观**:
* 小字号的行内元素会把行盒**撑高**,实测 h2 46 → **49px**、h3 37 → **40px**,
* 每级都多 3px。3px 不起眼,但它出现在**每一个**标题上:长文 10 个二级标题
* 就多出 30px,h2 的灰底跟着变高,整篇标题的节奏全变;更麻烦的是它**只在
* 该标题有编号时出现**,于是「有编号的文章」和「没编号的文章」行距不一致,
* 极难归因。
*
* ⚠️ 我先试过 `line-height: inherit`(以及写死 46px)—— **完全无效**,
* 仍 49px。原因值得记下来,因为直觉是反的:
*
* 同行高下,行内盒的上下边界由「半行距」决定,而半行距
* 把 content-area **居中**塞进 line-height:
*
* 盒底 = baseline + desc×fs + (L − fs) / 2
* h2 (fs=24, L=46): 0.25×24 + (46−24)/2 = 17.00
* span(fs=14.88,L=46): 0.25×14.88+ (46−14.88)/2 = 19.28 ← 多探 2.28
*
* **字号越小,半行距越大**,盒底反而越往下走。所以「让两者共用行高」
* 不是解药,恰恰就是病根 —— line-height 怎么调都救不回来。
*
* ⚠️ `vertical-align` 也试过:`baseline` 仍 49px;`middle` 48.52px。
*
* ✅ 真解:把编号变成**原子行内盒**(inline-block),它就只按自身内容
* 高度参与行盒,不再受半行距拉扯。配 `line-height: 1` 让盒高正好
* 等于字号(14.88px),基线落在 asc 处 —— 上下都远在 strut 之内,
* 实测 h2 46→**46**、h3 37→**37**,一分不多。
* (八组隔离试验的完整数据见 `_tooling/yang_theme_test/_diag362b.js`。)
*
* 副作用检查:inline-block 不会跨行断开 ⇒ 编号永远留在标题第一行开头,
* 正是想要的;`white-space: nowrap` 随之只作用于这个原子盒,不会影响标题文字。
*
* ⚠️ 归档页 `h3.ar-year` 同样不会带编号 —— 编号是在 the_content
* 过滤器里按「目录条目」插的,而归档短代码的年份标题不在
* yang_toc_scan() 的结果里(它们是 [Archives] 渲染时才产生的),
* 所以天然不受影响。评论区 h3 同理(不在 .content 内)。 */
.k-main .details .article .content .yang-h-num {
/* 🔴 原子行内盒:不参与行盒的半行距计算,标题高度才不会被撑高(见上)
*
* 3.2.63:字号改成 1em(与标题同大)。用户反馈 0.62em 太小、"很不协调"。
* 实测同字号之后,inline / inherit 也不会撑高了(撑高本来就是小字号的
* 半行距造成的,字号一致时半行距也一致)。但 display 仍保留 inline-block:
* ① 防回归 —— 以后若有人把字号改回小号,不会立刻复发 3px 问题;
* ② 编号变成原子盒,永远不会跨行断开,始终贴着标题第一行开头。 */
display: inline-block;
line-height: 1;
/* 🔴 margin-right 是相对**编号自身**字号的(0.35em 在 14.88px 时是 5.2px)。
* 字号改成 1em 后同一个 0.35em 会变成 8.4px,间距明显偏大 ⇒ 同步收到 0.25em
* (24px 标题 ⇒ 6.0px,20px ⇒ 5.0px,17px ⇒ 4.25px,与原来的观感接近)。 */
margin-right: 0.25em;
color: #a8abb2;
font-size: 1em;
font-weight: 400;
font-style: normal;
/* 数字等宽,1.1 和 3.10 宽度一致,多行标题才不会看着参差 */
font-variant-numeric: tabular-nums;
/* 编号自身不换行(inline-block 本来也不拆,写上是为了并列时也不拆) */
white-space: nowrap;
}
/* 暗色站点:跟目录序号同一个灰,否则 #a8abb2 在深色底上过亮 */
html.yang-dark .k-main .details .article .content .yang-h-num {
color: #767d88;
}
/* 列表缩进与行距 */
ul {
margin-left: 2em;
}
ol {
margin-left: 2em;
}
ol > li {
line-height: 1.8;
}
ul > li {
line-height: 1.8;
}
/* ══════════════════════════════════════════════════════════════════
* 容器宽度:改为流式(工单 #31)
*
* ── 改之前是什么样(线上实测,Playwright 逐 1px 扫 320~1920)──────
* Bootstrap 的 `.container` 只有 **4 个固定 max-width 档位**:
*
* 视口 576~767 → 容器 540px 单边留白 18~113.5px
* 视口 768~991 → 容器 720px 单边留白 24~135.5px
* 视口 992~1309 → 容器 960px 单边留白 16~174.5px
* 视口 1312~1920 → 容器 1280px 单边留白 16~320px
*
* 每个档位内宽度**完全不变**,于是窗口越宽、两侧空白越宽;跨过断点
* 宽度瞬间跳一格,留白突变 89.5 / 119.5 / 158.5px —— 这就是用户说的
* 「一蹦一蹦」「某些固定宽度下两边有两个非常宽的空白」。
*
* 根本问题不是"断点太少",而是**容器宽度本身只有 4 个取值**。
* 加密断点只是让台阶小一点,仍然是台阶。
*
* ── 现在怎么算 ─────────────────────────────────────────────────
* 用 **min()** 让宽度连续跟随视口、到上限后居中:
*
* width: min(1440px, 100% - 2×边距)
*
* · 视口窄时 → `100% - 边距` 生效,宽度随窗口连续变化,两侧永远只有
* 固定边距(不留大空白);
* · 视口宽到某个点 → `1440px` 生效,超出部分居中留白,正文不会无限拉长。
*
* 两个分支在交界处**数值相等**(1440 + 2×边距),所以过渡是**连续的**,
* 没有台阶、没有突变。这是 min() 相比 media query 的本质优势:
* media query 是**离散判断**,min() 是**连续取值**。
*
* ⚠️ 上限 1440px 是用户选定的(工单 #31)。原来 Bootstrap 最高到 1280,
* 1920 屏上单边空 320px;改 1440 后 1920 屏单边空 240px,同时正文
* 依然有足够的宽度可读性。
*
* ⚠️ 边距用 `max(16px, 2.5vw)` 而不是定值:窄屏保证 16px 不被吃掉
* (手机上贴边很难看),宽屏略微增加,与视口更协调。2.5vw 在 1440
* 视口时 = 36px,正好让上限分支的边距看起来不突兀。
*
* 🔴 三条规则都要,缺一条就前功尽弃:
* ① `.container` 基础规则 —— 覆盖 Bootstrap 的 `width:100%` 与 4 个
* `max-width` 档位(后者全在 `@media (min-width:…)` 里,优先级
* (0,1,0) 与基础规则相同,靠**后加载**取胜,见下方说明);
* ② 页面上有 **3 个** `.container`(导航 / 主内容 / 页脚),
* 必须同一条规则覆盖,否则导航和页脚会跟正文错位;
* ③ `max-width` 必须显式设成 `none`,否则 Bootstrap 的
* `@media (min-width:576px){.container{max-width:540px}}` 依然生效
* —— 它和我们的基础规则同优先级,而 media query 里那条**更晚**
* 出现在 bootstrap.min.css 里;我们虽然在 site.css(更晚加载),
* 但 `max-width` 那条属性必须被我们**显式声明**才能压住。
*
* 加载顺序(inc/theme-core.php):bootstrap → kratos.min → **site.css**,
* 所以同优先级下 site.css 胜出。
* ⚠️ 不要用 `!important`:Bootstrap 里 `@media print` 有一条
* `.container{min-width:992px!important}` 是打印用的,我们改的是
* `width`/`max-width`,不冲突;一旦上 `!important` 反而会与打印样式
* 打架。 */
.container {
/* 宽度 = min(上限, 视口 - 两侧边距)。连续取值,无台阶。 */
width: min(1440px, 100% - 2 * max(16px, 2.5vw));
/* 🔴 必须显式覆盖 Bootstrap 的 4 个 max-width 档位(540/720/960/1280) */
max-width: none;
/* 左右 auto 居中:窄屏时 width 已经等于可用宽度,auto 不产生额外偏移;
宽屏时 width 封顶 1440,auto 把剩余空间均分到两侧。 */
margin-right: auto;
margin-left: auto;
/* Bootstrap 原来是 15px;这里改由 width 的边距项承担,
否则会**双重扣减**(padding 15 + width 里再扣一次边距),
窄屏上内容被吃掉 30px。 */
padding-right: 0;
padding-left: 0;
}
/* 栅格行原本用 margin:-15px 抵消 container 的 15px padding。
现在 container 的 padding 已经是 0,这 -15px 会把内容**往外顶** 15px、
直接溢出容器右侧。所以同步归零。
(`.row` 只出现在 .k-main 里,导航/页脚没有 .row,不必担心误伤。) */
.k-main .row {
margin-right: 0;
margin-left: 0;
}
/* 列自身的 15px padding 保留:它是"两列之间的间隙"来源,
归零会让正文和侧栏贴在一起。这里显式声明是为了说明意图 ——
不管 Bootstrap 以后怎么改,这一条的语义是明确的。 */
.k-main .row > [class*="col-"] {
padding-right: 15px;
padding-left: 15px;
}
/* 🔴 导航里的 container 要单独补偿(实测才发现)
*
* `.k-nav` 同时带着 Bootstrap 的 `.navbar` 类,而 `.navbar` 有
* `padding-right/left: 16px`。所以导航里的 container 是嵌在**已经内缩
* 16px** 的盒子里的:
*
* 视口 1440:正文 container 宽 1368 左 36
* 导航 container 宽 1336 左 52 ← 比正文窄 32、右移 16
*
* 改之前两者都是 Bootstrap 的 1280px 固定档位、都被居中,宽度一致,
* 这个 16px 内缩只表现为"导航比正文稍微靠里一点",不明显。
* 改成流式后上限提高、位置随视口走,16px 的相对偏差就突出了,
* 导航栏的 logo/菜单会与正文左边缘对不齐。
*
* 解法见下方规则体内注释(用同一套 width 公式 + **margin:auto 居中**;
* 我最初的两版负 margin 方案都是错的,原因记在下面)。
* ⚠️ 不能给 `.k-nav` 直接 `padding:0` —— 那是 Bootstrap 给所有 navbar 的
* 统一内边距,小屏折叠态下菜单面板依赖它,去掉会贴边。 */
.k-nav > .container,
.k-nav .navbar > .container {
/* 导航里的 container 要和正文**逐像素对齐**。
*
* 难点:`.k-nav` 同时带 Bootstrap 的 `.navbar`,而它左右各有 16px padding。
* 直接用 `width: min(1440px, 100vw - 边距)` 会得到正确**宽度**,但
* 位置差 16px(父级内缩没被算进去)。
*
* 正确做法:用**与正文完全相同**的公式算宽度,然后**不要**加负 margin,
* 让 `margin: auto` 在这 16px 内缩的盒子里居中。数学上刚好抵消:
*
* 视口 1440:navbar 内容框 1408,容器 1368,auto ⇒ 左 = 16+(1408-1368)/2 = 36 ✅
* 视口 1920:navbar 内容框 1888,容器 1440,auto ⇒ 左 = 16+(1888-1440)/2 = 240 ✅
*
* 🔴 前两版都错在这里,值得记下:
* ① 用 `100%` + `calc(… + 32px)` + 负 margin ⇒ `100%` 在绝对定位的
* `.k-nav` 里解析成**视口宽**,结果满宽 320@0;
* ② 用 `100vw` 公式 + `margin-left/right: -16px` ⇒ 宽度对了,但负 margin
* 把容器顶到父级 padding **之外**,左 = 0 而不是 36。
* 负 margin 是"抵消父级内缩"的思路,但抵消之后就不再居中;
* 真正要的是"在已内缩的盒子里按比例居中"。 */
width: min(1440px, 100vw - 2 * max(16px, 2.5vw));
margin-right: auto;
margin-left: auto;
}
/* ── 引用块:首尾引号替代内缩,让引用文字与正文段落对齐(4.0.0 / 工单 #32)
*
* 【用户原话】"页面如果有引用的文字,会有一个色块,然后这个色块左侧有一定的宽度,
* 这样这个引用块里面的文字、段落也缩进了,这样就和主要段落缩进不同了。需要解决。
* 我的建议是,色块保留,然后在引用的开始和结束,增加两个引号,这两个引号,
* 代替引用文本的缩进,这样让引用文本的缩进和段落缩进基本保持一致。"
*
* ── 改前的实测(1440 视口,线上真实文章)────────────────────────
* 正文段落 p: left=67 text-indent=32px ⇒ 文字起点 99
* blockquote: left=67 border-l=6 padding-l=16 ⇒ 框内沿 89
* 块内 p: left=89 text-indent=32px ⇒ 文字起点 121
* ⇒ 引用文字比正文文字**靠右 22px**,且块内段落又叠了一层 32px 首行缩进。
*
* ── 修法:左侧内缩凑够 2em,块内段落首行不再缩进 ──────────────
* border-left 6 + padding-left 26 = 32 = 正文段落的 text-indent:2em
* ⇒ 引用文字起点 == 正文文字起点(实测 0.00px 差)
* 块内 `> p` 的 text-indent 归零,避免"块内缩 + 段首缩进"双层叠加。
* 右侧 padding 保持 kratos 原有的 16px(右边没有对齐要求)。
*
* 🔴 但"32px"只在宽屏成立 —— 正文段落的首行缩进其实**会随视口变**:
* kratos.css:1268 有 `@media screen and (max-width:768px)` 把
* `.content p` 的 font-size 降到 **15px**,于是 `text-indent: 2em = 30px`。
* 而 border 6 + padding-left 26 是写死的 32px ⇒ 窄屏引用文字比正文
* **靠右 2px**(实测 390 / 768 两档都是 2.00px)。
* 这是 pitfalls #470 同族:em 长度会跟着字号静默变化。
*
* 🔴 为什么不能直接写 `padding-left: calc(2em - 6px)`:
* `em` 的基数是**元素自己的 font-size**,而 blockquote 自己写着
* `font-size: 15px`(kratos.css:972,不跟视口变)。
* ⇒ 宽屏窄屏都会算出 15×2−6 = 24px,宽屏反而差 2px。
* em 基数必须与"正文段落"同步才能联动,而正文段落的字号是靠
* @media 跳变的 —— 所以这里只能**在与源规则同一个断点上覆盖**。
*
* 🔴 为什么不用 `font-size: inherit` 让 blockquote 跟着正文变(那样 em 就自动对齐):
* 那会把引用文字从 15px 抬到 16px,属于用户没要求的视觉改动。
* 本版只解决"缩进对齐",字号/色块/引号一律不动。
*
* ⚠️ 下面那条 `@media (max-width: 768px)` 的断点值必须与
* kratos.css:1253 严格一致。两者一旦脱钩,窄屏会重新错 2px。
* `_geom32.js` 的 B1 逐档断言对齐 ≤1.5px,脱钩会立刻红。
*
* ── 引号:左绝对定位、右 inline-block 零高 ──────────────────────
* 🔴 左引号**不能**用 `blockquote::before` 的 inline 形态:
* blockquote 的第一个子级是 **block 级 `<p>`**,行内伪元素会被挤成一个
* **独立的匿名块行**(实测 `Range.getClientRects()` 多出一条 width=0 的孤立行,
* 引号跑到段落上方单独一行)。所以必须 `position:absolute` 悬浮在
* padding 区,不参与文字流。
* 🔴 右引号若用普通 inline,会把行盒撑高 —— 实测块高 89 → **121px(+32)**。
* 这是 #467 同族的坑(行内盒的 line-height 参与行盒高度计算)。
* 解法:`display:inline-block; line-height:0; height:0; overflow:visible`
* —— 行盒贡献为 0(实测块高严格回到 89px,Δ=0.0000),字形靠 overflow 照常绘制。
* 这样它仍**在文字流里**,天然紧贴末字之后,不会随行宽漂移。
* ⚠️ 右引号挂 `> p:last-of-type` 而不是 `blockquote::after`:后者同样会另起
* 匿名块行;而且挂在 p 上才能贴着**最后一行文字的末尾**。
* ⚠️ 用 `:last-of-type` 而非 `:last-child`:引用块常带 `<cite>`(出处署名),
* `:last-child` 会落到 cite 上。
*
* ── 为什么左引号能"代替缩进" ────────────────────────────────
* 32px 的空白由 `padding-left` 提供,引号**画在这 32px 里**,
* 视觉上就是"引号占住了缩进的位置",而文字起点与正文段落齐平。
*
* 🔴🔴 但这句话一开始**并不成立** —— 差分实测(`_bqdiff.js` + pixel diff):
* `left: 6px`、伪元素盒左沿在元素内 x=12,
* 可引号**墨迹**却在 x=30.5~40,而首字墨迹起点是 32.5。
* ⇒ 引号整个压在正文文字上,缩进空白里空空如也。dir="ltr" 下完全看不出来。
*
* 根因:中文 UI 字体下 U+201C 走的是 **CJK 全角字形**,字身宽 1em,
* **左侧 side bearing ≈ 0.62em**。30px 字号 ⇒ 空白 18.5px,
* 盒左沿 12 + 18.5 = 30.5,正好压到首字。
* 这是"量盒 ≠ 量墨迹"的典型(#419 家族):盒对了,字形没画在那儿。
*
* 解法:给引号指定**拉丁衬线字体**,U+201C/U+201D 改走拉丁字形,
* side bearing 降到 ≈0.03em,left 就能用正常小正值。
* 顺便字形也更像"书刊引号",做装饰更合适。
*
* 最终参数(差分实测标定,见下表),元素内 CSS 坐标:
* | 目标 | 要求 | 实测(font 60px / left 2 / top 15)|
* | 落在缩进空白 (6,32) | 墨迹 x ∈ [8,30] | 10.0 ~ 29.5 ✅ |
* | 不压首字(32.5) | 墨迹右沿 < 31 | 29.5 ✅ |
* | 覆盖首行文字 | y 覆盖 24.5~39.5| 22.0 ~ 37.5 (略高 2.5px,视觉更自然)|
* | 比正文汉字更大 | 墨迹高 > 15 | 16.0 ✅(正文汉字 15.0)|
* 扫过的对照(都落选了,留档免得有人再试):
* · font-size 30 + CJK 字形:墨迹只有 9.5×7 —— 比正文还小,不是"放大"
* · font-size 30 + left -31:墨迹能落进空白,但要写负 left,且字号太小
* · font-size 44/52 + 拉丁字形:墨迹 15×11.5 / 18×13.5,仍小于正文汉字高
*
* ── 颜色/字号(用户选定"放大且淡色,做装饰")────────────────────
* 60px(引用文字 15px 的 4 倍;墨迹实际 20×16,比正文汉字略大,
* 因为是淡色描边感,视觉上不会喧宾夺主)
* #c6d2dd —— 比引用文字 #819198 更淡,且与左侧边框 #dce6f0 同色系,
* 保证"是装饰不是内容"。 */
.k-main .details .article .content blockquote {
/* border 6 + padding 26 = 32 = 正文段落 text-indent:2em(16px×2)
⚠️ 窄屏正文变 15px ⇒ 2em = 30 ⇒ 由**文件末尾**的 @media 改回 24
(必须放末尾!放这里会被后面的 ::before 基础规则压掉,见那条注释) */
padding-left: 26px;
/* 左引号绝对定位的包含块 */
position: relative;
}
/* 块内段落首行不再缩进 —— 否则 "块内缩 32" + "段首缩进 32" 双层叠加 */
.k-main .details .article .content blockquote > p {
text-indent: 0;
}
/* 左引号:悬浮在 padding 区,不参与文字流(原因见上) */
.k-main .details .article .content blockquote::before {
content: "\201C";
position: absolute;
/* 🔴 left/top 是差分标定出来的,不是推算的(推算差 18.5px,见上方长注释) */
left: 2px;
top: 15px;
font-size: 60px;
/* 🔴 必须用拉丁字体:CJK 全角字形左侧空白 0.62em,会把墨迹推到文字上 */
font-family: Georgia, "Times New Roman", serif;
line-height: 1;
color: #c6d2dd;
font-weight: 400;
font-style: normal;
/* 纯装饰,别让它挡住文字选中/点击 */
pointer-events: none;
/* 不被暗色规则里的 color 继承干扰 */
-webkit-user-select: none;
user-select: none;
}
/* 右引号:inline-block 零行盒高度,紧贴末字之后但不撑高 */
.k-main .details .article .content blockquote > p:last-of-type::after {
content: "\201D";
display: inline-block;
/* 🔴 这两行是不撑高的关键:行盒贡献 0,字形靠 overflow 溢出绘制 */
line-height: 0;
height: 0;
overflow: visible;
font-size: 56px;
/* 与左引号同一字体(同一个字形族才对得上) */
font-family: Georgia, "Times New Roman", serif;
color: #c6d2dd;
font-weight: 400;
font-style: normal;
/* 往下压一点,让引号底部与文字基线视觉齐平(实测 y 41~55.5,紧贴末字下沿) */
vertical-align: -0.28em;
margin-left: 0.04em;
pointer-events: none;
-webkit-user-select: none;
user-select: none;
}
/* 引用块里的出处署名(<cite>):不参与引号,也不吃首行缩进 */
.k-main .details .article .content blockquote cite {
text-indent: 0;
display: block;
font-style: normal;
margin-top: 8px;
}
/* ── 窄屏(≤768px)对齐修正 ──────────────────────────────────────
* kratos.css:1253 `@media screen and (max-width:768px)` 把 `.content p`
* 的 font-size 降到 15px ⇒ 正文 text-indent:2em = 30px。
* 这里把 padding-left 从 26 调到 24,凑回 border 6 + 24 = 30。
* 🔴 断点值 768 必须与 kratos.css:1253 严格一致,脱钩就重新错 2px。
*
* 🔴🔴 这个 @media 块**必须放在所有 blockquote 基础规则之后**。
* 第一版放在 `blockquote::before` 基础规则**之前**,两者特异性相同 ⇒
* 后出现的 `left: 2px` 把这里的 `left: 0` 压掉 ⇒
* 窄屏下引号墨迹右沿 30.0 正好顶到首字 30.0,压字。
* CSS 层判据全绿(它只能看到"@media 里确实写了 -2px"),
* 只有像素差分能发现。这是"同名选择器散在多处、后出现的赢"
* (pitfalls #437 / #472 同族)。
*
* 窄屏左侧空白由 32 缩到 30,引号墨迹宽 20,放不下 ⇒ left 再由 2 减 2。 */
@media screen and (max-width: 768px) {
.k-main .details .article .content blockquote {
padding-left: 24px;
}
.k-main .details .article .content blockquote::before {
left: 0;
}
}
/* ── 暗色适配 ─────────────────────────────────────────────────────
* 暗色由 `<html class="yang-dark">` 触发(本文件 321 行、toc.css 512 行等处
* 都是这个写法)。
* 暗底上 #c6d2dd 会偏亮、抢注意力,降到更暗的蓝灰。 */
html.yang-dark .k-main .details .article .content blockquote::before,
html.yang-dark .k-main .details .article .content blockquote > p:last-of-type::after {
color: #3d4b58;
}
/* 暗色下引用块本身的底色也要跟上(原来没有这条,浅蓝底在暗色页上刺眼) */
html.yang-dark .k-main .details .article .content blockquote {
background: #1c2026;
border-left-color: #2f3944;
}