1. 先别急着写CSS,把这个组件的渲染约束想清楚
这一期是CSS3专家级编程系列第五篇。我不打算继续罗列属性,而是想聊一个更实在的话题:当你面对一个看起来不复杂的响应式卡片列表,怎么用CSS3编程的思路把它写稳。很多人以为会几个布局属性就叫会CSS,其实真正拉开差距的,是你能不能提前把所有可能会出现的问题想清楚,然后让代码自己约束住边界。
我这里的场景很典型:一个商品卡片目录。桌面端一屏能放四到五列,平板变三列,手机变成一列;每张卡片内部有标题、摘要、价格、按钮,空间充裕的时候按钮和价格可以排在同一行,空间紧张就必须竖着堆。这类组件几乎每个网站都有,但恰恰是最容易被margin、padding打成补丁的地方。
为什么单拿它出来讲?因为它的所有复杂度都集中在两个层面:第一,外层用什么样的格子算法来分配列,才能在不同宽度下不溢出、不缺口;第二,当卡片本身的宽度变化时,内部细节应该以谁的宽度为准来响应。后一个问题用视口媒体查询很难彻底解决,因为卡片宽度不一定跟随视口一起变。这是CSS3编程里最容易被忽视的地方,也是这次我想重点解决的。
我会拿一套实际可用的代码从头走一遍,顺便把容器查询、Grid轨道算法、层叠上下文、渲染性能这些平时容易含糊的概念一次说透。适合两类人看:一类是在项目里写过很多CSS但总觉得到处在打补丁的人,另一类是能把页面写出来、但一调动画就出现闪白、一滚动就卡顿,想知道原因的人。下面内容不挑框架,只要你用的是现代浏览器,照着落地就行。
1.1 从一次“看着简单却改了一晚上”的栅格复盘开始
前阵子我帮朋友看一个列表页,现象非常典型:页面在1920px宽下正常,缩到1366px还勉强能看,再往下一到平板,第三张卡片就直接掉到第二行并且宽度撑满整行,和前面两张卡片的宽度对不上,看起来非常“突”。检查代码后发现,外层用的是flex布局,每个卡片设置了flex: 1 1 280px,因为flex在换行时不会自动收缩到网格轨道的整数倍,最后一行怎么排都会露出破绽。
如果你也遇到过这个问题,先记住一个结论:当你要做的是一个等宽卡片自动换行的布局,默认用Grid而不是flex。Grid的auto-fill或auto-fit轨道能保证每个轨道宽度一致,换行后每张卡片依然落在对齐的轨道上,不会出现某一行比其他行宽一点的情况。
这不是说flex不行,而是flex的定位是“一维上的排布与对齐”,它擅长处理单行内的伸缩,不擅长处理多行时行与行的轨道对齐。做整页的多列布局,CSS Grid是一开始就该考虑的工具。很多项目里所谓“复杂问题”,其实就是选错了布局模型,然后一路用子元素拆行、负margin、max-width去弥补,最后越补越乱。
1.2 高手写CSS和补丁选手的差别在哪
我复盘过很多类似问题,发现一个规律:代码写得很忙的人,习惯把CSS当成“装修”来干,页面出一块就量一处、补一处。而真正稳的写法,是先考虑约束。
约束来自几个方面。第一是容器宽度,也就是用户视口或父级组件给了你多少可用空间,这是决定几列的主要变量;第二是内容固有宽度,卡片内部标题再长也不能无限撑开,图片按什么比例缩放,按钮最少要留多少宽度;第三是间距系统,卡片与卡片之间的gap必须是统一计算的,不能这里14px那里9px。
这三层约束没在写代码之前定下来,后面一定会看到碎片化的样式。比如图片设置了固定宽度却忘了比例,标题超出后不换行把卡片撑破,按钮文字变长后把同行价格挤走。这些问题的本质都不是“样式写少了”,而是没有明确“这个组件在什么尺寸范围内会变成什么样子”。
所以我写样式前会在脑子里过一遍约束表:这个组件的宽度范围是多少;几个关键临界点分别是什么;每个临界点我要改变的是哪几个属性,哪些属性应该保持不变。接下来的内容,就按这个思路逐步展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Flex 还是 Grid?这里给出一份可直接照抄的判断路径
这一节先解决布局模型选型。判断标准我简化成一句话:只看一维排布,用flex;需要行列二维对齐,用Grid。 但实际项目里边界没那么清晰,我按经验再细分一下。
如果你是给卡片容器分列,希望列数随宽度自动变化,使用display: grid加repeat(auto-fill, minmax(...))。如果你的卡片数量固定,且只需要水平方向排列、垂直方向不要求严格对齐,用flex就能满足。如果卡片在每一行都要等高、列宽必须一致,不用犹豫,直接Grid。
还有一类情况是“混排错落”,常见于文章瀑布流或首页作品集,有人用flex实现瀑布流,最后发现卡片高度参差不齐就放弃了。其实那类布局不应该用一维flex去排列,而应该使用CSS多列布局或直接接受masonry插件的成本。这里不展开,只提醒一点:CSS3编程最忌讳“硬拿工具顶上”,布局模型选错,后面所有代码都是在跟浏览器较劲。
2.1 何时必须用Grid而不是Flex
我总结出三个典型的“必须Grid”信号,你在代码里只要碰到其中一个,就可以停止用flex硬撑。
第一个信号是列数需要跟随容器宽度自动变化,而且每一列宽度要相等。用flex实现时,你写的flex-basis只是一个理想的期望宽度,浏览器会在空间不足以放下下一项时折行,但折行后每项具体多宽受剩余空间分配影响,几乎不可能保证和第一行的每列完全一致。Grid的轨道则是明确的,你写auto-fill后轨道数量由浏览器计算,但每个轨道宽度都来自同一个minmax表达式,天然等宽。
第二个信号是卡片内部要分上下区域,并且每张卡片的某个区域需要横向对齐。比如所有标题行必须对齐、所有底部按钮条必须对齐。用flex做外层,卡片的高度由各自内容决定,很难让下方的按钮都贴在视觉同一条底线上。Grid可以通过让每一行等高的方式,让卡片的上下区域自动撑到相同高度,配合内部flex布局能把按钮固定在底部。
第三个信号是你要在代码里明确描述“左边是主内容区,右边是侧边栏,中间间距多少”,这种区域感用grid-template-columns配合命名区域最直观,flex不是解决区域划分的工具。
2.2 auto-fill 与 auto-fit 的差异,以及在实战里怎么选
几乎每个用Grid的人都会在这两个关键词上卡一次。表面看起来它们都能让轨道数量自动变化,区别只在于“轨道是否保留空位”。
auto-fill的意思是“只要宽度允许,就尽量多放轨道”,即使没有内容去填充那些轨道,空轨道也会被保留。auto-fit则是“优先填满容器”,放不下内容的多余轨道会被折叠,实际轨道会拉伸去占用空出来的空间。
拿一个容器宽度为980px、轨道最小260px、gap为20px举例。260px加gap后大约能放下三个轨道,总占用840px,剩余约120px。用auto-fill时,容器会继续再生成一个260px的空轨道,然后
