Tailwind 没有内置 scroll 状态修饰符,因滚动是动态 DOM 事件,CSS 无法监听,必须依赖 JS 或间接模拟(如 peer + checkbox);纯 CSS 方案需 JS 控制状态源,生产环境推荐用 Intersection Observer 或 scroll 监听加节流处理。

为什么直接加 scroll 相关类没用?
Tailwind 没有内置 scroll 状态修饰符,比如 scroll:bg-blue-500 或 scrolled:bg-gray-100 —— 这些写法浏览器根本不解析,也不触发任何行为。它不像 hover:、focus: 那样有对应伪类支持。滚动是动态 DOM 事件,CSS 本身无法监听 scroll 位置并切换 class,必须靠 JS 或借助状态源(如 checkbox + peer)间接模拟。
用 peer + input[type=checkbox] 模拟滚动状态
这不是“监听滚动”,而是把滚动行为和一个可切换的状态绑定起来:比如用户滚动到某区域后手动勾选 checkbox,再用 peer-checked 控制导航栏样式。实际项目中更常见的是结合 Intersection Observer 的 JS 方案,但若坚持纯 CSS,只能靠用户交互触发(例如点击“回到顶部”按钮时同步改变导航栏颜色)。
-
<input id="scrolled" type="checkbox" class="peer hidden">放在<nav>同级且之前 - 给
<nav>加peer-checked:bg-white peer-checked:shadow-sm等类 - 用 JS 在滚动到阈值时
document.getElementById('scrolled').checked = true - 注意:纯 CSS 无法自动响应 scroll,JS 是绕不开的一环;所谓“纯 CSS”只是把 class 切换逻辑从 JS 移到 class 名上,状态源仍需 JS 控制
真正可靠的滚动变色方案:用 JS 监听 scroll 并切换 class
99% 的生产环境都走这条路。关键不是“能不能不用 JS”,而是“怎么让 JS 轻量、可复用、不破坏 Tailwind 体系”。推荐做法:
- 给
<nav>设一个固定 class,比如data-scroll-change - 在全局 JS 中统一监听:
window.addEventListener('scroll', () => { ... }) - 判断
window.scrollY > 64时,给nav加scrolled类;否则移除 - 在 CSS 中定义:
.scrolled { @apply bg-white shadow-sm; }(或直接写在tailwind.config.js的theme.extend.container里) - 避免内联 style 或重复绑定,用节流(
throttle)防抖,尤其移动端
容易被忽略的细节:透明导航栏在滚动后变色时的视觉断裂
初始状态 bg-transparent,滚动后切到 bg-white,常伴随阴影出现——但若没设 transition-colors 和 transition-shadow,会“啪”一下跳变。更隐蔽的问题是:
立即学习“前端免费学习笔记(深入)”;
- 导航栏高度变化(比如从
h-16变成h-14)会导致页面内容上跳,必须保持高度一致 - fixed 定位的 nav 若没加
top-0和z-50,可能被其他元素遮挡 - 暗色模式下,
bg-white在 dark:bg-gray-900 场景里会突兀,建议用dark:scrolled:bg-gray-900分开控制 - 部分安卓 WebView 不支持
scroll-behavior: smooth,但不影响滚动监听,只是锚点跳转卡顿——这是独立问题,别混为一谈


















