本项目的全部视觉参数(颜色、阴影、圆角、字体、背景网格)都收敛为 --wp-* 前缀的 CSS 变量,定义在 src/styles/main.css。换肤、换主色、换字体都只需要动这一个文件。

1. 换肤架构(三层)

text
1234567891011121314151617
第 1 层:设计 token(CSS 变量)
  :root, .light { --wp-accent: #e86233; --wp-bg: #f5f4f0; ... }   ← 亮色值(.light 供子树强制亮色)
  .dark { --wp-accent: #db7753; --wp-bg: #141413; ... }     ← 暗色值
        │
        ▼
第 2 层:Tailwind 桥接(@theme inline)
  @theme inline {
    --color-accent: var(--wp-accent);
    --color-paper:  var(--wp-bg);
    ...
  }
  → 自动生成工具类:bg-accent / text-accent / border-line / shadow-card ...
        │
        ▼
第 3 层:组件使用语义类
  <article class="border border-line-light bg-surface shadow-card">
  <a class="hover:text-accent">

由此得到两条实践结论:

想做的事正确做法错误做法
换主色改 :root 和 .dark 里的 --wp-accent 等变量全局替换组件里的 text-accent
加一个新颜色1、加 --wp-xxx;2、在 @theme inline 里加 --color-xxx在组件里写死 #RRGGBB
改圆角改 --radius-wp(部分组件写死了 px,见#8.3 说明)—

2. 换主色:最小改动步骤

场景:把柑橘橙(#e86233)换成蓝紫(#5b6ee1)。这是最常见的换肤需求,共用 6 步。

第 1 步:改亮色主色三件套

src/styles/main.css 的 :root 块:

css
123456
:root {
  --wp-accent: #5b6ee1;        /* 原 #e86233 —— 主色,按钮/链接/高亮 */
  --wp-accent-hover: #7484e8;  /* 原 #ea7b53 —— hover 亮一档 */
  --wp-accent-deep: #3f4fb0;   /* 原 #b6502b —— 深一档,用于链接文字 */
  /* --wp-link / --wp-link-hover 默认引用上面两个变量,无需改 */
}

第 2 步:改暗色主色三件套

.dark 块(暗色下要降低饱和度并提亮,否则刺眼):

css
12345
.dark {
  --wp-accent: #8b9bf0;        /* 原 #db7753 */
  --wp-accent-hover: #a3b0f4;  /* 原 #e28969 */
  --wp-accent-deep: #7286e8;   /* 原 #c86541 */
}

第 3 步:改背景网格线颜色(最容易被忘的一步)

--wp-grid-image 是一段内联 SVG data URI,线条颜色硬编码在 URL 编码里,不会跟随 --wp-accent:

css
123
/* 亮色:stroke='%23E86233'  ← 注意 # 被编码成 %23 */
--wp-grid-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 28 28'%3E%3Cpath d='M0 0L28 0M0 0L0 28' fill='none' stroke='%23E86233' stroke-opacity='0.14' stroke-width='1'/%3E%3C/svg%3E");
/* 暗色:stroke='%23DB7753',透明度 0.11 */

把两处 %23E86233 / %23DB7753 分别替换成新主色的十六进制(不带 #,前面加 %23)。width='28' height='28' 是网格单元尺寸,改这里要同步改 body 的 background-size: 28px 28px。

第 4 步:检查 --wp-gradient-pink 与品牌色

变量当前值是否随主色
--wp-gradient-pinklinear-gradient(135deg, #8a667e 0%, #b06b78 100%)否,独立品牌渐变(用于 .wp-gradient-bg)
--wp-gold亮 #8a6939 / 暗 #b08d4e否,用于「置顶」角标
ProfileCard.astro 的 platformBrands各平台官方色否,社交按钮专属

第 5 步:AboutPage.astro 主题色(可选)

因为 about 是一个特殊的独立页面,为自定义页面,所以请自行参考修改。

第 6 步:验证

bash
1
pnpm dev   # 切换明暗主题,检查:链接、按钮 hover、网格背景、分页当前页、置顶角标、代码块左侧色条、TOC 激活项

3. --wp-* 变量全表

3.1 背景与表面

变量亮色暗色用途
--wp-bg#f5f4f0#141413页面底色(也是 Tailwind paper)
--wp-surface#ffffff#1b1a18卡片 / 面板表面
--wp-surface-hover#f4eae2#23221f悬停底色、图片占位底
--wp-code-bg#ece7dc#0e0d0c代码块背景、表头背景
--wp-header-bgrgba(245,244,240,0.65)rgba(20,20,19,0.65)吸顶头部背景(配合 backdrop-filter: blur(3px))
--wp-glass-bgrgba(245,244,240,0.15)rgba(20,20,19,0.55).wp-glass 玻璃面板(<main>、页脚)
--wp-grid-imageSVG data URI,stroke-opacity: 0.14同结构,0.11方格草稿纸背景

3.2 主色与链接

变量亮色暗色用途
--wp-accent#e86233#db7753主色:强调文字、按钮、边框高亮、进度条
--wp-accent-hover#ea7b53#e28969主色 hover
--wp-accent-deep#b6502b#c86541主色加深(正文链接 --wp-link 等)
--wp-linkvar(--wp-accent-deep)var(--wp-accent)正文链接颜色
--wp-link-hovervar(--wp-accent)var(--wp-accent-hover)链接 hover

注意亮/暗的链接策略不同:亮色模式链接用「深一档」保证对比度,暗色模式直接用主色(暗底上深色不可读)。改 --wp-link 时请维持这个思路。

3.3 文字

变量亮色暗色用途Tailwind 类
--wp-text#322e2a#e7e2d9正文主色text-ink
--wp-text-secondary#625b52#9c948a次级文字(摘要、引用块)text-ink-2
--wp-text-tertiary#6b645a#90887b三级文字(日期、元信息、占位)text-ink-3

3.4 边框与阴影

变量亮色暗色用途
--wp-border#e0d9cb#2b2924常规边框、表格线、滚动条滑块
--wp-border-light#e9e2d5#23211e更浅的分隔线(卡片边缘、页脚上边线)
--wp-shadow-card0 1px 3px rgba(0,0,0,.04), 0 1px 2px rgba(0,0,0,.02)0 1px 3px rgba(0,0,0,.3), 0 1px 2px rgba(0,0,0,.2)卡片默认阴影
--wp-shadow-card-hover0 4px 12px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.03)0 4px 12px rgba(0,0,0,.4), 0 2px 4px rgba(0,0,0,.25)卡片 hover 阴影

3.5 语义色

变量亮色暗色用途
--wp-gold#8a6939#b08d4e「置顶」角标背景(card-badge-left)
--wp-silver#686d75#96989c备用中性色
--wp-safe-blue#4a6e8c#6c8ca4友链空状态插画的眼泪
--wp-life-green#5c7355#7a9173备用
--wp-gradient-pinklinear-gradient(135deg,#8a667e,#b06b78)linear-gradient(135deg,#865f7c,#a96676).wp-gradient-bg 渐变背景

3.6 提示容器标题色

变量亮色暗色对应容器
--wp-info-title#3f6fa3#7fa8ce:::info
--wp-tip-title#52713b#8fb078:::tip
--wp-warn-title#8a650d#d4a94e:::warning
--wp-danger-title#a94f41#d08377:::danger

只有标题文字色是变量,容器左边框色在 markdown.css 里写死:info #6fa8dc、tip #7ba05b、warning #d9a441、danger #c96a5b、details var(--wp-text-tertiary)。想换要同时改两处(见#8.2)。

3.7 代码高亮配色(Prism)

这些是构建期写进 HTML 的 <span class="token ...">,颜色由 CSS 变量在运行时决定,所以改颜色不需要重新构建。

变量亮色暗色作用的 token
--wp-syntax-keyword#984d2a#d49060keyword important atrule rule
--wp-syntax-string#466f37#8fb87astring char attr-value selector
--wp-syntax-comment#69625a#847d72comment prolog cdata(斜体)
--wp-syntax-number#2a7265#6fb8a8number boolean constant symbol
--wp-syntax-function#7b4b2a#c88860function class-name
--wp-syntax-type#7f5061#c99bae(在变量表中保留,CSS 中由 function/class-name 规则覆盖)
--wp-syntax-operator#6b6560#a8a099tag punctuation operator entity url
--wp-syntax-attribute#8f4e18#e2a062attr-name property
--wp-syntax-meta#855656#c9999b备用
--wp-syntax-regexp#a34e31#e08060regex variable
--wp-diff-del#a94442#d48a85token.deleted
--wp-diff-add#376c37#94c086token.inserted

4. Tailwind 桥接表(@theme inline)

main.css 第 94–116 行把这些变量注册为 Tailwind 主题变量,从而生成工具类:

Tailwind 变量映射到生成的常用类
--color-paper--wp-bgbg-paper text-paper
--color-surface--wp-surfacebg-surface
--color-surface-hover--wp-surface-hoverbg-surface-hover hover:bg-surface-hover
--color-code-bg--wp-code-bgbg-code-bg
--color-accent--wp-accentbg-accent text-accent border-accent hover:text-accent
--color-accent-hover--wp-accent-hoverhover:bg-accent-hover
--color-accent-deep--wp-accent-deepbg-accent-deep text-accent-deep
--color-ink--wp-texttext-ink
--color-ink-2--wp-text-secondarytext-ink-2
--color-ink-3--wp-text-tertiarytext-ink-3
--color-line--wp-borderborder-line text-line
--color-line-light--wp-border-lightborder-line-light
--color-gold--wp-goldbg-gold text-gold
--color-silver--wp-silver—
--color-safe-blue--wp-safe-blue—
--color-life-green--wp-life-green—
--font-serifsans-seriffont-serif
--font-monomonospacefont-mono
--radius-wp8pxrounded-wp
--shadow-card--wp-shadow-cardshadow-card
--shadow-card-hover--wp-shadow-card-hovershadow-card-hover

新增一个语义色的完整示例(加一个「成功绿」):

css
12345678910
/* 1) 在 :root 里定义 */
:root  { --wp-success: #3f8f5f; }
.dark  { --wp-success: #7bc39a; }

/* 2) 在 @theme inline 里桥接 */
@theme inline {
  --color-success: var(--wp-success);
}

/* 3) 在组件里使用 */
html
1
<span class="text-success">已保存</span>

5. 字体与排版

5.1 当前字体策略

css
12345678910111213
/* main.css */
@theme inline {
  --font-serif: sans-serif;   /* ← 注意:font-serif 实际也是无衬线 */
  --font-mono: monospace;
}

body {
  font-family: sans-serif;
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

设计取向:零字体网络请求(AboutPage 的「未来计划」中也写明「自定义字体」仍在评估,因为要考虑加载时间)。所以标题用的 font-serif 实际渲染为系统无衬线字体。

5.2 换成自定义字体(需权衡)

引入 web 字体会破坏「零请求」取向!

若确定要换,注意 astro.config.mjs 的 vite.build.assetsInlineLimit = 4096:小于 4KB 的资源会被内联,字体文件通常远大于此,会作为独立文件输出。

推荐把字体文件放 public/fonts/(astro.config.mjs 的 swup ignore 已包含 /fonts/ 前缀,不会走 SPA 导航),然后:

css
12345678910111213
/* main.css 顶部 */
@font-face {
  font-family: 'MySans';
  src: url('/fonts/MySans.woff2') format('woff2');
  font-display: swap;          /* 避免文字长时间不可见 */
  unicode-range: U+0000-00FF;  /* 按需裁剪,减小体积 */
}

@theme inline {
  --font-serif: 'MySans', system-ui, sans-serif;
}

body { font-family: 'MySans', system-ui, sans-serif; }

5.3 常用排版参数调整位置

想改什么改哪里当前值
全局基准字号main.css 的 body { font-size }16px
全局行高main.css 的 body { line-height }1.7
页面内容最大宽度BaseLayout.astro 的 <main class="max-w-[832px]">800px
头部高度SiteHeader.astro 的 h-[60px] max-md:h-[52px]60 / 52px
文章正文字号/行高markdown.css 的 .post-content1.06rem / 1.85
文章标题字号markdown.css 的 .post-content h1..h61.75 / 1.45 / 1.2 / 1.05 / 1 / 0.95 rem
代码块字号/行高markdown.css 的 .code-body 变量--code-font-size: 0.85rem、--code-line-height: 1.7
表格字号markdown.css 的 .post-content table0.95rem

为什么 <main> 是 max-w-[832px] 而内容宽度算 800px?

832px 是玻璃卡片外框的宽度,正文实际可读宽度 = 外框 − 左右内边距。旧写法 max-w-[800px] + px-6(800 − 2×24 = 752),新写法 max-w-[832px] + px-4(832 − 2×16 = 800)。页脚 SiteFooter.astro 同步用 max-w-[832px] + px-4 保持外框对齐。因此若想改内容宽度到 W,应把两处改成 max-w-[W+32px] 且保持 px-4;页面内部的 max-w-[800px](首页列表、文章页头/尾、评论等)则直接用内容宽度 W。

标题的「层级色条」机制:

.post-content :is(h1..h6)::before 用 --i(0–5)计算高度:height: calc(100% - var(--i) * 14%)。所以 h1 的色条最长、h6 最短。想改成等长,删掉 calc() 直接用 100% 即可。

6. 暗色模式实现细节

6.1 切换机制

环节实现
类名document.documentElement.classList.toggle('dark', …)
Tailwind 暗色变体@custom-variant dark (&:where(.dark, .dark *))(main.css 第 4 行)
默认值来源<html data-default-theme>(由 siteConfig.defaultTheme 渲染)
存储 keylocalStorage.themeMode,取值 'light' | 'dark'(无 'auto' 存储值)
'auto' 语义不写 localStorage,实时跟随 prefers-color-scheme
系统变化监听initTheme() 中 media.addEventListener('change', …),仅当 themeMode.get() === 'system' 时应用
切换动画抑制applyTheme 在变化时给 <html> 加 is-theme-transitioning,300ms 后移除;该类用 !important 禁用全站 transition。不禁用 animation:animation-name 被置 none 再恢复会取消并重建动画,导致入口动画(如友链卡片 friend-card-in)在切换结束后重播抖动
触点扩散动画点击 [data-theme-toggle] 且浏览器支持 View Transitions API 时,用 document.startViewTransition 做圆形扩散:新主题快照以按钮圆心 clip-path: circle() 从 0 半径扩散至铺满全屏(500ms,ease-in-out),旧主题快照垫底;布局零变化、无缩放(纯裁切)。移动端(max-width: 768px)改由 main.css 媒体查询接管:新快照 translateY(100%) → 0 自底部划入,旧快照同步淡出,时长与缓动一致。prefers-reduced-motion 或不支持的浏览器回退为瞬时切换。默认 VT 交叉淡入由 main.css 的 ::view-transition-old/new(root) { animation: none } 关闭
同步机制每次切换派发 document 上的 theme-change 事件(detail 为 'light'/'dark'),评论插件据此更新 iframe 主题

移动端与桌面端对于 View Transition API 在实现上存在显著差异,所有做了两套动画方案:

  • 桌面端:圆形扩散
  • 移动端(max-width: 768px):底部划入 + 淡出

6.2 主题状态 API(src/stores/theme.ts)

ts
123456
import { themeMode, resolvedTheme, toggleTheme, initTheme } from '@/stores/theme'

themeMode.get()        // 'light' | 'dark' | 'system'
themeMode.set('dark')  // 写入 localStorage 并立即生效
resolvedTheme.get()    // 'light' | 'dark'('system' 已按系统偏好解析)
toggleTheme()          // 在亮/暗之间切换(写入 localStorage)
导出说明
ThemeMode'light' | 'dark' | 'system' 类型
themeMode{ get(), set(mode) }
resolvedTheme{ get() },把 'system' 解析成实际值
toggleTheme(origin?)快捷切换;传入触发按钮元素时走触点扩散动画(圆心即按钮中心)
initTheme()幂等(内部 initialized 标志);绑定 astro:after-swap 重新应用主题、matchMedia 变化监听、[data-theme-toggle] 点击委托

initTheme() 在 BaseLayout 的 <script> 中调用一次即全局生效。

6.3 写自定义样式时如何兼容暗色

方式一(推荐):只用变量

css
12345
.my-box {
  background: var(--wp-surface);
  border: 1px solid var(--wp-border-light);
  color: var(--wp-text);
}

方式二:需要在暗色下换具体值时

css
12
.my-box { background: rgba(255,255,255,0.35); }
.dark .my-box { background: rgba(255,255,255,0.03); }   /* markdown.css 的 .code-header 就是这么做的 */

方式三:Tailwind 的 dark: 变体

html
1
<span class="text-accent-deep dark:text-accent">CC BY-NC-SA 4.0</span>

7. 全局样式文件与工具类

7.1 CSS 文件清单

文件引入位置职责
styles/main.cssBaseLayout.astro(import '@/styles/main.css')变量定义、Tailwind 引入、@theme 桥接、body 基础排版、滚动条、动画 keyframes、Swup 过渡
styles/markdown.cssmain.css 内 @import "./markdown.css"全部 .post-content 内部元素(标题、代码块、表格、图片、容器、KaTeX、脚注)
styles/PostPage.cssPostPage.astro桌面侧边 TOC 定位与激活态
styles/PostCard.cssPostCard.astro卡片角标(.card-badge 系列)、克隆内 hover 镜像样式
styles/ProfileCard.cssProfileCard.astro个人名片、统计区、下方社交方块条、时钟层与数码管、「唤回宠物」按钮(.pet-restore,仅补一条 [hidden] 规则把 hidden 属性接回来,其余复用 .card-toggle)
styles/main.css 的共享 .card-preview 样式块被 ProfileCard.astro / PostCard.astro 共同使用名片与文章卡共用的预览方窗覆盖层(窗口裁切、--preview-size、is-on 开关、reduced-motion 兜底)
components/Pagination.astro 的内联 <style>Pagination.astro分页按钮(未抽成独立 CSS 文件,是唯一的例外)
styles/ScrollControls.cssScrollControls.astro右下按钮组
styles/comments.cssComments.astro评论骨架屏与状态类
styles/FriendsPage.cssFriendsPage.astro友链卡片、空状态插画动画
styles/music-player.cssMusicPlayer.astro播放器全部样式
styles/Pet.cssPet.astro左下角宠物:--pet-unit 尺寸链(clamp + 视口/边距约束)、贴内容列左侧的 left 计算、与右侧按钮组最低边对齐的 bottom(--pet-bottom)、方格描边轮廓与 will-change;长按气泡(.pet-card 及其 is-open / is-closing、pet-card-in/out)、蹦一下(pet-hop)、消解 / 聚合的方格层(.pet-grid / .pet-cell)与让位规则(is-leaving / pet-assembling / pet-off)也在这里;颜色只取 --wp-accent,随主题自动适配

7.2 main.css 提供的全局工具类

类名定义用途
.mdi-iconvertical-align: middle; line-height: 1所有 Icon 的基线对齐
.wp-glassTailwind 类,配合 --wp-glass-bg<main> 与页脚的玻璃面板
.wp-gradient-bgbackground: var(--wp-gradient-pink); color: #fff品牌渐变块
.wp-overlayanimation: wp-fade-in .18s ease-out弹窗遮罩淡入
.wp-modal-panelanimation: wp-pop-in .2s ease-out弹窗面板上浮
.wp-overlay.wp-closinganimation: wp-fade-out .16s ease-in forwards弹窗遮罩关闭淡出(JS 加类,0.16s 后置 hidden + 移除类)
.wp-modal-panel.wp-closinganimation: wp-pop-out .16s ease-in forwards弹窗面板关闭下沉缩回
.wp-drawer-panel-right / -leftwp-slide-in-right/left .22s抽屉面板(预留)
.wp-spinninganimation: wp-spin .8s linear infinite加载旋转(预留)
html.is-theme-transitioning *仅禁 transition(!important)主题切换瞬间防颜色渐变叠加;不禁 animation,避免动画取消后重播

全局元素规则必须放进 @layer

main.css 里针对裸元素的规则(a / a:hover 的颜色与过渡)统一写在 @layer base { … } 内。原因:Tailwind v4 的工具类都位于 utilities 层,而未分层的规则优先级高于任何层——裸 a { color: … } 一旦漏在层外,就会压掉组件里给锚点写的 text-*(V1.0.9 修对比度时实测踩过这个坑)。

  • 新增全局 CSS 规则一律放进 @layer(默认放 base);这是 R4 的硬约束;
  • 想覆盖 base 层的裸元素颜色:用工具类(如 text-ink-2),或写有意不分层的作用域规则(markdown.css 的 .post-content a 就属于后者,优先级更高,正文链接仍取 --wp-link);
  • 排查手段:DevTools 里若看到 a { color: var(--wp-link) } 压过 .text-ink-2,说明该规则漏在层外了。

7.3 动画 keyframes 一览

keyframe效果使用者
wp-spin360° 旋转.wp-spinning
wp-fade-in透明度 0→1.wp-overlay
wp-pop-intranslateY(8px) scale(.98) → 正常.wp-modal-panel
wp-fade-out透明度 1→0(ease-in 0.16s).wp-overlay.wp-closing
wp-pop-out正常 → translateY(8px) scale(.98) 并淡出.wp-modal-panel.wp-closing
wp-slide-in-right / wp-slide-in-left从左右两侧滑入抽屉面板
img-shimmer背景位置 120% → -120%文章图片 LQIP 骨架(markdown.css)
comment-shimmer同上评论骨架(comments.css)

已有 prefers-reduced-motion 降级的位置:

文件覆盖的动画
markdown.cssimg-shimmer、图片 opacity 过渡
comments.csscomment-shimmer
FriendsPage.css:196友链空状态插画
main.css:250.transition-swup-fade
ScrollControls.css侧边整合菜单弹出层的展开/收起动画(prefers-reduced-motion 时 transition: none)
PostPage.css文章阅读动效(正文块滚动淡入上移):整块包在 @media (prefers-reduced-motion: no-preference) 里,reduced-motion 用户直接常显、无初始隐藏;JS 侧 initReadingReveal() 也有 reduced-motion 提前返回

弹窗关闭动画不靠 CSS 降级,由 SearchModal / PostPage 的 close() 在 prefers-reduced-motion 时直接瞬时隐藏(跳过 0.16s 等待)。动画时长在 CSS 与 JS(setTimeout(done, 160))各有写死,改时长需两处同步。

尚无降级:music-player.css:62 的 music-spin 6s linear infinite(keyframes 定义在 :64),以及 main.css 的 wp-spin / wp-fade-in / wp-pop-in / wp-slide-in-right / wp-slide-in-left。

加新动画时建议补上 prefers-reduced-motion 降级。

7.4 Swup 过渡样式

css
1234
.transition-swup-fade { opacity: 1; will-change: opacity; transition: opacity .32s cubic-bezier(.22, 1, .36, 1); }
html.is-animating .transition-swup-fade { opacity: 0; transition: opacity .16s ease-in; }        /* 淡出:旧内容渐隐 */
html.is-changing:not(.is-animating) .transition-swup-fade { opacity: 1; transition: opacity .35s cubic-bezier(.22, 1, .36, 1); } /* 淡入:新内容渐显 */
body > .swup-progress-bar { top: 0; z-index: 200; background-color: var(--wp-accent); }
想改什么改哪里
换页淡出时长html.is-animating .transition-swup-fade 的 transition(ease-in)
换页淡入时长html.is-changing:not(.is-animating) .transition-swup-fade 的 transition(cubic-bezier)
顶部进度条颜色.swup-progress-bar 的 background-color
关闭动画astro.config.mjs 的 swup({ animationClass: … }) 置空,或删掉 .transition-swup-fade 类

8. 局部样式修改速查

8.1 文章内标题左侧色条

markdown.css:

css
1234567
.post-content :is(h1,h2,h3,h4,h5,h6)::before {
  content: ""; position: absolute; left: 0; top: 50%; translate: 0 -50%;
  width: 4px;                                  /* ← 色条宽度 */
  border-radius: 2px;
  background: var(--wp-accent);                /* ← 色条颜色 */
  height: calc(100% - var(--i) * 14%);         /* ← 层级递减幅度 */
}

同时在 .post-content :is(h1..h6) 上设置了 padding-left: 0.9rem(给色条留位置)与 scroll-margin-top: calc(60px + 1rem)(锚点跳转避开吸顶头部,改头部高度时要同步这里)。

8.2 提示容器(:::info 等)

每类容器需要改两个位置:

类型左边框色(markdown.css,写死)标题色变量(main.css)
info#6fa8dc--wp-info-title
tip#7ba05b--wp-tip-title
warning#d9a441--wp-warn-title
danger#c96a5b--wp-danger-title
detailsvar(--wp-text-tertiary)无(用默认文字色)

容器共同样式:padding: 1em 1.25em、border-radius: var(--radius-wp)、border-left-width: 3px、background: var(--wp-surface)、font-size: 0.98rem。

8.3 圆角不统一的问题

--radius-wp: 8px 只在部分位置生效。实际引用 var(--radius-wp) 的文件(全仓 grep):markdown.css(.md-container :443、.img-frame :373/:395、blockquote :101、.md-toc :532)、ScrollControls.css:8、music-player.css:76/:135、FriendsPage.css:9。

以下位置是写死的 px,想全局改圆角需要一并替换:

位置写死值
文章卡片 PostCard.astrorounded-[12px]
代码块 markdown.cssborder-radius: 12px
名片 .card(ProfileCard.css)border-radius: 12px
卡片角标 .card-badge(PostCard.css)12px 4px 12px 4px 等对角组合
分页按钮rounded(Tailwind 默认 4px)
弹窗面板rounded-[8px]
<main> / 页脚rounded-[8px]
标签胶囊 TaxonomyPage(kind="tags")rounded-[8px]、rounded-[3px]
TOC 激活项右侧 PostPage.cssborder-radius: 0 4px 4px 0(未用 --radius-wp)

建议:若要做「全局圆角开关」,把上述硬编码替换为 var(--radius-wp) 或新增 --radius-card 变量统一管理。

8.4 背景网格

css
12345
body {
  background-image: var(--wp-grid-image);
  background-size: 28px 28px;      /* ← 必须与 SVG 的 width/height 一致 */
  background-attachment: fixed;    /* ← 注释说明:限制背景跟随滚动 */
}
想改什么做法
网格更密/更疏同时改 SVG 的 width/height、viewBox 的 M0 0L28 0M0 0L0 28 以及 background-size
网格线更明显改 stroke-opacity(亮 0.14 / 暗 0.11)
完全去掉网格background-image: none
网格随页面滚动删掉 background-attachment: fixed

鼠标划过点亮(参考顶栏 hover 的实心主题色方块):BaseLayout 在 <body> 首子节点挂载 #grid-trail(position: fixed; z-index: -1,压在网格背景之上、页面内容之下),pointermove 按 28px 网格换算指针所在格子生成 .grid-trail-cell(var(--wp-accent) 实心方块、无圆角,grid-trail-out 0.65s 淡出后自动移除节点);快速划过时线性插值补齐跳过的格子形成连续轨迹。仅响应鼠标(pointerType === 'mouse'),prefers-reduced-motion 下不生成。关闭方法:删除 #grid-trail 节点与对应脚本及 main.css 的 .grid-trail-cell 样式。

卡片「另一套主题」预览方窗(ProfileCard 名片与 PostCard 文章卡共用机制):每张卡片内嵌一层 .card-preview 覆盖层(position: absolute; inset: -1px、overflow: hidden、border-radius: 12px、pointer-events: none、inert),覆盖层基础样式(窗口裁切、--preview-size、is-on 开关、reduced-motion 兜底)定义在 main.css 的共享块中,两种卡片都调用 src/lib/pointerPreview.ts 的 attachPointerPreview(card, options)(共用同一份结构、节流与动画时序,差异只在 options:prepareClone 定制克隆、syncClone 增量同步、resetOnDetach、hover 悬停镜像、resyncEvents)。鼠标在卡内移动时用 clip-path: inset(...) 只留出指针处 56×56 的方窗,窗口里是卡片克隆件套上相反主题 token 后的真实渲染(亮色模式给克隆加 .dark、暗色模式加 .light),所以看到的就是「切到另一套主题时长什么样」。克隆在脚本加载期构建一次并常驻,状态变化只做就地同步、从不重建节点(重建换新 <img> 要等解码,空帧在方窗里会表现为头像闪烁)。名片刷漆切换时钟时克隆层会跟着本体跑同一套 clip-path 关键帧(否则克隆层只能在全显/全裁之间二选一,方窗会在整段动效里停在切换前的画面),落定瞬间再由 profile-card-clock-settle 事件同步到终态;时钟模式下方窗照常唤起,显示反色时钟(亮段 --wp-accent 随反主题翻转)。文章卡克隆额外剔除 .card-badge / .card-actions 角标与操作条(方窗内不骑边),且因覆盖层 pointer-events: none 克隆收不到 :hover,脚本把指针命中的 data-hover 链接(标题/系列/标签)按序号镜像到克隆节点加 .preview-hover 类,由 PostCard.css 用克隆自身的反主题变量复刻 hover 效果。.light 与 :root 同级(main.css 的 :root, .light)且声明同一套亮色 token,专供这种「在子树里翻转主题」的场景。想改窗口尺寸改 main.css 的 --preview-size(仅此一处,两处卡片共享);关闭方法:删掉组件里的 .card-preview 节点与对应的 attachPointerPreview() 调用、main.css 的共享 .card-preview 样式块,以及 PostCard.css 的 .preview-hover 镜像规则(.light 可保留,它只是 token 作用域)。详见 02-组件说明.md#41-profilecardastro 与 02-组件说明.md#42-postcardastro。

8.5 滚动条

css
123
::-webkit-scrollbar { width: 0; height: 6px; }         /* 竖向滚动条隐藏(宽度 0) */
::-webkit-scrollbar-thumb { background: var(--wp-border); border-radius: 3px; }
::-webkit-scrollbar-track { background: transparent; }

项目刻意隐藏了竖向滚动条,只保留 6px 的横向细滚动条(用于代码块/表格横向滚动)。若想恢复竖向滚动条,把 width: 0 改成 width: 8px。

9. 样式改动自检清单

  • 亮色与暗色都检查过(ScrollControls 的主题按钮切换)
  • 硬编码颜色是否改为 var(--wp-*) 或语义类
  • 新增颜色是否同时定义了 :root 与 .dark 两套值
  • --wp-grid-image 里的 stroke='%23XXXXXX' 是否与新主色一致
  • AboutPage.astro 的 16 处 #E86233 是否需要同步
  • 移动端断点 768px / 640px / 400px 是否正常
  • 新增动画是否补了 prefers-reduced-motion 降级
  • 改头部高度后,是否同步了 markdown.css 的 scroll-margin-top
  • pnpm check 与 pnpm build 通过