通过伪元素叠加半透明遮罩层,可实现父容器背景图变暗效果,同时确保子元素(如顶部导航栏)保持完全不透明。
在实际编写 CSS 时,你是否也遇到过类似困扰?页面上有一个包含大背景图的父容器,为了提升文字可读性,需在其上方叠加一层半透明遮罩,使整体背景变暗。但另一个关键需求是:容器内的顶部导航栏必须保持纯色,不能被透明度影响。
如果直接给父容器设置 opacity,结果会怎样?所有子元素——包括 #topbar——都会一起变为半透明。这显然不符合预期。
为什么会这样?opacity 并非可继承的背景属性,它作用于整个元素及其所有后代。简单来说,一旦父容器添加了 opacity,就像在整块玻璃上贴了一层半透明膜,玻璃上的任何内容都会随之变暗。
那么正确的做法是什么呢?其实很简单:将透明效果与 DOM 结构彻底分离。不让 #bg 本身参与任何透明度渲染,而是利用它的伪元素 ::before 来充当视觉层面的遮罩层。这样一来,#bg 仅作为一个定位容器,自身不设置背景色也不调整透明度,它的子元素自然不会被无辜牵连。
直接上代码示例:
body {
margin: 0;
background: url('bg.jpg') no-repeat center/cover;
}
#bg {
position: relative; /* 为伪元素提供定位上下文 */
height: 100vh;
width: 100%;
}
#bg::before {
content: "";
display: block;
position: absolute;
inset: 0; /* 等价于 top: 0; right: 0; bottom: 0; left: 0 */
background-color: black;
opacity: 0.7;
z-index: 0; /* 确保遮罩位于底层 */
}
#topbar {
background-color: gray;
height: 80px;
width: 100%;
position: relative; /* 启用 z-index */
z-index: 1; /* 置于遮罩层之上,保持完全不透明 */
}
关键要点说明
- #bg 不设置 background-color 或 opacity,从根本上避免对子元素的污染;
- ::before 伪元素负责视觉半透明遮罩,精准覆盖整个容器的范围;
- #topbar 必须声明 position(relative、absolute 或 fixed),否则 z-index 不会生效;
inset: 0是现代 CSS 推荐写法,兼容性良好(Chrome 58+/Firefox 55+/Safari 14.1+),如果需要支持更老的浏览器,可以显式写成top: 0; left: 0; right: 0; bottom: 0;。
这个方案的思路非常清晰:层级关系分明,遮罩层与内容层各自独立,完全绕开了 opacity 继承的陷阱。无论背景是图片还是渐变色,只要追求“背景柔化 + 前景实色”的效果,这套做法都值得作为首选方案。
