三栏布局博客的侧边栏悬浮优化与暗夜模式适配

技术随笔

三栏布局博客的侧边栏悬浮优化与暗夜模式适配

1. 概述

Blog-SSR 从单栏布局升级为三栏布局(左侧边栏 / 主内容区 / 右侧边栏)后,侧边栏的体验细节成为需要打磨的重点。本文记录了两项核心优化:

  • 悬浮固定:侧边栏在页面滚动时保持可见,不随内容流消失。
  • 暗夜模式适配:移除硬编码的 Tailwind dark: 类,统一使用 CSS 自定义属性(变量),实现主题一键切换。

2. 问题分析

2.1 滚动消失问题

初始三栏布局的 HTML 结构如下:

<div class="lg:grid lg:grid-cols-[220px_1fr_240px] lg:gap-8">
  <aside><!-- 左侧边栏卡片 --></aside>
  <main><!-- 页面内容 --></main>
  <aside><!-- 右侧边栏卡片 --></aside>
</div>

侧边栏卡片直接放在 grid 子项中,没有 position: sticky,当主内容滚动时,两侧随整体流式布局一起滚出视口。用户阅读长文时需要反复滚动到页面顶部才能使用导航/目录,体验较差。

2.2 暗夜模式硬编码

SideBarCard 组件使用 Tailwind 的 dark: 变体:

class: "bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-5 shadow-sm",

这种方式有两个问题:

  1. 与全局主题方案割裂:项目使用 CSS 变量 + data-theme 属性切换主题(light / dark),而非 Tailwind 的 class 策略。dark: 变体依赖 prefers-color-scheme 媒体查询或 .dark 类,与项目的 [data-theme="dark"] 选择器不匹配。
  2. 维护负担:每个样式属性都需要同时写 light 和 dark 两个值,修改主题时需多处同步更新。

3. 悬浮固定实现

3.1 方案选择

| 方案 | 说明 | 选择 | |------|------|------| | position: fixed | 脱离文档流,位置相对于视口 | ❌ 会与 grid 布局冲突,宽度需手动计算 | | position: sticky | 在父容器内固定,宽度由 grid 自动分配 | ✅ 与 grid 完美配合 |

3.2 实现细节

在侧边栏的 aside 内部增加一个粘性容器:

aside { class: "hidden lg:block w-full",
    div { class: "sticky top-24 max-h-[calc(100vh-8rem)] overflow-y-auto space-y-5",
        style: "scrollbar-width:thin; scrollbar-color:var(--border) transparent;",
        // ... 卡片内容
    }
}

关键属性说明:

  • sticky top-24:元素在滚动到距离视口顶部 24 * 0.25rem = 6rem(96px)时粘住,与 64px 高的导航栏 + 32px 间隙对齐。
  • max-h-[calc(100vh-8rem)]:最大高度为视口高度减去 8rem(128px),确保底部不被裁切。
  • overflow-y-auto:当侧边栏内容超长时启用内部滚动。
  • scrollbar-width:thin:Firefox 细滚动条,减少视觉干扰。

为什么用 sticky 而不是 fixed

fixed 会脱离 grid 流,宽度需写死(220px / 240px),在小屏/缩放下行为异常。sticky 保留在 grid 单元格内,宽度由 grid-template-columns 自动分配,响应式无需额外处理。

4. 暗夜模式适配

4.1 项目的主题变量体系

项目的主题变量定义在 templates/index.html 中:

:root {
  --bg: #eff1f5;           --card: #e6e9ef;
  --border: #ccd0da;        --text: #4c4f69;
  --secondary: #5c5f77;     --tertiary: #9ca0b0;
  --primary: #1e66f5;       --primary-light: #d6e4fb;
  --hover: #ccd0da;
}

[data-theme="dark"] {
  --bg: #1e1e2e;            --card: #181825;
  --border: #313244;        --text: #cdd6f4;
  --secondary: #bac2de;     --tertiary: #6c7086;
  --primary: #89b4fa;       --primary-light: #2a4a7a;
  --hover: #313244;
}

所有颜色通过 var(--name) 引用,[data-theme="dark"] 下的变量值会在用户切换主题时自动覆盖。

4.2 SideBarCard 重构

修改前:

class: "bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-5 shadow-sm",
h3 { class: "font-bold mb-3", {title} }

修改后:

class: "rounded-lg border p-5 shadow-sm",
style: "background:var(--card); border-color:var(--border);",
h3 { class: "font-semibold mb-3 text-sm tracking-wide", style: "color:var(--secondary);", {title} }

对比效果:

| 样式 | 修改前 Light | 修改前 Dark | 修改后(CSS 变量) | |------|-------------|-------------|-------------------| | 卡片背景 | #ffffff | #1f2937 | var(--card)#e6e9ef / #181825 | | 卡片边框 | #e5e7eb | #374151 | var(--border)#ccd0da / #313244 | | 标题颜色 | inherit(黑色) | inherit(白色) | var(--secondary)#5c5f77 / #bac2de |

4.3 侧边栏链接与标签

所有交互元素统一使用主题变量:

/* assets/main.css */
.sidebar-link:hover {
    background: var(--hover);
    color: var(--text) !important;
}

计数标签(分类文章数、归档条目数)使用:

style: "background:var(--primary-light); color:var(--primary);",

5. 优化效果

5.1 滚动体验

| 场景 | 优化前 | 优化后 | |------|--------|--------| | 阅读长文 | 侧边栏随内容消失,需回顶部操作 | 侧边栏悬浮固定,导航/目录始终可见 | | 多标签页跳转 | 每次需滚动回顶 | 任意位置可直接点击 | | 窄屏(< 1024px) | 侧边栏隐藏 | 同左(未改变) |

5.2 主题切换

Light:  ┌─────────────────────┐        Dark:  ┌─────────────────────┐
        │ background: #e6e9ef │               │ background: #181825 │
        │ border: #ccd0da     │    ⇄          │ border: #313244     │
        │ color: #5c5f77      │               │ color: #bac2de      │
        └─────────────────────┘               └─────────────────────┘

所有卡片会在用户点击 🌙 切换按钮时瞬间统一变色,无需刷新页面。

5.3 可维护性提升

  • 颜色值集中管理在 index.html 的 CSS 变量中,修改主题只需改变量值。
  • 侧边栏组件无需关心具体色值,只引用 var(--xxx)
  • 未来要新增主题(如 Sepia 护眼模式),只需在 CSS 中新增一组变量。

6. 总结

两项优化都遵循了渐进增强的原则:在小屏设备上侧边栏原本就隐藏,行为无变化;在大屏上 sticky 提供了更好的滚动体验。暗夜模式的改造则让组件与项目的全局主题系统对齐,消除了一处技术债。这些细节打磨虽然不改变功能,但对日常使用体验的提升是显著的。

CSS暗夜模式布局优化前端UX