纯css tab切换必须用:checked + ~实现,而非:hover;input需同名互斥,dom顺序为input→label→section,内容区用~控制显隐;避免display: none,改用visibility/opacity或height+overflow;动画优先max-height或opacity+transform;ie11降级用label:focus。

Tab切换靠:checked + ~选择器,不是:hover
很多人一上来就用:hover做Tab切换,结果鼠标移开就塌了——这不是Tab,是Tooltip。真要“保持选中状态”,必须依赖表单控件的交互状态。<input type="radio">配合:checked是最稳妥的纯CSS方案,浏览器原生支持,无需JS干预,且能被屏幕阅读器识别。
关键点在于:所有input必须同名(name="tab"),才能互斥;每个input对应一个label和一个section,用for/id或嵌套绑定;内容区用~(兄弟选择器)控制显隐,不能用+(相邻兄弟),因为label和section通常不紧邻。
-
input必须放在label之前(DOM顺序),否则~无法向上选中 - 避免用
display: none隐藏未选中项——会导致焦点丢失、动画卡顿;改用visibility: hidden+opacity: 0或height: 0+overflow: hidden - 如果Tab项动态生成,确保
id/for值唯一且可预测,否则label点击无效
层级错乱?检查z-index是否在:checked触发后才生效
常见现象:点击Tab后内容闪一下、显示错位、背景盖住文字——本质是堆叠上下文(stacking context)没理清。纯CSS Tab里,z-index只对定位元素(position非static)有效,而很多人忘了给section加position: relative。
更隐蔽的问题是::checked ~ .tab-panel这类选择器本身不创建新层叠上下文,但如果你在.tab-panel上写了z-index: 1却没设position,它完全不起作用。正确做法是让每个.tab-panel默认position: relative; z-index: 1,再用:checked ~ .tab-panel把当前项提至z-index: 2,其他项保持z-index: 1即可。
立即学习“前端免费学习笔记(深入)”;
- 不要给
input或label设z-index——它们默认不在层叠上下文中,设了也无效 - 如果Tab头用了
position: absolute,务必确认其父容器有position: relative,否则会脱出正常流,导致点击区域偏移 - 移动端Safari对
z-index在:checked后的重绘有时延迟,可加transform: translateZ(0)强制硬件加速
动画卡顿?别用height做过渡,改用max-height或opacity + transform
height: 0 → height: auto无法过渡——CSS不支持auto参与transition。强行写只会跳变。常见补救是设个固定height,但这违背响应式原则,内容一多就溢出。
真正可行的方案只有两个:max-height(设一个足够大的值,如max-height: 500px)或opacity + transform: translateY()。前者简单但不够精准(高度估算不准时仍有裁剪),后者更可控,适合需要平滑收起/展开的场景。
-
max-height过渡需配合overflow: hidden,且初始值不能是0(部分浏览器渲染异常),建议用max-height: 0.1px - 用
transform时,避免同时过渡opacity和transform——某些旧版Chrome会闪烁,分开写transition: opacity 0.2s, transform 0.2s更稳 - 别在
section上直接写transition: all——会把z-index等不可动画属性也拖进来,增加重排开销
IE11及以下不支持:checked联动?用:focus降级兜底
IE11支持:checked,但不支持~选择器跨多个兄弟元素(比如input和section中间隔了一个div)。一旦结构稍复杂,IE下就失效。这时候别硬扛,用:focus模拟选中态更实际。
做法是:把label设为tabindex="0",用label:focus ~ .tab-panel触发显示。虽然失去“单击即选中”的语义,但至少功能可用,且键盘用户仍能操作。注意label必须可聚焦(tabindex不能为负值),且需用outline: none配合:focus样式避免双焦点环。
- IE下
input[type="radio"]本身无法获得焦点(除非显式tabindex),所以不推荐依赖它 - 降级方案里,
label点击后需手动blur(),否则焦点残留影响后续交互 - 如果项目明确不支持IE,这步可跳过;但凡有政务、金融类老系统对接需求,这个兜底逻辑得提前写进基础样式里
纯CSS Tab最麻烦的从来不是怎么写,而是怎么让不同浏览器对“同一个:checked状态”做出一致的层叠和动画响应。细节全在选择器顺序、定位上下文、过渡属性这三处咬合上,漏掉一个,整个Tab就松动。










