余白:文章详情按 innei.in 重排题头与正文 + 顶栏宽度统一 + 纸面留白收紧

参考 innei.in 的文章页(它本身也用 yohaku 命名,把它的 CSS 拆出来对了值)

题头居中
- 元信息行 justify-content:center + tabular-nums(日期/字数等宽,换行不左右跳),
  标题 text-align:center + text-wrap:balance(防居中标题折出「最后一行只落一两个字」);
  正文仍回到左对齐,和参考一致
- 这几条都改成 > 直接子元素:原来的 .paper h1 会连正文里的 h1 一起命中
  (有的文章正文就是从 `# 标题` 开始的),把正文标题也居中了——手机截图上一眼看到

正文
- 段距 0 0 1.35em -> 上下各 1.5em(参考 .v10dui0 .rich-paragraph),首末元素吃掉外边距
- 补设计稿漏掉的 h1 / h3 / ol / img / figcaption(h1 之前是浏览器默认 2em+.67em 外边距、
  h3 是 1.17em,在 16.5px/1.95 的正文里很突兀;插图之前顶满整列且和文字紧贴)
- 链接改用 text-decoration(1px、underline-offset 2px、hover 加粗 2px)替掉 border-bottom
- 分隔线收成 60px 居中细线;.prose 加 text-autospace:normal(中英混排自动字距)

把 classic 的 .prose 挡回去
- styles.css 那段 .prose 不带 data-ui 前缀,vivid 也照样命中;我们的规则特指度更高,
  但只覆盖自己声明过的属性,没声明的就漏过来。量出来并逐条认领:
  a 的 border-bottom(和新下划线叠成双线)、hr 的 height+background(细线被垫成 2px)、
  h3::after(每条 h3 下方多一条 28×2 强调色横杠)、ul/ol 的 1.1em 下边距、
  blockquote 末段 p、pre 的 1px 边框、h4(用 classic 的 16.5px 不成层级)
- 首字下沉也在这批里:设计稿没有,是 classic 漏过来的固定 52px,参考站余白模式也有,
  于是认领成自己的 2.4em(跟着正文字号缩放),短文按 classic 的分法关掉

