CSS网格生成器

填列数、最小轨道宽、gap、内边距与行高,即时输出 grid-template-columns 与可直接粘贴的网格 CSS,并按规范 7.2.3 的公式算出每列实得宽度、box-sizing 造成的溢出量。

轨道写法

4 列、min 180px、gap 16px、padding 16px:按 1200px 容器算出 6 列,可用宽 1088px,每列 181.33px。

grid-template-columnsrepeat(auto-fill, minmax(180px, 1fr))
按 1200px 容器实得列数6
每列实得宽度181.33 px
可用宽(容器 − padding − gap)1088 px
content-box 溢出量6 列 × 32px = 192 px
关于 1fr 与 box-sizing 的实测提醒1fr 不是「均分」:默认最小值是 auto(解析为 min-content),长 URL 或无空格英文会把轨道顶宽并溢出容器,写 minmax(0, 1fr) 才是修法。另外默认 box-sizing:content-box 时子项 padding 加在轨道宽之外,溢出量 = 列数 × padding×2,与容器宽无关。

使用方法

  1. 填「列数」(1 ~ 12,示例 4)。上限 12 是实用边界:超过 12 列后单列宽低于 80px,中文标题会被逐字换行。
  2. 填「最小轨道宽」(80 ~ 600 px,示例 180)。这个值只在 auto-fill / auto-fit 模式下生效,固定列数模式下会被忽略。
  3. 填 gap(0 ~ 80,示例 16)与容器 padding(0 ~ 60,示例 16)。这两个值直接吃掉可用宽,是最容易被忽略的部分。
  4. 选轨道写法:auto-fill、auto-fit、固定列数 1fr、或 minmax(0, 1fr)。四者的差别在下面「原理和变量」里逐条讲。
  5. 点「生成 CSS」(三个都是单行数字框,按 Enter 直接生成)。结果区会给出按 1200px 容器实算的列数、每列宽度与 content-box 溢出量。

这个 CSS网格生成器能解决什么问题

一是把「几列」换算成「该写多少 px」。多数人卡在 minmax() 的第一个参数上:想让 1200px 容器排 4 列,直觉答案是写 300px,但 gap 吃掉了 3 × 16 = 48px,写 300px 只会排出 3 列。实测反解区间是 228 ~ 288px(大于 227.2、不超过 288)——写 289px 就掉回 3 列。本页把这个区间和实得列数都算出来。

二是分清 auto-fill 与 auto-fit。这是两个最容易混的关键词:auto-fill 保留空轨道,auto-fit 把空轨道折叠为 0。实测在 1200px 容器、最小轨道 150px 下两者都算出 7 列,但只放 3 个子项时,auto-fill 每条仍是 157.71px(右侧空 630.9px),auto-fit 则塌成 3 条、每条 389.33px 撑满整行。子项数 ≥ 列数时两者完全等价。

三是诊断横向溢出。页面出现横向滚动条时,最常见的原因不是列数太多,而是默认 box-sizing: content-box:grid 分配给子项的是内容盒宽,padding 加在这个宽度之外。实测容器 1000px、padding 24px、gap 16px、卡片 padding 16px 时,4 列的溢出量正好是 4 × 32 = 128px,且与容器宽无关——2 列溢出 64px、6 列溢出 192px,线性于列数。

四是给「长串内容」一个不会撑破的写法。轨道写成 1fr 时最小值是 auto,会解析到 min-content;放一个 1400px 宽的 URL 时,容器 1200px 会被撑到 1616px(1.35 倍),横向滚动条立刻出现。改成 minmax(0, 1fr) 后最小值被显式压成 0,轨道恒为 984px,溢出交给 overflow 处理。

本页不做这些事:不生成 Flexbox 布局(那是CSS Flexbox布局生成器的事,两者的一维/二维模型不同)、不排版格式化已有 CSS(用CSS格式化美化)、不生成阴影与圆角。要阴影用CSS长方体阴影生成器,要圆角用CSS圆角生成器。

原理和变量

1. fr 的分配算法。规范里的 fr 是剩余空间的分数,不是「均分容器」。算法是:可用宽 = 容器宽 − padding − gap × (列数 − 1),然后每 fr = 可用宽 / fr 总数。实测 1200px 容器、gap 16px、三条 1fr:可用宽 = 1200 − 16 × 2 = 1168px,每列 1168 ÷ 3 = 389.33px,验算 3 × 389.33 + 32 = 1200 ✓。改成 2fr 1fr 1fr:每 fr = 1168 ÷ 4 = 292px,2fr 列得 584px、两个 1fr 列各 292px。

