accordion__header--is-open必须绑定在header元素上,因展开/收起是用户对header的操作反馈;content区应使用accordion__content--expanded控制布局响应,二者需js同步切换。

accordion__header--is-open 修饰符必须绑定在 header 元素上,不是 content
很多人把 accordion__content--is-open 当作状态类加在内容区,结果动画不触发、JS 切换无反应。BEM 要求交互状态类必须落在承载该状态的触发元素上——也就是用户点击的那个标题区域。因为展开/收起是用户对 header 的操作反馈,不是 content 自身的状态。
常见错误现象:
DevTools 里看到 accordion__content--is-open 类已添加,但 CSS 规则没匹配上
transition 不生效,内容瞬间显隐,没有过渡
键盘聚焦时视觉反馈错位,:focus 和实际展开态不一致
- 正确写法:
accordion__header--is-open控制 icon 旋转、背景色变化等交互反馈 - 布局响应由另一个类承担,比如
accordion__content--expanded,它只负责控制max-height或transform - 两个类必须由 JS 同步切换:点击 header 时,同时 toggle
accordion__header--is-open和accordion__content--expanded
max-height 动画失效?别用 auto,用具体数值或 transform 替代
max-height: 0 → max-height: auto 看似简洁,但浏览器无法对 auto 插值,transition 直接跳过,变成硬切。这不是你 CSS 写错了,是 CSS 引擎根本没法算。
实操建议:
若内容高度相对固定(如 FAQ 条目),直接写死 max-height: 300px,确保大于所有可能内容高度
若高度动态(如含图片、富文本),改用 transform: scaleY(0) → scaleY(1) + overflow: hidden,GPU 加速且无需测量
务必加 will-change: transform,尤其在移动端,否则 Safari 可能掉帧
- 避免在
accordion__content上设height: auto—— 它会干扰transform动画的渲染层 - 不要给
accordion__content设padding-top/bottom并期望它参与动画 ——transform不影响盒模型,需额外包裹一层或用margin
单开模式下如何避免多个 content 同时展开?靠 eventKey 还是 DOM 结构?
纯 CSS 实现不了真正的单开(exclusive open)——:checked 方案天然支持多选,details 标签默认也是各自独立。如果你需要“点一个,关掉其他”,就必须靠 JS 控制状态,而 BEM 类名只是状态的视觉映射。
关键点在于:状态管理不在 CSS,而在 JS 如何组织 eventKey 或 DOM 查找逻辑。BEM 只负责让类名可预测、可定位。
- 推荐结构:每个
accordion__item是独立块,内部含accordion__header和accordion__content - JS 用
document.querySelectorAll('.accordion__item')获取全部项,遍历移除所有accordion__content--expanded,再给当前项加上 - 别依赖父容器 class 控制子项,比如
.accordion--single-open .accordion__content—— 这种嵌套选择器权重高、难覆盖,且违背 BEM “扁平命名”原则
服务端渲染(SSR)时 accordion 状态类名容易 mismatch
初始 HTML 里没带 accordion__header--is-open,但 JS 水合后立刻加,会导致 React/Vue 报 warning,甚至样式闪动。这不是 bug,是 SSR 渲染和客户端状态不同步的典型表现。
解决思路不是禁用 hydration,而是让初始类名可预测:
- 把初始展开态作为 props 传入组件,服务端和客户端使用同一份状态源
- 避免在
useEffect里首次运行就 toggle 类名 —— 改成根据 props 初始值直接生成 className - 如果必须动态判断(如根据 URL hash 展开某一项),用
useLayoutEffect在 DOM 绘制前同步设置,减少闪动
最易被忽略的一点:BEM 修饰符的语义必须严格对应真实交互意图。写 --is-open 就代表“这个 header 当前已被用户主动打开”,而不是“这个 content 应该显示”。类名一旦偏离这个契约,后续所有动画、可访问性、SSR 都会连锁出问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











