사이드바 레이아웃

Sidebar layout

고정 폭 사이드바 + 나머지를 채우는 본문. 미디어 쿼리 없이 컨테이너가 좁아지면 자동으로 세로로 쌓입니다.

다른 이름: Every Layout Sidebar
···
html
<div class="stage" id="stage">
  <div class="sidebar-demo">
    <div class="side">Sidebar<br><span>flex-basis: 12rem</span></div>
    <div class="content">Content<br><span>flex-grow: 999; min-width: 50%</span></div>
  </div>
</div>
css
.stage{width:26%;min-width:90px;max-width:92%;transition:width 2.8s cubic-bezier(.4,0,.2,1)}
.stage.wide{width:92%}
.sidebar-demo{display:flex;flex-wrap:wrap;gap:10px;width:100%}
.side{flex-basis:74px;flex-grow:1;background:var(--accent);color:#fff;border-radius:8px;
  display:grid;place-items:center;text-align:center;font-size:10px;font-weight:700;min-height:64px;padding:6px}
.content{flex-basis:0;flex-grow:999;min-width:50%;background:var(--surface);border:1px solid var(--line);
  border-radius:8px;display:grid;place-items:center;text-align:center;font-size:10px;font-weight:700;
  color:var(--muted);min-height:64px;padding:6px}
.side span,.content span{display:block;font-weight:400;font-size:8px;opacity:.85;margin-top:4px}
js
const stage = document.getElementById('stage');
setInterval(() => stage.classList.toggle('wide'), 2400);

보통 "사이드바 + 본문"을 반응형으로 만들려면 `@media (max-width: 768px) { flex-direction: column }`처럼 특정 화면 폭을 하드코딩합니다. 이 레이아웃이 언제 쌓여야 하는지는 사실 뷰포트 크기가 아니라 "사이드바와 본문이 각자 최소한으로 필요한 폭의 합"에 달려 있는데, 미디어 쿼리는 그 사실을 모릅니다.

Every Layout의 트릭은 세 가지 선언만으로 이 판단을 CSS 자신에게 맡깁니다: 부모에 `display: flex; flex-wrap: wrap`, 사이드바에 `flex-basis: 12rem`, 본문에 `flex-basis: 0; flex-grow: 999; min-width: 50%`. 본문의 `flex-grow: 999`는 공간이 남을 때 그 공간을 거의 전부 가져가라는 뜻이고, `min-width: 50%`는 "내가 쓸 수 있는 폭이 컨테이너의 절반보다 좁아지면 다음 줄로 넘어가겠다"는 뜻입니다. 두 조건이 부딪히는 지점에서 flex-wrap이 자동으로 줄바꿈을 일으킵니다.

이 방식의 장점은 "얼마나 좁아지면 쌓이는가"가 숫자 하나(`min-width`의 %)로 컨테이너 자체 크기에 비례해 정해진다는 점입니다. 그래서 같은 사이드바 컴포넌트를 전체 화면 폭에 놓든 좁은 모달 안에 놓든, 각자의 문맥에 맞게 알아서 반응합니다 — 이는 컨테이너 쿼리가 풀기 전부터 있었던, 컨테이너 자체를 감지하는 오래된 순수 CSS 해법입니다.

언제 쓰나

앱 셸의 내비게이션 사이드바, 글 옆의 목차, 상품 상세의 옵션 패널처럼 "본문보다 좁고 고정 폭에 가까운" 보조 영역에 씁니다. 사이드바가 항상 떠 있어야 한다면(스크롤에 안 딸려가야 한다면) `position: sticky`를 함께 씁니다.