임동욱 임동욱 08-18
feat: AdminShell 레이아웃을 실제 CSS로 세우고 뷰포트 고정 스크롤 구조로 정리
셸은 `flex min-h-screen flex-col` 같은 Tailwind 클래스로만 잡혀 있었는데 Tailwind 제거
(e3d3133) 이후 그 클래스를 정의한 CSS가 없어 **배치가 하나도 적용되지 않고 있었다**. 헤더·
사이드바·본문·풋터가 그냥 블록으로 쌓여 사이드바가 본문 옆에 서지도 않았다.

@fox에는 넣지 않았다(사용자 확정) — 로고·프로필처럼 이 앱에만 있는 구성을 전제로 한 배치라
포터블 디자인시스템에 들어갈 성격이 아니다. 대신 여백·색·서체는 전부 @fox 토큰을 거친다.

- admin-shell.module.scss 신설. 뷰포트를 채우고 그 안에서만 스크롤한다 — 헤더는 항상 보이고
  사이드바와 본문이 각자 스크롤한다. 사이드바가 이미 `overflow-y: auto` 전제로 만들어져 있어
  (시안 높이 1251px이 뷰포트보다 크다) 행 높이가 뷰포트에 묶여야 그 스크롤이 동작한다.
- 본문 열에 `min-inline-size: 0`을 줬다. flex 자식 기본값이 auto라 넓은 표가 들어오면 줄지 않고
  사이드바를 밀어낸다.
- main 여백은 헤더의 좌우 여백과 같은 padding(8)=24px이라 세로 기준선이 맞는다.
- 풋터가 components/ui/footer를 떠나 셸이 직접 그린다 — 그쪽도 무스타일이라 그대로 쓰면
  테두리도 여백도 없다. 헤더가 components/ui/header를 떠난 것과 같은 이유다.

검증 — 1440×900에서 헤더 y0/64/전폭, 사이드바 y64(시안과 같은 위치)/240/잔여 높이,
본문열 x240/1200/자체 스크롤/흰 배경, main 여백 24, 풋터 border-top 1px·여백 16·24·
13px font-neutral-subtler·가운데 정렬. 페이지 자체는 스크롤되지 않고, 본문을 끝까지 내려도
사이드바 scrollTop은 0으로 유지된다(독립 스크롤).

Co-Authored-By: Claude Opus 5 
@c559b7e3bdcee0c4545c7ecd003dda8a98c4804d
 
app/(protected)/(basic)/_components/admin-shell.module.scss (added)
+++ app/(protected)/(basic)/_components/admin-shell.module.scss
@@ -0,0 +1,59 @@
+// Basic 레이아웃 셸 — 헤더·사이드바·본문·풋터의 배치만 책임진다.
+//
+// @fox에 넣지 않고 레이아웃 전용으로 둔다(사용자 확정) — 로고·프로필처럼 이 앱에만 있는
+// 구성을 전제로 한 배치라 포터블 디자인시스템에 들어갈 성격이 아니다. 대신 여백·색·서체는
+// 전부 @fox 토큰을 거치므로 없는 이름을 쓰면 빌드가 실패한다.
+//
+// **뷰포트를 채우고 그 안에서만 스크롤한다.** 헤더는 항상 보이고 사이드바와 본문이 각자
+// 스크롤한다 — 사이드바가 `overflow-y: auto`를 전제로 만들어져 있어(시안 높이 1251px이
+// 뷰포트보다 크다) 행 높이가 뷰포트에 묶여 있어야 그 스크롤이 동작한다.
+// globals.scss가 `html, body { block-size: 100% }`와 `body { display: flex }`를 깔아 두므로
+// 이 셸은 그 flex 자식으로서 남은 높이를 그대로 받는다.
+
+@use "@fox/styles/abstracts" as fox;
+
+.shell {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  // 자식이 넘칠 때 셸이 늘어나지 않게 한다 — 늘어나면 안쪽 스크롤 대신 페이지가 스크롤된다.
+  min-block-size: 0;
+}
+
+.body {
+  display: flex;
+  flex: 1;
+  min-block-size: 0;
+}
+
+.content {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  // flex 자식의 기본 min-inline-size는 auto라, 넓은 표가 들어오면 줄어들지 않고 사이드바를
+  // 밀어낸다. 0으로 낮춰야 표 쪽이 스크롤된다.
+  min-inline-size: 0;
+  background: fox.color(background-default);
+  overflow-y: auto;
+}
+
+.main {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  // 헤더의 좌우 여백과 같은 값이라 세로 기준선이 맞는다.
+  padding: fox.padding(8);
+}
+
+.footer {
+  flex-shrink: 0;
+  padding-block: fox.padding(6);
+  padding-inline: fox.padding(8);
+  border-block-start: fox.border(1) solid fox.color(border-neutral-subtler);
+  color: fox.color(font-neutral-subtler);
+  font-family: fox.font-family(body);
+  font-size: fox.font-size(label-sm);
+  line-height: 1.5;
+  letter-spacing: -0.025em;
+  text-align: center;
+}
app/(protected)/(basic)/_components/admin-shell.tsx
--- app/(protected)/(basic)/_components/admin-shell.tsx
+++ app/(protected)/(basic)/_components/admin-shell.tsx
@@ -2,6 +2,7 @@
 
 import type { SidebarMenu } from '@/lib/domain/sidebar-menu';
 import { AdminFooter } from './admin-footer';
+import styles from './admin-shell.module.scss';
 import { AdminHeader } from './admin-header';
 import { AdminSidebar } from './admin-sidebar';
 
@@ -24,13 +25,13 @@
  */
 export function AdminShell({ children, adminName, menu }: AdminShellProps) {
   return (
-    <div className="flex min-h-screen flex-col">
+    <div className={styles.shell}>
       <AdminHeader adminName={adminName} />
-      <div className="flex flex-1">
+      <div className={styles.body}>
         <AdminSidebar menu={menu} />
-        <div className="flex flex-1 flex-col">
-          <main className="flex flex-1 flex-col">{children}</main>
-          <AdminFooter />
+        <div className={styles.content}>
+          <main className={styles.main}>{children}</main>
+          <AdminFooter className={styles.footer} />
         </div>
       </div>
     </div>
Add a comment
List