如何创建响应式完美矩形网格?解决半步网格布局异常
可用演示
以下是CSS实现响应式完美矩形网格的演示,无论屏幕宽度如何,网格始终保持完美矩形。
body { max-width: 900px; } .grid-1 { display: grid; grid-template-columns: repeat(auto-fill, 100px); gap: 5px; max-width: 100%; width: fit-content; overflow: hidden; } .grid-1>* { background-color: green; width: 100px; height: 100px; } .grid-2 { display: grid; grid-template-columns: repeat(auto-fill, 100px); gap: 5px; width: 1000px; } .grid-2>* { background-color: red; width: 100px; height: 100px; }
<div class="grid-1"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div class="grid-2"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> </div> </div>

异常演示
当把行列尺寸设为原尺寸的1/2,并让网格项占据2行2列时,问题出现了:特定窗口宽度会导致网格最后一项变成半宽单元格,破坏完美矩形。
body { max-width: 900px; } .grid-1 { display: grid; grid-template-columns: repeat(auto-fill, 50px); grid-template-rows: repeat(auto-fill, 50px); gap: 5px; max-width: 100%; width: fit-content; overflow: hidden; } .grid-1>* { background-color: green; width: 100px; height: 100px; grid-column: span 2; grid-row: span 2; } .grid-2 { display: grid; grid-template-columns: repeat(auto-fill, 100px); gap: 10px; width: 1000px; } .grid-2>* { background-color: red; width: 100px; height: 100px; }
<div class="grid-1"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div class="grid-2"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> </div> </div>

使用这种方法时,半数情况下整体网格的最后一项会变成半宽红色网格项。需要找到纯CSS方案消除该问题,实现半步网格下的响应式完美矩形,同时保留所有半列、不限制列数为2N。
补充演示说明
以下是更详细的演示,说明为何需要半步网格:希望右上角的tile与其他tile之间有可见间隙,通过半步网格可以减小间隙尺寸,同时不牺牲绿色/红色tile可填充的额外列数。
:root { --grid-gap: 0.4rem; --grid-division: 2; /*Integer*/ --tile-base-size: 9.5rem; --tile-size: calc( var(--tile-base-size) - ( (var(--grid-division) - 1) * var(--grid-gap) ) ); } .overlay { grid-column-start: calc(-1 - var(--grid-division)); grid-column-end: -1; grid-row-start: 1; grid-row-end: calc(1 + var(--grid-division)); background-color: blue; } .filler { grid-column-start: calc(-2 - var(--grid-division)); grid-column-end: -1; grid-row-start: 1; grid-row-end: calc(1 + 2 * var(--grid-division)); } body { max-width: 900px; } .grid-1 { display: grid; grid-template-columns: repeat(auto-fill, calc(var(--tile-size)/var(--grid-division))); gap: var(--grid-gap); max-width: 100%; width: fit-content; overflow: hidden; margin: 0 auto; } .tile { background-color: green; width: var(--tile-base-size); height: auto; aspect-ratio: 1; grid-column: span var(--grid-division); grid-row: span var(--grid-division); } .grid-2 { display: inline-grid; grid-template-columns: repeat(auto-fill, calc(var(--tile-base-size))); column-gap: calc(var(--grid-gap)); justify-content: start; width: 1000px; height: var(--tile-base-size); grid-row: span var(--grid-division); overflow: hidden; } .grid-2 > * { background-color: red; width: 100%; height: auto; aspect-ratio: 1; }
<div class="grid-1"> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="grid-2"> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> </div> <div class="filler"></div> <div class="overlay"></div> </div>
解决方案
核心问题是固定宽度设置导致tile在列数不足时被挤压,以及嵌套网格的宽度计算冲突。通过以下纯CSS调整可解决:
移除固定宽度,依赖Grid布局控制尺寸
去掉tile的width属性,完全通过grid-column: span var(--grid-division)和aspect-ratio:1保证正方形和正确宽度,避免固定宽度导致的挤压变形。优化嵌套网格的宽度控制
不给嵌套网格设置固定宽度,而是通过grid-column: span X让它占据父网格中指定数量的半列,确保内部tile能完整排列。调整变量计算逻辑
重新计算基础列宽,确保var(--grid-division)个基础列加上间隙的总宽度等于var(--tile-base-size):--tile-column-width: calc((var(--tile-base-size) - (var(--grid-division) - 1)*var(--grid-gap)) / var(--grid-division));
修改后的完整代码:
:root { --grid-gap: 0.4rem; --grid-division: 2; /* 可调整的整数分割数 */ --tile-base-size: 9.5rem; /* 计算基础列宽:确保N个列+N-1个间隙= tile基础尺寸 */ --tile-column-width: calc((var(--tile-base-size) - (var(--grid-division) - 1)*var(--grid-gap)) / var(--grid-division)); } .overlay { grid-column: span var(--grid-division) / -1; grid-row: 1 / span var(--grid-division); background-color: blue; } .filler { grid-column: span calc(2*var(--grid-division)) / -1; grid-row: 1 / span calc(2*var(--grid-division)); } body { max-width: 900px; } .grid-1 { display: grid; grid-template-columns: repeat(auto-fill, var(--tile-column-width)); gap: var(--grid-gap); max-width: 100%; width: fit-content; overflow: hidden; margin: 0 auto; } .tile { background-color: green; aspect-ratio: 1; grid-column: span var(--grid-division); grid-row: span var(--grid-division); } .grid-2 { display: grid; grid-template-columns: repeat(auto-fill, var(--tile-base-size)); gap: var(--grid-gap); /* 让嵌套网格占据4个半列(即2个完整tile宽度) */ grid-column: span calc(2*var(--grid-division)); grid-row: span var(--grid-division); } .grid-2 > * { background-color: red; aspect-ratio: 1; }
<div class="grid-1"> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="tile"></div> <div class="grid-2"> <div></div> <div></div> <div></div> <div></div> </div> <div class="filler"></div> <div class="overlay"></div> </div>
调整后,无论屏幕宽度如何变化,tile都会保持完整的正方形,不会出现半宽情况,同时保留了半列布局的灵活性,可继续使用overlay等元素占据半列空间。
内容的提问来源于stack exchange,提问作者thenickman100

