✎ 修改左侧代码,右侧实时更新
🧪响应式卡片栅格标准写法
有时候页面上就想摆几张小卡片:数据看板、入口按钮、商品分类、快捷操作。
一开始屏幕宽,三张排一行;屏幕窄了,自动掉到下一行。最好别写一堆媒体查询,也别算宽度。
这段代码干的就是这个事:用 Flexbox 做一个会自动换行的卡片布局。
原代码很短,HTML 只负责放内容,CSS 负责排队、间距、样式。
html<!-- 外层容器:负责管理里面这些卡片怎么排 -->
<div class="grid">
<!-- 每一张卡片 -->
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
</div>这里的 .grid 不是 CSS Grid 的意思,只是类名叫 grid。
真正控制布局的是后面这句:
css.grid {
display: flex;
}可以把 .grid 想成一张桌子,里面的 .card 是几张卡片。
默认情况下,div 会一人占一整行,像排队买奶茶,每个人都站一排。加上 display:flex 以后,它们就变成横着坐在同一张桌子边上。
再看完整 CSS:
css/* 页面本身的底色和留白 */
body {
margin: 0; /* 去掉浏览器默认外边距 */
padding: 24px; /* 页面四周留一点空,不贴边 */
background: #0f172a; /* 深蓝黑背景 */
}
/* 卡片容器 */
.grid {
display: flex; /* 让里面的卡片横向排列 */
gap: 16px; /* 卡片之间留 16px 间距 */
flex-wrap: wrap; /* 一行放不下,就自动换到下一行 */
}
/* 单张卡片 */
.card {
flex: 1 1 160px; /* 能放大、能缩小,基础宽度 160px */
height: 120px; /* 卡片高度 */
border-radius: 14px; /* 圆角 */
background: linear-gradient(135deg, #38bdf8, #2563eb); /* 蓝色渐变 */
display: grid; /* 用 grid 只是为了居中文本 */
place-items: center; /* 水平垂直都居中 */
color: #fff; /* 白色文字 */
font-size: 24px; /* 字号 */
font-weight: 700; /* 加粗 */
}JS 是空的,因为这件事不需要 JavaScript。
js// 这里不需要 JS
// 卡片换行、拉伸、间距,全交给 CSS Flexbox这点挺重要。很多新手看到“响应式布局”,第一反应是写 JS 监听窗口宽度。其实像这种“宽了多放几张,窄了自动换行”的需求,CSS 本来就会做,没必要把事情搞复杂。
flex: 1 1 160px这段代码里,最关键的不是渐变,也不是圆角,而是这句:
css.card {
flex: 1 1 160px;
}它其实是三个值的缩写:
css.card {
flex-grow: 1; /* 有多余空间时,愿意变宽 */
flex-shrink: 1; /* 空间不够时,愿意缩小 */
flex-basis: 160px; /* 先按 160px 这个基础宽度来算 */
}用生活里的话说:
flex-basis: 160px:每张卡片先说,我理想座位宽度是 160px。flex-grow: 1:桌子还有空位的话,我可以多占一点。flex-shrink: 1:桌子挤了的话,我也能稍微让一让。flex-wrap: wrap:实在坐不下,就去下一排。所以它不是死板地写“每张卡片宽 160px”。
它更像是说:先按 160px 排,空间多了大家平分,空间不够就换行。
大概流转是这样:
txt容器宽度足够 ┌──────────────────────────────────────┐ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ 1 │ │ 2 │ │ 3 │ │ │ └──────┘ └──────┘ └──────┘ │ └──────────────────────────────────────┘ 容器变窄,一行放不下 ┌────────────────────┐ │ ┌──────┐ ┌──────┐ │ │ │ 1 │ │ 2 │ │ │ └──────┘ └──────┘ │ │ ┌──────────────┐ │ │ │ 3 │ │ │ └──────────────┘ │ └────────────────────┘
注意第三张为什么可能会变宽?
因为它掉到第二行以后,这一行只有它一张卡片,而 flex-grow: 1 又允许它吃掉剩余空间,所以它会撑满这一行。
很多卡片布局要的正是这个效果:不管一行有几张,都尽量把空间用满,看起来不空。
原始版本适合理解。真放进项目,我一般会把数据和样式拆清楚:卡片内容来自数组,页面只负责渲染。这样以后从 3 张变成 8 张,不用复制粘贴一堆 <div class="card">。
TS 版可以这样写,适合 React 项目。
tsxtype CardItem = {
id: number;
title: string;
};
const cards: CardItem[] = [
{ id: 1, title: '订单' },
{ id: 2, title: '用户' },
{ id: 3, title: '收入' },
];
export default function FlexCards() {
return (
<div className="page">
<div className="cardList">
{cards.map((card) => (
<div className="card" key={card.id}>
{card.title}
</div>
))}
</div>
</div>
);
}配套 CSS:
css.page {
min-height: 100vh;
box-sizing: border-box;
padding: 24px;
background: #0f172a;
}
.cardList {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 160px;
height: 120px;
border-radius: 14px;
background: linear-gradient(135deg, #38bdf8, #2563eb);
display: grid;
place-items: center;
color: #fff;
font-size: 24px;
font-weight: 700;
}如果你不用框架,普通 JS 也够。
html<div id="app"></div>jsconst cards = [
{ id: 1, title: '订单' },
{ id: 2, title: '用户' },
{ id: 3, title: '收入' },
];
const app = document.querySelector('#app');
app.innerHTML = `
<div class="page">
<div class="cardList">
${cards
.map((card) => `<div class="card">${card.title}</div>`)
.join('')}
</div>
</div>
`;CSS 还是同一份:
css.page {
min-height: 100vh;
box-sizing: border-box;
padding: 24px;
background: #0f172a;
}
.cardList {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.card {
flex: 1 1 160px;
height: 120px;
border-radius: 14px;
background: linear-gradient(135deg, #38bdf8, #2563eb);
display: grid;
place-items: center;
color: #fff;
font-size: 24px;
font-weight: 700;
}这里还有个小细节:我把 body 上的样式挪到了 .page。
原因很简单,项目里 body 往往是全局的,你在一个组件里改 body,可能会影响别的页面。写 demo 可以随手写,进项目还是收一收,别污染全局。
这种写法适合“卡片数量不固定,每张最小宽度差不多,能自动换行”的场景。
比如:
但它不是万能的。
如果你要的是严格的二维布局,比如“永远三列,每列对齐,每个格子位置很固定”,CSS Grid 会更顺手。
css.cardList {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}如果你想要“手机 1 列、平板 2 列、桌面 4 列”,Grid 也更直白。
css.cardList {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
}还有一种情况,只有两三张固定卡片,页面也不会响应式变化。那直接写宽度也没什么丢人的,没必要为了显得高级,强行上复杂布局。
这段 Flexbox 卡片布局,最值钱的其实就两句:
cssdisplay: flex;
flex-wrap: wrap;再加上:
cssflex: 1 1 160px;卡片就有了“能排、能挤、能换行、能撑满”的能力。
你项目里这种卡片列表一般怎么写?是 Flex 一把梭,还是直接上 Grid?如果你遇到过“最后一张卡片撑满一整行,看起来怪怪的”这种问题,也可以把场景丢出来,下一篇可以专门讲怎么处理最后一行的卡片宽度。