card 是 block,但仅负责外层容器结构;card-header等不是block,应由布局系统驱动;price-tag、rating-stars等可复用元素才该是独立block。

卡片组件用 BEM 实现高内聚,核心是把「视觉区块」和「职责边界」对齐——不是所有带阴影圆角的容器都叫 card,只有真正能独立存在、复用、测试的才该是一个 block。
card 是 block 还是 element?
常见错误是把整个卡片写成 card block,然后塞进 card__title、card__image、card__footer,结果发现 card__footer 在弹窗里也要用,但样式被 card 的 margin 或 padding 锁死了。
正确做法:只在语义上不可再分、且能脱离上下文复用的部分才定义为 block。比如:
-
card本身只负责外层容器结构(如 border、shadow、border-radius),不控制内部排版 -
card-header、card-body、card-footer都不该存在——它们只是 layout 容器,应由父级布局系统(如 Flex/Grid)驱动,而非 BEM 命名 - 真正需要独立封装的,是像
avatar、badge、chip这类跨组件复用的元素,它们才该是 block
如何拆出真正内聚的子 block?
看一个真实例子:电商商品卡片里常有价格、评分、操作按钮。如果全塞进 card,改评分图标就得动 card 样式;换按钮文案又要改 card__action ——耦合就来了。
立即学习“前端免费学习笔记(深入)”;
更合理的拆法:
-
price-tag:自己管货币符号位置、折扣线、是否禁用态 -
rating-stars:封装 SVG 渲染逻辑、hover 交互、aria-label 生成 -
cta-button:统一处理 loading 状态、尺寸响应、焦点轮廓
这些 block 各自带完整样式与状态,card 只通过 gap / padding 控制间距,不参与它们的视觉实现。
modifier 别滥用,状态优先走属性或 JS 控制
别写 card--featured、card--compact 这类 modifier 来覆盖布局或颜色——它会让 card 越来越重,最终变成“什么都能干但什么都干不好”的巨石组件。
推荐替代方案:
- 尺寸/密度变化 → 用 CSS 自定义属性控制,比如
--card-gap: 1rem,由父容器注入 - 主题色切换 → 用
data-theme="dark"配合 :where() 选择器,不污染 block 名 - 加载态/禁用态 → 直接加
disabled属性,用[disabled]选择器接管,比card--loading更语义、更可预测
为什么 card__image 不该存在?
因为 card__image 暗示这个图必须依附于 card 才有意义,但现实中头像、商品主图、封面图都可能复用于列表项、侧边栏甚至弹窗。一旦写死在 card 下,就等于放弃了复用能力。
应该做的是:
- 单独定义
image-placeholderblock,处理加载失败 fallback、宽高比、object-fit - 让
card只声明display: flex或grid-area,把图像区域留给子 block 自行填充 - 若需响应式裁剪逻辑,抽成
aspect-ratio-box,而不是塞进card__image
真正难的不是命名,而是判断某个 UI 片段是否具备“独立生命周期”——它能否在不修改其他代码的前提下,被复制粘贴到新页面中直接运行。没通过这个检验的,就还不是 block。


















