移动端栅格系统可借助CSS自定义属性与calc()实现灵活响应,通过--cols、--gutter、--span动态控制列数、水槽和跨度,并在:root中用媒体查询切换变量值,避免硬编码与冗余class。

移动端栅格系统不用套框架也能快速落地,关键在于用好 calc() 和 CSS 自定义属性(--*),避免写死百分比或重复媒体查询。
为什么不能直接写 .col-6 { width: 50%; }
硬编码宽度会失去灵活性:列数变、断点变、水槽变时,所有类都要重算重写;更麻烦的是,当需要支持 4/6/12 列混合切换(比如小屏 4 列、中屏 6 列),纯 class 维护成本爆炸。
用自定义属性把可变因子抽出来,calc() 负责实时运算:
-
--cols: 4控制当前总列数 -
--gutter: 12px控制水槽宽度 -
--span: 2表示占几列 - 单列宽度 =
calc((100% - (var(--cols) - 1) * var(--gutter)) / var(--cols)) - 元素总宽 =
calc(var(--span) * (100% - (var(--cols) - 1) * var(--gutter)) / var(--cols) + (var(--span) - 1) * var(--gutter))
calc() 算宽度时容易漏掉水槽补偿
常见错误是只算列宽乘以 span,却忘了中间的 span - 1 个水槽也要加进去。比如 4 列总宽 100%,水槽 12px,那 2 列实际应占:
立即学习“前端免费学习笔记(深入)”;
.col {
--cols: 4;
--gutter: 12px;
--span: 2;
width: calc(
var(--span) * (100% - 3 * var(--gutter)) / var(--cols) +
(var(--span) - 1) * var(--gutter)
);
}漏掉第二行的 +(var(--span) - 1) * var(--gutter),会导致两列之间间隙被压缩,视觉上“挤在一起”。
如何用一个 class 支持多断点动态列数
靠媒体查询配合 :root 下不同断点的变量覆盖,而不是为每个断点写一堆 class:
- 在
:root设默认值:--cols: 4; --gutter: 8px; - 在
@media (min-width: 768px)里改::root { --cols: 6; --gutter: 12px; } - 在
@media (min-width: 1024px)里再改::root { --cols: 12; --gutter: 16px; } - 所有
.col元素自动响应,无需额外 class 或 JS
注意:CSS 变量不支持在 calc() 中做条件判断,所以列数必须靠媒体查询切换,不能指望 calc() 自己“判断屏幕大小”。
移动端适配中 100% 的实际参照对象是谁
这是最容易被忽略的底层细节:100% 在 calc() 中始终相对于父容器内容区宽度(不含 padding),不是视口宽度,也不是祖先的 border-box 宽度。
这意味着:
- 如果父容器有
padding: 16px,那100%就是parent.innerWidth - 32px - 如果父容器设了
box-sizing: border-box,但没设width,它的100%仍按 content-box 计算 - 安全做法:确保栅格容器有明确的
width: 100%且无 padding,或把 padding 提前减进calc()
复杂点在于嵌套——子栅格的 100% 是相对于它的直接父容器,不是最外层 container。这点一旦错位,整行宽度就偏移。


















