用 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) 实现响应式定价卡片,需配合 grid-column: 1 / -1 和 align-items: start 防错位;Flex 因一维限制无法可靠对齐多层内容,而 auto-fit 可动态收缩轨道避免留白,卡片内部应以 flex column + margin-top: auto 确保按钮底对齐。

直接用 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)) 就能实现真正可用的响应式定价卡片对比,不需要 JS,也不必为每个断点写死列数——但必须配合 grid-column: 1 / -1 和 align-items: start,否则标题错位、按钮飘移、高度不一致全是必然结果。
为什么不能用 flex 做 Pricing Table 对比
Flex 是一维布局,强行让它处理“横向多列 + 纵向分层(标题/功能/价格/按钮)”时,会暴露三个硬伤:
- 不同卡片功能项行数不同时,
align-items: stretch在 Safari 和部分安卓 WebView 中完全失效,底部按钮无法对齐 - 换行后卡片 DOM 顺序和视觉顺序错位,比如“企业版”卡片的
.price可能跑到第二行,和它上面的h3脱节 - 无法跨列统一控制某一层(如所有价格区域)的高度或对齐方式,只能靠 JS 补救或牺牲语义加 wrapper
auto-fit + minmax() 的实际行为与常见误用
这个组合不是“智能猜列数”,而是浏览器按规则计算轨道:每列最小宽度由 minmax() 第一个参数决定,空轨道是否保留由 auto-fit 控制。关键差异如下:
-
auto-fit:只生成有内容的轨道,剩余空间均分给现有列 —— 小屏下真就一列,不会留白 -
auto-fill:预留所有可能轨道,哪怕没内容也会撑开空白列 —— 卡片少时下方大片留空,视觉失衡 -
repeat(3, 1fr):强制三列,窄屏下卡片被压缩、文字换行、按钮变形,且触发横向滚动 - 别在
minmax()里写min-width: 300px在卡片上 —— Grid 会忽略它,应改用min-width: 0防止子元素撑破约束
卡片内部如何保证功能区撑高、按钮底对齐
靠 align-items: stretch 外部拉伸不可靠,尤其当卡片内有图片、margin-top: auto 或父容器无显式高度时。更稳的做法是卡片自身用 display: flex:
立即学习“前端免费学习笔记(深入)”;
- 根元素设
display: flex; flex-direction: column; height: 100% - 价格区域设
flex: none(避免被拉伸) - 功能列表区域设
flex: 1(自动占满剩余空间) - CTA 按钮外层容器加
margin-top: auto,而不是直接加在按钮上 - 绝对不要写
height: 100%在卡片上 —— 若其父容器(Grid 容器)没有可计算高度上下文(如min-height或内容撑开),Safari 会算出 0
标题/价格/功能块跨整行对齐的写法细节
列数变化时,“专业版”标题突然跳到第二行,根本原因不是样式没写好,而是网格轨道和 DOM 顺序没对齐。必须让同类区块强制跨整行:
- 所有
.pricing-title加grid-column: 1 / -1(不是1 / 3或1 / span 2) - 所有
.price、.features、.cta同样加grid-column: 1 / -1 - 如果用
grid-template-areas,小屏断点只需改值为"header" "price" "features" "cta",不用动 HTML -
grid-area值里不能有多余空格,grid-area: "header "会彻底失效
最常被忽略的一点:Grid 容器本身得有高度上下文才能触发 stretch,而卡片内部的 flex: 1 又依赖父容器高度 —— 这个链路一旦断裂,所有对齐都会崩。检查顺序应该是:Grid 容器有没有 min-height 或内容撑开 → 卡片有没有 height: 100% 或 min-height: 0 → 功能区有没有 flex: 1 且没被 overflow: hidden 截断。


