顶栏宽度
- 撤掉上一版按 /post/* 放宽到 1340 的 .top-in.wide:从首页点进文章时导航会跳一下。
  顶栏全站固定 1100,品牌与主题键在首页和文章页落在同一 x(实测 199 / 1207)

纸面与栅格收紧
- .paper padding 44/48/52 -> 30/24/40;--v-measure 680 -> 728;中列仍 776 = 728 + 24×2
  (这三个数是一组,只改内边距会让正文在列里偏左:实测离左栏 83px、离右栏 131px)
- .article-layout max-width 1340 -> 1272、padding 36/28/60 -> 24/24/44、gap 32 -> 24。
  上限必须正好等于「三栏 + 两个 gap + 左右内边距」,声明的 gap 才是渲染出来的 gap:
  180+776+220+24×2 = 1224 内容宽,+48 = 1272。否则 space-between 把富余塞回列间隙,
  数字改小也不动(实测只改 pad/gap 数字,正文到侧栏反而 78 -> 86)
- 手机 .article-layout padding 24/20/48 -> 20/20/40

实测(用列的实际位置反推,不信任声明值;正文宽度用 offsetWidth,.paper 有入场动画的 scale)
- 1456:正文到侧栏 78 -> 48,正文 728 不变,布局 1340 -> 1272
- 1280:正文 697 -> 721;1200:617 -> 641;三档实际列间隙都等于声明的 24
- 公网 138.2.17.73:8080 产物 md5 与磁盘一致;npm run build 无警告
This commit is contained in:
Sakurasan
2026-09-24 01:03:59 +08:00
parent 565b5be3d7
commit 8fd8674910
6 changed files with 106 additions and 39 deletions
+1 -1
View File
@@ -165,7 +165,7 @@ function onTocClick(e) {
<!-- 短文不渲染标题:正文第一句就是全部,再给个截断标题是重复 -->
<h1 v-if="isShort" class="sr-only">{{ title }}</h1>
<div class="prose" v-html="sanitizeHtml(props.contentHtml)" />
<div class="prose" :class="{ 'prose-note': isShort }" v-html="sanitizeHtml(props.contentHtml)" />
<div class="end">
<RouterLink v-for="t in props.post.tags || []" :key="t" class="tg" to="/tags">{{ t }}</RouterLink>
+2 -6
View File
@@ -1,5 +1,5 @@
<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { RouterLink, useRoute } from 'vue-router'
import { site } from '../../site'
@@ -31,10 +31,6 @@ function isOn(l) {
return l.to === '/' ? route.path === '/' : route.path.startsWith(l.to)
}
// 阅读页的三栏栅格在宽屏是 1340(见 article.css),顶栏跟着放宽才对齐。
// 其余页面仍是 1100 —— 首页信息流卡片已经够宽,跟着放到 1340 只会让封面拉成条。
const wide = computed(() => route.path.startsWith('/post/'))
function onScroll() {
scrolled.value = (window.scrollY || 0) > 8
}
@@ -89,7 +85,7 @@ function toggleTheme() {
<template>
<header class="top" :class="{ scrolled }">
<div class="top-in" :class="{ wide }">
<div class="top-in">
<RouterLink class="brand" to="/">
<span class="av">{{ initial() }}</span>
<span>{{ title() }}</span>
+98 -24
View File
@@ -1,20 +1,20 @@
html[data-ui='vivid'] .article-layout {max-width:1100px;margin:0 auto;padding:36px 28px 60px;
html[data-ui='vivid'] .article-layout {max-width:1100px;margin:0 auto;padding:24px 24px 44px;
display:grid;
grid-template-columns:140px minmax(0,1fr) 180px;
gap:32px;align-items:start;}
gap:24px;align-items:start;}
@media (max-width:960px) {
html[data-ui='vivid'] .article-layout {grid-template-columns:1fr;gap:0}
html[data-ui='vivid'] .side-l, html[data-ui='vivid'] .side-r {display:none}
html[data-ui='vivid'] .progress-pct {display:none}
}
/* 宽屏:三栏往外扩。1100 的上限在 1456 的屏上两侧各空 171px,而且中间那列只有
650px(正文 554px,根本没到 --measure 的 680),所以「文章太窄」和「留白太大」
是同一个原因。这里把上限提到 1340,中列固定在 776 = 680(正文) + 48×2(.paper 内边距),
正文正好吃满 --measure 而不留内空;侧栏 180/220 把多出来的宽度吸收掉,
space-between 让时间线左缘 / 目录右缘正好落在 28px 内边距上(和顶栏对齐)。
顶栏在阅读页同步放宽到 1340,见 chrome.css 的 .top-in.wide。 */
/* 宽屏三栏。上限必须和「三栏 + 两个 gap + 左右内边距」正好相等,声明的 gap 才是
渲染出来的 gap —— 否则 justify-content:space-between 会把富余量塞回列间隙里,
数字改小也不动:
180 + 776 + 220 + 24×2(gap) = 1224 内容宽,+ 24×2(内边距) = 1272
中列 776 = 728(正文, --measure) + 24×2(.paper 内边距),正文吃满不留内空。
顶栏不跟着放 —— 顶栏全站一个宽度(1100),从首页点进文章时导航不会跳。 */
@media (min-width:1200px) {
html[data-ui='vivid'] .article-layout {max-width:1340px;
html[data-ui='vivid'] .article-layout {max-width:1272px;
grid-template-columns:180px minmax(0,776px) 220px;
justify-content:space-between;}
}
@@ -32,30 +32,59 @@ html[data-ui='vivid'] .tl-item.now::before {background:var(--accent);
html[data-ui='vivid'] .tl-item b {display:block;color:var(--soft);font-weight:700;font-size:12px}
html[data-ui='vivid'] .tl-item.now b {color:var(--accent)}
/* 纸张不再是卡片:去掉底色 / 边框 / 圆角 / 阴影,正文直接落在页面上(手机和桌面一致)。
48px 横向内边距留着 —— 现在它只是留白,但它决定了正文列在中列里的位置:
776(中列) - 96 = 680 = --measure,正好吃满,卡片里和列里都不会剩空。
横向内边距 24px(原来 48)——它决定了正文列在中列里的位置:776 - 24×2 = 728 = --measure,
正好吃满不剩空。这三个数是一组,改一个另两个要跟着动,否则正文会在列里偏左
(实测:只把内边距改成 24、measure 不动,正文离左栏 83px、离右栏 131px,一边宽一边窄)。
右上角那团强调色渐晕是卡片的角饰,卡片没了也一并去掉。 */
html[data-ui='vivid'] .paper {padding:44px 48px 52px;
html[data-ui='vivid'] .paper {padding:30px 24px 40px;
animation:paperIn .7s var(--spring) both;}
@keyframes paperIn {
from {opacity:0;transform:translateY(24px) scale(.985)}
to {opacity:1;transform:none}
}
html[data-ui='vivid'] .paper .meta {display:flex;gap:12px;align-items:center;flex-wrap:wrap;
font-size:12.5px;color:var(--muted);font-weight:600;margin-bottom:18px;}
html[data-ui='vivid'] .paper h1 {margin:0 0 20px;font-size:clamp(28px,4vw,40px);font-weight:800;
letter-spacing:-.03em;line-height:1.25;color:var(--ink);}
html[data-ui='vivid'] .paper .dek {margin:0 0 32px;font-size:17px;line-height:1.8;color:var(--soft);
/* 题头居中(参考 innei.in 的文章页:题头整块居中,正文才回到左对齐)。
tabular-nums 让日期/字数这类数字等宽,换行时也不会左右跳。
注意都用 > 直接子元素:.paper h1 会连正文里的 h1(正文本身可能从 `# 标题` 开始)
一起命中,把正文标题也居中。 */
html[data-ui='vivid'] .paper > .meta {display:flex;gap:12px;align-items:center;flex-wrap:wrap;
justify-content:center;
font-size:12.5px;color:var(--muted);font-weight:600;
font-variant-numeric:tabular-nums;
margin-bottom:18px;}
html[data-ui='vivid'] .paper > h1 {margin:0 0 20px;font-size:clamp(28px,4vw,40px);font-weight:800;
letter-spacing:-.03em;line-height:1.25;color:var(--ink);
text-align:center;
/* 居中标题容易折出「最后一行只落一两个字」,balance 让两行长度接近(Chrome/Safari) */
text-wrap:balance;}
html[data-ui='vivid'] .paper > .dek {margin:0 0 32px;font-size:17px;line-height:1.8;color:var(--soft);
padding-left:16px;border-left:3px solid var(--accent);
font-family:var(--serif);}
html[data-ui='vivid'] .prose {font-size:16.5px;line-height:1.95;color:var(--text);max-width:var(--measure)}
html[data-ui='vivid'] .prose p {margin:0 0 1.35em}
html[data-ui='vivid'] .prose {font-size:16.5px;line-height:1.95;color:var(--text);max-width:var(--measure);
/* 中英混排自动补一点字距(Chrome 120+ 的 text-autospace)。不支持就退回现在的样子。 */
text-autospace:normal;}
/* 段距上下各 1.5em(参考 .v10dui0 .rich-paragraph),首末段把外边距吃掉,
否则标题/分隔线上方会多出一段空白 */
html[data-ui='vivid'] .prose p {margin:1.5em 0}
html[data-ui='vivid'] .prose > :first-child {margin-top:0}
html[data-ui='vivid'] .prose > :last-child {margin-bottom:0}
html[data-ui='vivid'] .prose h2 {font-size:21px;font-weight:800;margin:2.2em 0 .8em;
letter-spacing:-.02em;color:var(--ink);
padding-bottom:10px;border-bottom:1px solid var(--line);}
html[data-ui='vivid'] .prose a {color:var(--accent);font-weight:600;border-bottom:1.5px solid var(--accent-soft);transition:border-color .25s}
/* 设计稿只写了 h2,h1/h3 不补样式会退回浏览器默认(h1 是 2em + .67em 外边距、
h3 是 1.17em + 自己的行高),在 16.5px/1.95 的正文里很突兀。
正文里出现 h1 是因为有的文章正文本身就是从 `# 标题` 开始的。 */
html[data-ui='vivid'] .prose h1 {margin:2.4em 0 .8em;font-size:1.5em;font-weight:800;
letter-spacing:-.02em;color:var(--ink);}
html[data-ui='vivid'] .prose h3 {margin:1.65em 0 .65em;font-size:1.18em;font-weight:700;
letter-spacing:-.01em;color:var(--ink);}
/* 链接改用 text-decoration:border-bottom 会进盒模型,换行时下划线也跟着断得不对。
参考里是 1px、hover 加粗到 2px。 */
html[data-ui='vivid'] .prose a {color:var(--accent);font-weight:600;
text-decoration:underline;text-decoration-thickness:1px;
text-underline-offset:2px;text-decoration-color:var(--accent-soft);
transition:text-decoration-color .25s,text-decoration-thickness .25s;}
@media (hover: hover) and (pointer: fine) {
html[data-ui='vivid'] .prose a:hover {border-bottom-color:var(--accent)}
html[data-ui='vivid'] .prose a:hover {text-decoration-color:var(--accent);text-decoration-thickness:2px}
}
html[data-ui='vivid'] .prose code {font-family:var(--mono);font-size:.88em;
background:var(--accent-soft);color:var(--accent);
@@ -72,15 +101,60 @@ html[data-ui='vivid'] .prose blockquote {margin:1.8em 0;padding:16px 20px;
font-family:var(--serif);font-size:1.05em;
color:var(--soft);font-style:italic;}
html[data-ui='vivid'] .prose ul {padding-left:1.3em}
/* 设计稿漏了 ol,默认 40px 缩进和 ul 对不上;序号比圆点宽,给多一点 */
html[data-ui='vivid'] .prose ol {padding-left:1.6em}
html[data-ui='vivid'] .prose li {margin-bottom:.55em}
html[data-ui='vivid'] .prose hr {border:0;border-top:1px dashed var(--line-strong);margin:2.5em 0}
/* 分隔线收成 60px 居中(参考),比整幅虚线安静 */
html[data-ui='vivid'] .prose hr {border:0;border-top:1px solid var(--line-strong);
width:60px;margin:3.5em auto;}
/* 图:设计稿没写,插图会顶满整列、和上下文字紧贴 */
html[data-ui='vivid'] .prose img {max-width:100%;height:auto;border-radius:var(--radius-sm)}
html[data-ui='vivid'] .prose p > img {margin:2em 0}
html[data-ui='vivid'] .prose figure {margin:2em 0}
html[data-ui='vivid'] .prose figcaption {margin-top:1em;font-size:.875em;line-height:1.5;
color:var(--muted);text-align:center;}
/* 标签用 home.css 里那个共用的 .tg(纯文本 #标签),不加自己的一套。
「标签」二字的小标也去掉了:有 # 就是标签,再来两个字是重复。
这里只补一点纵向内边距 —— 详情页的标签是链接,12.5px 裸文字只有 ~18px 高,
手机上不好点;不画背景不画边框,观感不变。 */
html[data-ui='vivid'] .paper .end {margin-top:40px;padding-top:24px;border-top:1px dashed var(--line-strong);
html[data-ui='vivid'] .paper > .end {margin-top:40px;padding-top:24px;border-top:1px dashed var(--line-strong);
display:flex;gap:14px;flex-wrap:wrap;align-items:center;}
html[data-ui='vivid'] .paper .end .tg {padding:6px 0}
html[data-ui='vivid'] .paper > .end .tg {padding:6px 0}
/* ==========================================================================
把 classic 的 .prose 挡回去
--------------------------------------------------------------------------
styles.css 里那一段 .prose 规则是不带 data-ui 前缀的,vivid 也照样命中。
我们的规则特指度更高,但只覆盖自己声明过的属性 —— 没声明的就漏过来了。
实测漏过来的是这几条(都是量出来的,不是猜的):
· a 的 border-bottom → 和新的 text-decoration 叠成双下划线
· hr 的 height+background → 我们的 60px 细线被垫成 2px、颜色也变成 --line
· h3::after → 每条 h3 下面多一条 28×2px 的强调色横杠
· ul/ol 的 1.1em 下边距 → 和正文 1.5em 的段距对不齐
· blockquote 里末段 p → 下边距没被吃掉,引用块底部多半行空
· pre 的 1px 边框 → 深色代码块外多一圈发丝线
· h4 → 用 classic 的 16.5px(和正文字号一样大,不成层级)
下面逐条认领。tokens.css 里那段防线处理的是 .stat/.toolbar/.chip 那类组件外壳,
正文这一摊放在这里,改正文的人一眼能看到。
========================================================================== */
html[data-ui='vivid'] .prose a {border-bottom:0}
html[data-ui='vivid'] .prose hr {height:0;background:none}
html[data-ui='vivid'] .prose h3::after {content:none}
html[data-ui='vivid'] .prose ul,
html[data-ui='vivid'] .prose ol {margin:1.5em 0}
html[data-ui='vivid'] .prose blockquote p:last-child {margin-bottom:0}
html[data-ui='vivid'] .prose pre {border:0}
html[data-ui='vivid'] .prose h4 {margin:1.4em 0 .5em;font-size:1.05em;font-weight:700;
letter-spacing:-.01em;color:var(--ink);}
/* 首字下沉:设计稿里没有这条,classic 漏过来的是固定 52px。参考站(innei.in 余白模式)
也有首字下沉,那就认领成自己的,按 2.4em 走、跟着正文字号缩放。 */
html[data-ui='vivid'] .prose > p:first-of-type::first-letter {float:left;
font-family:var(--serif);font-size:2.4em;line-height:1;
margin:.06em .18em 0 0;color:var(--accent);}
/* 短文不做首字下沉:正文第一句就是全部,压一个大字进去反而像截断
(classic 也是这么分的,它用 .prose-short 关掉) */
html[data-ui='vivid'] .prose.prose-note > p:first-of-type::first-letter {float:none;
font-family:inherit;font-size:inherit;line-height:inherit;margin:0;color:inherit;}
html[data-ui='vivid'] .back {display:inline-flex;align-items:center;gap:6px;
font-size:13px;font-weight:700;color:var(--accent);
margin-bottom:20px;padding:6px 0;
-5
View File
@@ -9,11 +9,6 @@ html[data-ui='vivid'] .top.scrolled {border-bottom-color:var(--line);
html[data-ui='vivid'] .top-in {max-width:1100px;margin:0 auto;
padding:0 28px;height:60px;
display:flex;align-items:center;gap:20px;}
/* 阅读页(/post/*)在宽屏用的是 1340 的三栏栅格(article.css),顶栏得跟着一起宽:
否则品牌和主题键会落在 1100 的边上,比左栏时间线 / 右栏目录缩进一百多像素。 */
@media (min-width:1200px) {
html[data-ui='vivid'] .top-in.wide {max-width:1340px}
}
html[data-ui='vivid'] .brand {display:flex;align-items:center;gap:10px;
font-weight:700;font-size:15px;letter-spacing:.02em;
transition:transform .5s var(--spring);}
+2 -2
View File
@@ -15,9 +15,9 @@
html[data-ui='vivid'] .hero {padding:48px 20px 32px}
html[data-ui='vivid'] .wrap {padding:8px 20px 36px}
/* 阅读页在手机上只收回留白:.article-layout 走 20px 页边距,.paper 的内边距归零
(桌面那 48px 是为了给 776 的中列凑出 --measure 的 680,手机上只会浪费宽度)。
(桌面那 24px 是为了给 776 的中列凑出 --measure 的 728,手机上只会浪费宽度)。
卡片样式本身已在 article.css 里去掉了,手机桌面一致。 */
html[data-ui='vivid'] .article-layout {padding:24px 20px 48px}
html[data-ui='vivid'] .article-layout {padding:20px 20px 40px}
html[data-ui='vivid'] .paper {padding:0}
html[data-ui='vivid'] .about-card {padding:32px 22px}
/* showcase 的 .showcase 是 max-width + 自己的 padding(不带 .wrap 类),
+3 -1
View File
@@ -21,7 +21,9 @@
--v-radius-lg: 20px;
--v-radius-pill: 999px;
--v-content-width: 1100px;
--v-measure: 680px;
/* 正文行宽。改它要连 article.css 里阅读页中列(minmax(0,776px))和 .paper 的
横向内边距一起改:三者是 776 = 728 + 24×2 的关系,只动一个会让正文在列里偏左。 */
--v-measure: 728px;
--v-nav-h: 64px;
--v-motion: 1; /* 动效强度倍数;置 0 即全部即时生效 */