根本原因是CSS加载顺序决定层叠优先级,而非选择器权重;Bootstrap CSS必须在自定义CSS之前引入,否则同选择器下后加载者覆盖前者。

Bootstrap样式被其他CSS覆盖,根本不是选择器写得不够狠,而是加载顺序和特异性规则被误读了——后加载的CSS在同权重下天然赢,但很多人把自定义样式放到了Bootstrap前面,还反复加!important。
检查标签顺序是否颠倒
这是最常见、最容易漏掉的问题。浏览器按<link>出现顺序解析CSS,同选择器下后加载者胜出。如果你的HTML里是这样:
<link rel="stylesheet" href="css/custom.css"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
那custom.css里的.btn规则一定被Bootstrap覆盖,哪怕你写了!important也救不回来(因为Bootstrap自己也用!important)。
- 正确顺序必须是 Bootstrap 在前,自定义 CSS 在后
- Webpack/Vite 项目中,确认
import 'bootstrap/dist/css/bootstrap.min.css'出现在import './custom.css'之前 - React 中用
create-react-app时,别在组件里import './Button.css'去覆盖.btn——它会插入到末尾,晚于全局引入但早于预期
别硬刚!important,优先用CSS变量覆盖
Bootstrap 5.3+ 的按钮、提示框、表单控件等都已转向--bs-*变量驱动,直接改变量比写十行带!important的规则更干净、更可控。
立即学习“前端免费学习笔记(深入)”;
- 变量必须注入在 Bootstrap 样式之后,比如放在
前的<style></style>里,或确保custom.css在bootstrap.min.css之后加载 - 改
.btn-primary颜色不能只设--bs-primary,还得同步改--bs-primary-rgb,否则:hover状态文字对比度可能不达标 - 对 JS 动态插入的组件(如
.tooltip、.modal),变量要作用在其实际挂载位置的祖先上,例如body .my-tooltip { --bs-tooltip-bg: #1a202c; }
遇到伪类状态失效(:hover/:disabled/:focus-visible)怎么办
Bootstrap 5.3+ 在这些状态里大量使用!important,你只覆盖.btn-primary的背景色,:hover时还是会回退——因为.btn-primary:hover是一条独立且带!important的规则。
- 必须连同伪类一起重写:
.btn-primary:hover, .btn-primary:focus-visible, .btn-primary:disabled - 或者用更高 specificity 的选择器绕过,比如
button.btn.btn-primary(权重 0,2,1)压过 Bootstrap 的.btn.btn-primary(0,2,0) - 避免在自定义 CSS 中对同一属性反复加
!important,否则后续维护时没人敢动这条链
JS动态插入的DOM节点最容易漏掉
像modal、dropdown、tooltip这些组件,JS 运行时把它们插到document.body底部,完全脱离你写的容器结构。你在.my-page .btn里写的规则,对body > .modal里的按钮根本不起作用。
- 给这类组件加专属 class 并单独写样式,比如
body .my-modal .btn-primary - Vue/React 项目中,用
<teleport to="body"></teleport>或ReactDOM.createPortal确保自定义样式能命中目标节点 - 不要依赖
::v-deep或/deep/这种已被废弃的穿透写法,改用:deep(.btn)(Vue 3)或 scoped style + 局部 class 组合
真正难处理的从来不是怎么覆盖,而是哪些地方根本没被你“看到”——比如tooltip不在你写的<div class="app">里,<code>modal-backdrop的z-index被另一个position: relative父容器悄悄截断了层叠上下文。调试时先看 computed,再看 actual DOM 位置,比猜选择器权重管用得多。


















