1150 字
6 分钟
Shirone 排版调优:从三列瀑布流到完美等高卡片

在折腾完双仓分离架构后,我本以为博客的建设大业已经完成,直到我点开文章列表的「网格模式(Grid)」。

满屏的卡片高低错落、无图卡片矮一截、长标题直接把卡片撑爆……这对于强迫症来说简直是灾难。

这篇文章记录了我对 Shirone 主题 PostCard.astro 和 PostMeta.astro 组件的深度调教,一步步实现“三列等高完美网格”和“列表模式高级虚化”的全过程。


🧱 第一阶段:为什么我的网格只有两列?#

Shirone 的网格布局是基于 CSS Grid 和容器查询(@container)实现的。默认情况下,如果你用的是 regular 档位,容器宽度必须达到 49rem(约 784px) 才会激活两列。如果你用的是 compact 档位,也需要达到 41rem(约 656px)。

如果你的主内容区因为侧边栏挤压,宽度只有 742px,就会刚好卡在 regular 的阈值下方,导致永远只能显示单列。

解决方案: 如果你想强行上三列,可以修改主题源码 src/config/postListConfig.ts 中的最小宽度常量:

export const POST_CARD_MIN_WIDTH = {
compact: "14rem", // 默认是 20rem,改为 14rem 即可在 742px 下容纳三列
regular: "24rem",
relaxed: "28rem"
};

同时在内容仓的 post-list.yaml 中开启 compact 模式。


📐 第二阶段:强迫症福音 —— 卡片绝对等高#

激活多列后,最丑的问题来了:有封面的卡片很高,无封面的卡片很矮;标题长的文章很高,标题短的很矮。

要解决这个问题,我们需要进行三步手术。

1. 无图卡片插入“透明占位块”#

在 PostCard.astro 的 HTML 中,不要用 {hasCover && ...} 来控制整个图片容器的渲染。我们应该始终渲染容器,内部用三元表达式判断:

<div class="m3-blog-postcard__cover">
{hasCover ? (
<picture>
{/* 你的图片代码 */}
</picture>
) : (
<div class="m3-blog-postcard__cover-placeholder"></div>
)}
</div>

然后在 CSS 中,让它在列表模式下隐藏,网格模式下显示(利用 aspect-ratio: 16/9 撑开高度):

&__cover
&-placeholder
background: var(--card-bg) // 或者纯透明
width: 100%
height: 100%
@media (min-width: bp-md)
&:not(.m3-blog-postcard--cover) &__cover
display: none // 列表模式隐藏占位块
grid-card-layout(shell-selector)
:global({shell-selector} .m3e-post-list--grid) &:not(.m3-blog-postcard--cover) &__cover
display: block // 网格模式强制显示

2. 强制文本截断( -webkit-line-clamp )#

为了防止长标题和长标签把卡片撑爆,必须在 PostCard.astro 中限制它们的行数。

  • 标签行:display: -webkit-box; -webkit-line-clamp: 2; overflow: hidden;
  • 简介行:display: -webkit-box; -webkit-line-clamp: 2; overflow: hidden;
  • 标题:因为标题是用 <a> 包裹的 flex 布局(为了让右侧箭头对齐),直接加截断会失效。所以我们在标题文字外面套一层 <span class="m3-blog-postcard__title-text">,只对文字进行截断。

3. 锁死卡片物理高度#

最后,在 grid-card-layout 宏里,给最外层的卡片加上固定高度(比如 365px),并切断溢出:

grid-card-layout(shell-selector)
:global({shell-selector} .m3e-post-list--grid) &
flex-direction: column-reverse
height: 365px // 绝对高度
overflow: hidden // 超出部分物理切断

⚓ 第三阶段:底部信息的“推底”设计#

默认的日期、字数、阅读时间混在元数据里。为了实现强迫症对齐,我在 PostCard.astro 的 HTML 里把手写了底部信息行:

<div class="m3-blog-postcard__footer">
<span class="m3-blog-postcard__footer-item">
<Icon name="material-symbols:calendar-today-outline-rounded" /> {published}
</span>
<span class="m3-blog-postcard__footer-divider">|</span>
<span class="m3-blog-postcard__footer-item">
<Icon name="material-symbols:article-outline-rounded" /> {wordCount}
</span>
</div>

配合 __body 的 flex 布局,让它永远贴着底边:

&__body
display: flex
flex-direction: column
&__footer
margin-top: auto // 核心:吃掉中间所有多余的高度
padding-top: var(--m3e-space-2)
font-size: 0.85rem

🪄 第四阶段:列表模式的图片“高级感”#

网格模式解决后,列表模式(List)的右侧封面也有讲究。我想实现:平时左侧虚化融合,悬停时放大且边缘变清晰。

注意:绝对不要加 pointer-events: none,否则 :hover 无法触发。

&__cover
> picture > img
transition: transform 0.4s cubic-bezier(0.2, 0, 0, 1)
// 悬停放大
.m3-blog-postcard:hover & > picture > img
transform: scale(1.05)
@media (min-width: bp-md)
position: absolute
top: 0
right: 0
bottom: 0
width: 32%
// 默认渐变虚化
mask-image: linear-gradient(to right, transparent 0%, black 30%)
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 30%)
transition: mask-image 0.4s ease
// 悬停时清晰
.m3-blog-postcard:hover &
mask-image: linear-gradient(to right, transparent 0%, black 5%)
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 5%)

同时,在网格模式下,必须使用 mask-image: none !important 强制取消虚化,保证网格图片边缘干净利落。


总结#

经过这一番改造,PostCard.astro 不再是一个简单的展示组件,而是一个精密的布局机器。虽然代码量增加了不少,但最终呈现的视觉效果——整齐划一的等高网格和丝滑的虚化悬停,绝对对得起这几天的折腾。

如果你也受够了排版错乱,不妨对着上面的代码试试看!

Shirone 排版调优:从三列瀑布流到完美等高卡片
https://shirone.mysqil.com/posts/shirone-排版调优/
作者
残梦断忆
发布于
2026-10-25
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取