
HY
折腾进行时
Hello, I'm HY.








动态页想换个样子。参照的是另一个同主题的站,数据源不变 —— 还是继续接 memos,只换皮。

参考站也是 Firefly 的 fork,但它那套动态是自己写的组件(MomentCard.astro 加一个独立页面)。按这个路子走,得新开一个页面、把数据管线重接一遍 —— 动静不小。
所以先把我们的动态页 DOM dump 出来看了一眼:
1dynamic-entry.page-entry.card-base > article2 ├ page-entry-header → page-avatar / page-identity(page-author + page-meta)3 │ / page-pinned-badge / page-entry-decoration4 ├ page-content5 ├ dynamic-gallery → page-gallery-grid6 └ dynamic-inline-comments → page-comment-toggle + page-comment-panel头像、作者、时间、地点、置顶徽章、图片网格、评论按钮 —— 一个不缺。参考站那套 card-header / card-avatar / card-tags 跟它几乎是逐项对应的。
差的只是样式。那就不用新开页面了:纯样式覆盖,加一点脚本补它没有的东西。
约束还是老一套:src/pages/、src/components/、src/styles/ 这些红线一根不碰。做法是——
| 文件 | 状态 | 干什么 |
|---|---|---|
public/assets/css/dynamic-card.css | 新增 | 全部样式,只在 /dynamic/ 加载 |
public/assets/js/dynamic-hero.js | 新增 | 把页脚的封面区外壳搬进正文 |
src/config/FooterConfig.html | 改(追加) | 封面区外壳与文案、PAGE_STYLES 条目 |
参考站卡片最显眼的是左边一根主题色竖条。上游的 .page-entry 是 overflow: visible,竖条贴边会被裁掉,所以先把它裁住:
1dynamic-entry.page-entry {2 position: relative;3 overflow: hidden;4 padding: 1rem;5}6
7dynamic-entry.page-entry::before {8 position: absolute;9 inset: 0 auto 0 0;10 width: 4px;11 border-radius: 0 2px 2px 0;12 background: var(--primary);13 opacity: 0.35;14 content: "";15}其余都是对着参考站抄:头像从 48px 收到 40px 并加一圈 2px 主题色描边、昵称改成主题色、时间与地点之间补一个 ·(上游只靠 gap 分隔)、正文取消那个跟头像对齐的 3.75rem 缩进、右上角那枚纯装饰的引号撤掉。
所有选择器都挂 dynamic-entry 前缀。上游的 .page-* 在友链页、书签导航那批自建页里也在用,不加前缀会误伤一片。
参考站的网格是固定 100px 的方块(按张数排 1 / 2 / 3 列)。我们上游本来是自适应宽度,所以要把尺寸压成固定的:
1dynamic-entry .page-gallery > .page-gallery-grid {2 width: fit-content;3 grid-auto-rows: 100px;4 grid-template-columns: repeat(3, 100px);5}写完一测——格子还是被摊开。原因在特异性:
| 选择器 | 特异性 |
|---|---|
上游 .page-gallery-grid[data-count="2"](还写着 width: min(100%, 28rem)) | 0-2-0 |
我写的 dynamic-entry .page-gallery-grid | 0-1-1 |
0-2-0 赢。 容器宽度始终是 448px(=28rem),格子设成 100px 也只是被这 448px 平分。改成 dynamic-entry .page-gallery > .page-gallery-grid[data-count=…](0-3-1)才拿下。
最终实测:单图 100px、2 图 206、3 图 312、4 图 206(两列)、6 与 9 图 312(三列)—— 跟参考站的规则对上了。窄屏(≤480px)转自适应均分。
单图我保留了上游的「完整显示不裁剪」,没照搬成 100px 方块。一张风景照被裁成小方块,实在难看。
参考站顶部那一整块——封面大图、右下角浮出的头像、压在封面上的昵称、右对齐的签名,下面一排按钮——上游完全没有。
补它的路子是「页脚注入外壳 + 脚本搬运」:DOM 连同全部文案、链接先写在页脚的 FooterConfig.html 里,由 dynamic-hero.js 在动态页把它搬进正文顶部。
搬的时候有个要点:用 cloneNode,原壳留在页脚。因为正文整个在 swup 的替换范围里,直接把节点搬过去,切一次页就没了;原壳留着,下次切回来再克隆一份。
页面重进会触发好几个事件(astro:page-load、swup:page:view、swup:content:replace),每个都会跑一次搬运。所以插之前得先认门。
第一版我写的是:
1if (host.querySelector(":scope > .ff-wx-cover")) return;看着挺对。实测:连触发三次事件,插出了三份封面。
换成锚点上的标记才拦住:
1if (anchor.dataset.ffHeroMounted === "1") return;2anchor.dataset.ffHeroMounted = "1";swup 换页时 .page-header 是新元素、没有标记,会重新插一份;同一份 DOM 反复触发则不会再插。实测三连触发后仍然是「页脚壳子 1 份 + 正文副本 1 份」。
想要的效果是:跟壁纸用同一批图,但不轮播,每次进来随机一张。
壁纸池在 backgroundWallpaper.ts 的 desktop 数组里 —— 但那是 src/assets/ 下的图,构建后路径是哈希化的 /_astro/xxx.webp,静态 HTML 里写不出来。
仓库里那张图其实有三份,用途完全不同:
| 位置 | 内容 | 能不能当封面 |
|---|---|---|
src/assets/images/DesktopWallpaper/ | 壁纸本体,优化过的 webp(160–290KB) | 路径写不出来 |
public/gallery/desktop-wallpaper/ | 相册原图(1.4–2.4MB) | 太重 |
public/images/desktop-wallpaper/ | 复制来的轻量副本 | ✅ 用这个 |
池子写在注入 HTML 的 data-covers 上(逗号分隔),脚本在挂载时随机挑一张 —— 只在挂载时挑,页面内不轮播;切进切出算一次「进来」,会重新挑。
顺带剔了一张:壁纸数组里的 cover.webp 其实是「暂无封面」那张占位图,第一版收进了池子,随机到它非常难看。
封面区下面紧跟着上游的页面头部(图标 + 「站长动态」+ 描述 + RSS + 计数 + 搜索 + 年份筛选)。它自己是一张卡片,叠在封面卡片下面就是两层壳。
功能全留,壳拆了:背景、边框、圆角、阴影全撤,那个大图标也隐藏,两块的间距收到 4px —— 视觉上变成封面卡片的下半截。
「写说说」按钮本来在封面区下面单独占一行,只有一个按钮,又空又白吃一行高度。挪进了标题行右侧,跟「订阅RSS」「17 动态」排成一排(那格本来就是页面级操作区),尺寸收小到跟旁边两个小字协调。
① 按钮上标评论数。 之前只有「评论」两个字,不点开不知道有没有人聊过。做法是进页面时批量问一次 Waline 的计数接口:
1GET /api/comment?type=count&url=/dynamic/xxx/&url=/dynamic/yyy/…2→ {"data":[2,0,5,…]}一次请求拿回全部,按传入顺序返回 ✓。有评论就显示「评论 2」,0 条不带数字 —— 有数字本身就是在说有人聊过。
② 只自动展开真有评论的。 一版之前做过「全部自动展开」,自己回看一遍又推翻了 —— 那时一进页面每条动态下面都摊着评论区,而其中大部分是空的。现在只展开 count > 0 的,空面板保持收起;懒展开照旧走 IntersectionObserver(离视口 400px 才展开),一页十几条全展开会把首屏拖垮。自动展开只做一次,用户手动收起后不会再被弹开。
③ 编辑器压成一行。 展开之后,Waline 那个「昵称 / 邮箱 / 网址 + 大输入框 + 表情栏」顶在最上面太挡视野。现在默认收成一个 35px 的输入框(整块 77px),点进去自动还原成完整表单(227px):
1editor.classList.add("ff-compact"); // 默认压缩2editor.addEventListener("focusin", () => editor.classList.remove("ff-compact"));用「加类 / 摘类」而不是 :not(:focus-within) 反转,是因为后者得去猜 Waline 原本的 display 到底是 flex 还是 block —— 摘类让规则干脆不生效,它自己什么样就什么样。
第一版我按 .wl-textarea 写选择器,压缩样式一声不响地没生效:编辑器该多高还多高。
dump 出来才知道,Waline v3 的输入框是:
1<div class="wl-comment">2 <div class="wl-panel">3 <div class="wl-header item3">…昵称 / 邮箱 / 网址…</div>4 <textarea class="wl-editor"> ← 是它5 <div class="wl-preview">…6 <div class="wl-footer">…表情 / 字数 / 提交….wl-textarea 压根不存在。改成 .wl-editor 才对。

顺带还藏掉了「评论区 / 分享你的想法,与大家交流讨论」那行标题 —— 动态页一条动态配一个评论区,标题只是白占地方。那块来自全站共用的 src/components/comment/index.astro(上游文件),文章页里它是正文与评论的分界、有用,所以只在动态这个 iframe 里藏,没全站删。
这一轮从头到尾没改一个主题源文件。红线自检(src/pages、src/components、src/styles 等目录)始终是空的。
三个坑有个共同点:都不报错。特异性不够只是样式被压过去、:scope 选择器不生效、类名写错只是匹配不到 —— 没有一个会给你红字提示。所以这类改造真正花时间的不是写样式,是验证「它到底有没有生效」:算一遍特异性、dump 真实 DOM 看类名、给每个改动配一条实测。
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
部分内容可能已过时
分享你的想法,与大家交流讨论