2. 1fr 与 minmax(0, 1fr) 的区别。裸写 1fr 时轨道的最小值是 auto,最终解析为 min-content(受自动最小尺寸规则影响)。所以在 grid-template-columns: 200px 1fr 里,如果第二格放的是不可断的长串,轨道会被 min-content 顶大。实测容器 1200px、第二格 min-content 为 1200px 时溢出 216px,为 1400px 时溢出 416px;写成 minmax(0, 1fr) 后轨道恒为 1200 − 200 − 16 = 984px,容器不再横向溢出。

3. 列数公式。repeat(auto-fill, minmax(MINpx, 1fr)) 的列数按 n = floor((容器宽 + gap) / (MIN + gap)) 算,最少 1。实测 MIN = 150、gap = 16 时:360px → 2 列、640px → 3 列、768px → 4 列、1024px → 6 列、1200px → 7 列、1440px → 8 列。注意 1024 → 6 列直接跳过了 5 列,因为 floor 的结果就是这样,不存在「5 列」这个中间态。

4. gap 与 padding 怎么吃掉列宽。在 1200px 容器、padding 24px 下:2 列时 gap 0 → 每列 576px、gap 16 → 568px;4 列时 gap 0 → 288px、gap 16 → 276px;6 列时 gap 0 → 192px、gap 16 → 178.67px。每列的损失是 16 × (列数 − 1) / 列数——列数越多损失越大,但总和恒为 1152px。

5. minmax() 的三种写法与失效条件。规范规定 minmax() 的最小值只能是 <length-percentage> 或 auto,不能是 fr,也不能是 min-content / max-content。所以 minmax(1fr, 1fr) 是无效 CSS,整条声明被丢弃——注意不是「回退到默认值」,是「这条规则不存在」。最大值则可以取 fr、min-content、max-content、none。auto-fill 与显式列数混用(repeat(3, 1fr) auto-fill)同样整条丢弃。

6. 为什么本页坚持显式断点。只用 auto-fit 时列数随容器宽连续变化,在 640px 与 1000px 之间会出现无法用媒体查询覆盖的中间态。规范推荐的写法是「默认 1 列 → @media(min-width:640px) 2 列 → @media(min-width:1000px) 4 列」,保证三个可预测、可截图对比的状态。360px 宽下容器 padding 16px × 2 = 32px,1 列内容宽 328px,无横向滚动。

完整示例

默认这组:列数 4、最小轨道 180px、gap 16px、padding 16px、行高 1.5、auto-fill 模式(数值仅作演示)。

下方表格里 4 列那行可以直接对照:gap 0 → 288.00px、gap 16 → 276.00px,每列少 12px。

输入范围与对照表

五个输入框的约束:列数 min="1" max="12" step="1"、最小轨道 min="80" max="600" step="1"、gap min="0" max="80" step="1"、padding min="0" max="60" step="1"、行高 min="1" max="3" step="0.1"。0 在 gap 与 padding 上是合法值(就是不要间隙),但列数下限是 1——repeat(0, 1fr) 在 CSS 里非法。

列数gap (px)容器 (px)可用宽 (px)每列宽 (px)每列少 (px)
2012001152576.000.00
21612001136568.008.00
3012001152384.000.00
31612001120373.3310.67
4012001152288.000.00
41612001104276.0012.00
5012001152230.400.00
51612001088217.6012.80
6012001152192.000.00
61612001072178.6713.33
8012001152144.000.00
81612001040130.0014.00
10012001152115.200.00
101612001008100.8014.40
1201200115296.000.00
1216120097681.3314.67

默认 12 行服务端直出,点「加载更多行」补到 16 行。容器固定 1200px、padding 24px,所以「可用宽」随列数与 gap 变化;4 列 + gap 16 那行标蓝,就是正文里引用的那组数。

几个常见误用的对照:

边界与精度

1. 反解「几列对应多少 px」的正确区间。由 n = floor((1200 + 16) / (MIN + 16)) = 4 得 4 ≤ 1216/(MIN+16) < 5,即 243.2 < MIN + 16 ≤ 304,MIN ∈ (227.2, 288]。实测 MIN = 240 → 4 列、MIN = 288 → 4 列、MIN = 289 → 只剩 3 列。所以「1200 / 4 = 300」是错的直觉答案。

2. content-box 的溢出量是列数的线性函数。容器 1000px、padding 24px、gap 16px、卡片 padding 16px 时实测:2 列溢出 64px、3 列 96px、4 列 128px、6 列 192px——都等于「列数 × 32px」,与容器宽无关。这解释了为什么「宽屏没事、窄屏有滚动条」或反之:变的其实是列数,不是容器。

3. minmax() 写错是静默失效。minmax(1fr, 1fr)、minmax(min-content, 1fr) 都违反规范,整条 grid-template-columns 声明被丢弃,元素退回单列(无模板的默认网格)。没有报错、没有回退值,所以本页输出的写法都是规范允许的组合。

