如何让Grid布局容器自动填充侧边栏折叠后腾出的空间?
问题
我在外层2×2的CSS Grid布局中混合使用了Grid与Flexbox:左下角是包含2×2 Grid的可折叠侧边栏,右下角是3×2 Grid布局的主容器。当侧边栏完全向左折叠后,希望主容器能向左拉伸占据腾出的空间,同时自动调整内部元素大小,但目前实现不了。我知道main容器上的flex-grow:1无效,但不知道正确的解决方法。
现有代码
HTML
<html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" crossorigin="anonymous"> <link rel="stylesheet" type="text/css" href="styles.css" /> </head> <body class="body"> <section class="logo"> <img class="img" src="../static/images/logo.png"> </section> <header class="header"> <div class="head-card"><span class="notification">[ some msg ]</span></div> <div class="head-card"> </div> </header> <!-- Collapsible Side Nav bar for menus --> <section id="aside" class="side-bar"> <div class="side-card"> <div class='accord-button'> <input type='checkbox' id='accordianinput'> <label for='accordianinput'> <i class="fa-solid fa-arrow-circle-left not-active"></i> <i class="fa-solid fa-arrow-circle-right active"></i> </label> </div> </div> <div class="side-card"> <ul class="nav"> <li><button type="button" class="menu-button" onclick="#">Home</button></li> <li><button type="button" class="menu-button" onclick="#">About</button></li> </ul> </div> <div class="side-card"> <span class="long-copyright">© Runaway Company, Inc.</span> <span class="short-copyright">©RC</span> </div> </section> <main class="main"> <div class="card"> </div> <div class="card"> </div> <div class="card"> </div> <div class="card"> </div> <div class="card"> </div> <div class="card"> </div> </main> </body> </html>
CSS
* { box-sizing: border-box; } .body { height: 100vh; width: 100vw; margin: 0; display: grid; grid-template-columns: 20% 1fr; grid-template-rows: 10% 1fr; grid-template-areas: "logo head" "side main"; } .logo { background-color: #fff; grid-area: logo; display: flex; flex-flow: row nowrap; justify-content: flex-start; align-content: center; } .img { height: 100%; object-fit: cover; } .header { background-color: #fff; padding: 0.3rem; grid-area: head; display: grid; grid-template-columns: 2fr 1fr; grid-template-rows: 1fr; grid-template-areas: "msg regoraccess"; gap: 0.5em; } .head-card { border-radius: .25rem; display: flex; flex-flow: row wrap; justify-content: center; align-content: center; } .head-card:nth-child(1) { grid-area: msg; } .notification { text-align: center; margin: 0; } .head-card:nth-child(2) { grid-area: regoraccess; display: flex; flex-flow: row wrap; justify-content: flex-end; align-content: center; } .side-bar { background-color: #1c1f23; padding: 0.1rem 0 0.1rem 0.1rem; grid-area: side; display: grid; grid-template-columns: 0.05fr 0.95fr; grid-template-rows: 0.9fr 0.1fr; grid-template-areas: "collapse menu" "rc rc"; } .side-card { display: flex; } .side-card:nth-child(1) { grid-area: collapse; display: flex; flex-flow: row wrap; justify-content: flex-end; align-content: center; } .fa-solid { color: #3DED97; } .accord-button>input { display: none; } .accord-button>input:checked+label .not-active { display: none; } .accord-button>input:not(:checked)+label .active { display: none; } .side-card:nth-child(2) { grid-area: menu; display: flex; flex-flow: column wrap; justify-content: flex-start; align-content: flex-start; transition: width 300ms; } .nav { display: flex; flex-flow: column wrap; padding: 1rem; gap: 1rem; } .nav li { display: flex; } .menu-button { background: transparent; border: 0; color: white; } body:has(.accord-button > input:checked) { #aside { width: 1em; } .side-card:nth-child(2) { display: none; } .side-card:nth-child(3) { width: 1em; } .long-copyright { display: none; } } body:has(.accord-button > input:not(:checked)) { .short-copyright { display: none } } .side-card:nth-child(3) { grid-area: rc; display: inline-flex; flex-flow: row wrap; justify-content: flex-start; align-content: flex-end; } .long-copyright { color: #D3D3D3; font-size: 1rem; } .short-copyright { color: #D3D3D3; font-size: 0.5rem; } .main { background-color: #c3c5ca; padding: 0.5rem; grid-area: main; display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; grid-template-areas: "col-1 col-2 col-3" "col-4 col-4 col-5" "col-4 col-4 col-6"; gap: 1em; flex-grow: 1; /* 无效属性 */ } .card { background-color: #f6f7f9; border-radius: 1rem; } .card:nth-child(1) { grid-area: col-1; } .card:nth-child(2) { grid-area: col-2; } .card:nth-child(3) { grid-area: col-3; } .card:nth-child(4) { grid-area: col-4; } .card:nth-child(5) { grid-area: col-5; } .card:nth-child(6) { grid-area: col-6; } @media (max-width: 900px) { .main { grid-template-columns: 1fr; grid-template-rows: repeat(6, 1fr); grid-template-areas: initial; } .card { grid-area: initial !important; } }
解决方案
问题核心是外层Grid的列宽度是固定的20% 1fr,侧边栏折叠时只是修改自身宽度,但外层Grid的列定义没变化,所以主容器无法扩展。只需要修改外层Grid的列规则,配合:has()选择器实现折叠时的布局调整:
修改步骤
- 给外层Grid添加过渡动画,让布局变化更平滑
- 折叠状态下重写外层Grid的列宽,把侧边栏列设为
min-content(仅占内容所需最小宽度),主容器列保持1fr占据剩余空间 - 移除
main上无效的flex-grow:1,Grid容器的子元素是Grid项,该属性完全无效 - 调整侧边栏折叠时的自身样式,让宽度自适应内容,避免固定宽度导致的布局错位
修改后的关键CSS代码
/* 修改外层Grid,添加过渡动画 */ .body { height: 100vh; width: 100vw; margin: 0; display: grid; grid-template-columns: 20% 1fr; grid-template-rows: 10% 1fr; grid-template-areas: "logo head" "side main"; transition: grid-template-columns 300ms; /* 新增:平滑过渡 */ } /* 折叠状态下修改外层Grid列宽 */ body:has(.accord-button > input:checked) .body { grid-template-columns: min-content 1fr; /* 侧边栏列仅占内容宽度 */ } /* 移除main上的无效属性 */ .main { background-color: #c3c5ca; padding: 0.5rem; grid-area: main; display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; grid-template-areas: "col-1 col-2 col-3" "col-4 col-4 col-5" "col-4 col-4 col-6"; gap: 1em; /* flex-grow: 1; 删掉这行 */ } /* 调整侧边栏折叠时的样式 */ body:has(.accord-button > input:checked) { #aside { width: auto; /* 改为自适应内容 */ } .side-card:nth-child(2) { display: none; } .side-card:nth-child(3) { width: auto; /* 改为自适应内容 */ } .long-copyright { display: none; } }
原理说明
- 外层Grid的列定义是控制整体布局的核心,折叠时修改
grid-template-columns,让侧边栏列只占按钮的宽度,主容器列自动填充剩余空间 min-content会根据侧边栏内的折叠按钮宽度自动调整,避免固定宽度导致的适配问题- Grid布局中,
1fr会自动分配剩余空间,所以主容器会自然拉伸,内部的Grid项也会跟着自动调整大小
内容的提问来源于stack exchange,提问作者user1931485
相关产品推荐
相关产品推荐

