Grid 元素放置与区域
三种放置方式,先选对工具
| 方式 | 写法 | 适合 |
|---|---|---|
| 自动放置 | 什么都不写,按 DOM 顺序填格 | 卡片列表、相同结构的重复项 |
| 网格线放置 | grid-column / grid-row | 少量元素需要跨行跨列 |
| 命名区域 | grid-template-areas • grid-area | 页面级骨架,结构一目了然 |
按网格线放置元素
.item {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 2;
grid-row-end: 4;
}
简写:
.item {
grid-column: 1 / 3; /* 从第 1 条竖线到第 3 条,占 2 列 */
grid-row: 2 / 4; /* 从第 2 条横线到第 4 条,占 2 行 */
}
🔢 结束线本身不属于该元素。
1 / 3是「从 1 号线到 3 号线」,中间夹住了 2 个轨道,所以占 2 列而不是 3 列。差值就是跨度。
使用 span
不关心具体结束编号时,span 更好维护:
.item {
grid-column: span 2; /* 从自动位置开始,跨 2 列 */
grid-row: 2 / span 3; /* 从第 2 条横线开始,跨 3 行 */
grid-column: span 2 / -1; /* 右对齐到末尾,向左跨 2 列 */
}
实例:卡片列表中让第一个卡片变成“大卡片”:
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.grid > :first-child {
grid-column: span 2;
grid-row: span 2;
}
使用负数网格线
.header {
grid-column: 1 / -1; /* 横跨整个显式网格 */
}
-1 表示显式网格的最后一条线。好处是列数变了也不用改代码。注意:负数编号只针对显式网格,不包括后来生成的隐式轨道。
命名网格线
.layout {
display: grid;
grid-template-columns:
[full-start] 1fr
[content-start] minmax(0, 960px)
[content-end] 1fr
[full-end];
}
.article {
grid-column: content-start / content-end;
}
/* 通栏背景图破出内容区,横跨满屏 */
.hero {
grid-column: full-start / full-end;
}
这是实现「内容居中限宽 960px,但部分元素可以满屏」的经典方案,比嵌套 wrapper 更干净。
💡 如果线名以
-start/-end成对命名(如content-start/content-end),就会自动形成一个名为content的区域,可以直接写grid-column: content;。
使用命名区域布局
这是可读性最好的写法——CSS 里直接画出了页面长什么样:
.layout {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
规则:
- 每一行区域字符串的列数必须一致。
- 同名区域必须组成矩形,不能是 L 形或分离的两块。
- 用
.表示空单元格(多个点也行,如...)。 grid-area既可接收区域名,也可写作row-start / column-start / row-end / column-end。
带空格子的例子:
grid-template-areas:
"header header header"
"nav main . "
"footer footer footer";
响应式改写区域——只改一处,整个布局重排:
@media (max-width: 720px) {
.layout {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
}
}
这里
main被放到了sidebar前面,但 DOM 顺序没变。屏幕阅读器和键盘 Tab 仍然按 HTML 顺序走,因此要先保证 HTML 顺序符合阅读逻辑,再用 Grid 做视觉调整。
隐式网格与自动放置
当 Grid Item 数量超过显式轨道,或元素被放到显式网格之外,浏览器会自动创建隐式轨道。
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto); /* 隐式行至少 100px,内容多就长高 */
}
| 属性 | 作用 |
|---|---|
grid-auto-rows | 设置隐式行的尺寸 |
grid-auto-columns | 设置隐式列的尺寸 |
grid-auto-flow | 控制自动放置的方向与策略 |
.container {
grid-auto-flow: row; /* 默认,优先按行填充,不够就新增行 */
grid-auto-flow: column; /* 优先按列填充,不够就新增列 */
grid-auto-flow: row dense; /* 尝试回填前面的空洞 */
}
dense 的效果与代价
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: row dense;
}
.gallery .wide { grid-column: span 2; }
没有 dense 时,宽卡片放不下就会换行,前一行留下空洞;加上 dense 后,后面的小卡片会被拿上来填进空洞。
⚠️
dense会让视觉顺序与 DOM 顺序不一致,键盘焦点和屏幕阅读器仍遵循 DOM 顺序。只适合图库、瀑布流这类「顺序不重要」的内容,不要用在表单、列表导航上。
一个完整的后台页面骨架示例
<div class="admin">
<header class="admin-header">顶栏</header>
<nav class="admin-nav">侧边栏</nav>
<main class="admin-main">主内容</main>
<aside class="admin-aside">右侧面板</aside>
<footer class="admin-footer">底栏</footer>
</div>
.admin {
display: grid;
grid-template-columns: 220px minmax(0, 1fr) 280px;
grid-template-rows: 56px minmax(0, 1fr) 48px;
grid-template-areas:
"header header header"
"nav main aside"
"footer footer footer";
min-height: 100vh;
}
.admin-header { grid-area: header; }
.admin-nav { grid-area: nav; overflow-y: auto; }
.admin-main { grid-area: main; overflow-y: auto; min-width: 0; }
.admin-aside { grid-area: aside; }
.admin-footer { grid-area: footer; }
@media (max-width: 1024px) {
.admin {
grid-template-columns: 220px minmax(0, 1fr);
grid-template-areas:
"header header"
"nav main"
"footer footer";
}
.admin-aside { display: none; }
}
两个关键细节:minmax(0, 1fr) 防止主内容被长内容撑破;行上的 minmax(0, 1fr) 配合子元素 overflow-y: auto,才能实现「顶栏底栏固定、中间独立滚动」。