4. 列数上限取 12 的理由。12 是 Bootstrap 的栅格上限。超过 12 列后单列宽低于 80px,中文标题(每字 20px)一行放不下 4 个字,会被逐字换行,实用价值为零。这不是规范限制,是排版可读性限制。

5. 容器宽按 1200px 固定计算。真实页面宽度随视口变化,所以「实得列数」是以 1200px 为基准的估算,用于横向比较不同参数组合。精确列数要用 ResizeObserver 量实际容器——本页不做,那需要常驻监听且在多栏布局里没有唯一答案。

6. 行高只影响子项,不影响轨道。line-height 加在 .grid > * 上,只改变文字行距与子项高度,不参与轨道宽度的计算。所以调行高不会改变「每列宽」那几格数字——这一点在响应式微调时经常被误解。

常见问题

auto-fill 和 auto-fit 到底差在哪?我该用哪个?

只差在空轨道怎么处理:auto-fill 保留空轨道(列数照算,子项按顺序从左往右排),auto-fit 把空轨道折叠成 0(元素会撑满)。实测 1200px 容器、MIN 150px 时两者都算出 7 列,但只放 3 个子项:auto-fill 每列 157.71px、右侧空 630.9px;auto-fit 塌成 3 列、每列 389.33px。元素数量会变化就用 auto-fit,数量固定或不确定用 auto-fill。子项数 ≥ 列数时两者完全等价。

为什么我写 minmax(300px, 1fr) 结果只有 3 列,不是预期的 4 列?

因为 gap 不在你的除法里。列数公式是 floor((容器宽 + gap) / (MIN + gap)),不是 floor(容器宽 / MIN)。1200px 容器、gap 16px、MIN 300 时:floor(1216 / 316) = floor(3.85) = 3。要 4 列,MIN 必须落在 228 ~ 288px。这是本页最主要的功能——把「我想要几列」换算成「该写多少 px」。

页面出现横向滚动条,但列数明明只有 4 列,为什么?

最可能是 box-sizing。默认 content-box 下,grid 分配给子项的是内容盒宽度,子项的 padding 加在这个宽度之外。实测容器 1000px、padding 24px、gap 16px、卡片 padding 16px 时,4 列溢出正好 128px(= 4 × 32),且与容器宽无关。修法是给子项加 box-sizing: border-box;给轨道写 minmax(0, 1fr) 只能解决 min-content 问题,解决不了 padding。

1fr 明明写了「平分」,为什么格子会被内容撑破?

因为 1fr 的最小值是 auto,最终解析为 min-content。在 200px 1fr 这种组合里,如果 1fr 那格放的是不可断的长串(URL、无空格的英文标识符),轨道会被顶大。实测容器 1200px、min-content 1400px 时,不写 minmax 会撑到 1616px(1.35 倍);写成 minmax(0, 1fr) 后轨道恒为 984px,溢出交给 overflow。所以凡是有长串内容的格子,轨道都该用 minmax(0, 1fr)。

我写了 minmax(1fr, 1fr),浏览器为什么不认?

因为规范规定 minmax() 的最小值不允许是 fr,也不允许是 min-content / max-content,只能是 <length-percentage> 或 auto。所以 minmax(1fr, 1fr) 是无效声明,整条 grid-template-columns 被丢弃——注意不是「回退到某个默认值」,是「这条规则不存在」,元素会退回无模板的默认单列。最大值才可以取 fr。这种静默失效最难查,所以本页输出的写法都限制在规范允许的组合内。

子项之间的 gap 是加在轨道之间还是轨道里面?

加在轨道之间,不占轨道宽度。所以 4 列 + gap 16px 的实际占用是 4 × 轨道宽 + 3 × 16——只有 3 个间隙,不是 4 个。这正是「1200/4 = 300」不对的原因:(1200 − 48) / 4 = 288px 才是每列实得宽度。行方向同理,gap 也可以写成 gap: 16px 24px(先行后列)两个值。

为什么不用 auto-fit 一把梭,还要写媒体查询?

因为 auto-fit 的列数随容器宽连续变化,会在两个断点之间产生大量无法枚举的中间态(比如 768px 时是 3 列、769px 时突然变 4 列),截图对比和回归测试都很难做。规范推荐「默认 1 列 → @media(min-width:640px) 2 列 → @media(min-width:1000px) 4 列」,好处是三个状态都可预测、可断言。代价是宽屏上浪费空间——所以实际项目里常两者混用:断点管大布局,auto-fit 管大布局内部的次级网格。

行高改了,为什么「每列宽」那几格数字一点没变?

因为行高不参与轨道宽度计算。轨道宽只由容器宽、gap、padding 和 fr 分配决定;line-height 加在 .grid > * 上,只影响文字行距与子项的高度。想改宽度就调列数、gap、padding 或最小轨道宽;想改高度才动行高。这两件事在响应式微调时经常被混在一起改,结果改了一处另一处不动。

相关工具