我在真实项目中总结的CSS规范实践与避坑指南

上官树鹤 前端 阅读 1,581
赞 11 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

先说结论:我项目里现在基本不用全局 class 名了。不是因为 BEM 多高级,而是真被 .btn.title 给坑麻了。

我在真实项目中总结的CSS规范实践与避坑指南

以前我写个按钮,随手就是:

.btn {
  padding: 8px 16px;
  border-radius: 4px;
  background: #007bff;
  color: white;
}

结果某天运营提需求:“首页的按钮要圆角大一点,加个阴影;后台列表里的按钮要扁平化,hover 变灰。” 我改完 .btn,发现登录页的按钮也变了……再查 DOM,发现是另一个组件库也定义了 .btn,还用了 !important —— 那一刻,我盯着控制台缓存清了三遍。

后来我统一改成带命名空间的写法,比如:.user-card__btn.article-list__action-btn。不图多炫酷,就图它不会被误伤。

具体怎么落地?我用的是 BEM 的简化版(不严格套规则,但思路照搬):

/* ✅ 推荐:模块名 + 元素名,层级清晰 */
.product-card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
}

.product-card__image {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.product-card__title {
  font-size: 16px;
  font-weight: 600;
  margin: 12px 0 8px;
}

.product-card__price {
  color: #d32f2f;
  font-weight: bold;
}

好处是什么?一目了然谁属于谁;二来,你删一个模块的 CSS,几乎不影响别的地方;三来,团队新人看代码,扫一眼 class 名就知道这玩意儿在哪个组件里。

注意:我不写 .product-card__btn--primary 这种修饰符。太绕,而且容易写漏。需要变体?直接拆成 .product-card__buy-btn.product-card__add-to-cart-btn,语义更直白,也方便后续加 JS 钩子。

这几种错误写法,别再踩坑了

下面这几个,我在三个不同项目里都见过,每次修复都得花半天理样式优先级……

  • 滥用 ID 选择器#header 看着干净,但一旦页面里多个 header(比如弹窗里嵌了个小 header),ID 就撞了。更糟的是,CSS 中 ID 的权重是 100,比 class 高太多,后期想覆盖它,只能靠 !important 或者再塞个 ID —— 恶性循环开始了。
  • 过度嵌套.page .section .content .item .text 这种写法我一度以为很“严谨”,结果改个结构,全崩。现在我只要看到嵌套超过三层,就立刻问自己:是不是该抽个新 class?
  • 用标签名当样式钩子ul li a { color: #333; } —— 表面看没问题,但哪天产品说“这个列表里要加个 icon 图标”,你加个 ,它也会被上面这条样式影响。稳妥做法是:所有可样式化的元素,必须有 class。
  • 直接写内联样式或 style 属性:曾经有同事为了快速改个颜色,直接写 style="color: red",然后忘了删。上线后用户反馈“标题全红了”,我们翻了二十个文件才定位到那一行。现在我团队约定:除非是动画临时态(比如 loading 时的 opacity 变化),否则禁用内联样式。

实际项目中的坑

第一坑:伪类顺序。我写了好几年 :hover,直到某次 a:hover 不生效,才发现它被 a:visited 盖住了。浏览器对 :link:visited:hover:active 有固定顺序要求(LVHA),记不住?那就统一写成:

a {
  color: #007bff;
}

a:visited {
  color: #5a5a5a;
}

a:hover {
  color: #0056b3;
  text-decoration: underline;
}

a:active {
  color: #004080;
}

第二坑:移动端的 rem 基准。我一开始用 document.documentElement.style.fontSize = window.innerWidth / 375 * 16 + 'px',结果 iOS Safari 横屏切回来,字体炸了。后来改用 viewport + media query 控制根字号,稳定多了:

html {
  font-size: 16px;
}

@media screen and (min-width: 375px) {
  html { font-size: calc(100vw / 375 * 16); }
}

@media screen and (min-width: 414px) {
  html { font-size: calc(100vw / 414 * 16); }
}

第三坑:CSS 变量和主题切换。我们有个后台系统要支持暗色模式,本来想着用 :root 里一堆 --color-bg,结果发现某些第三方组件(比如日期选择器)压根不认变量,硬改又怕升级出问题。最后妥协方案:只在业务组件里用 CSS 变量,第三方组件统一套一层 wrapper class,靠 class 切换预设好的暗色 class 包,比如 .theme-dark .datepicker。虽然不够优雅,但上线一周没出问题,我就当它成功了。

字体、重置和一点点取舍

我不用 Normalize.css 全量引入,太重。我只抄它最关键的几行:表单控件默认样式归一、图片默认不撑容器、buttoncursor: pointer 加上。其他像 precode 的样式?按需加,项目里根本没几个 pre

字体方面,我本地开发用系统字体栈,线上加个兜底:

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
}

不用 Web Font?不是不想,是实测加载慢+ FOIT 问题严重。客户等不及那个“优雅降级”,他们只关心“点进去能不能看清字”。所以现在除非设计强制要求,否则我一律跳过。

还有个细节:我从不写 * { box-sizing: border-box }。听起来很省事,但它会把 textareaselect 等原生控件也卷进来,有些浏览器下表现诡异。我只写 *, *::before, *::after —— 不,等等,其实我连这个都不写。我直接在每个需要的组件里显式写 box-sizing: border-box。看起来啰嗦,但改起来心里踏实。

结尾

以上是我这几年在 CSS 规范上踩出来的经验,不是教科书,也没有银弹。有些方案不是最优,但足够稳;有些妥协不是因为懒,而是上线时间卡在那儿,你得选个能快速验证、回滚成本低的路。

比如那个暗色模式的 wrapper 方案,我知道有更好的——用 Shadow DOM 隔离、或者自研一套 CSS-in-JS 主题系统。但我没选,因为当前项目没那人力,也没那必要。

如果你有更好的解法,特别是处理第三方组件主题兼容、或者大规模 CSS 拆分治理的经验,欢迎评论区交流。这个主题我还会继续写,比如“如何让老项目渐进式接入 CSS Modules”,或者“PostCSS 插件链怎么配才不翻车”——反正坑还多着呢,咱慢慢填。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论