refactor(ui): 사이드 패널을 [스크롤 영역][고정 액션 영역]으로 분리

sticky + margin-top:auto 조합은 본문 높이가 패널 높이를 넘나들 때마다 액션 줄이
붙었다 떨어졌다 했다(2026-08-18 사용자 보고). 액션 줄을 스크롤 대상에서 아예 빼는
구조로 바꾼다.

- overlay.ts: 사이드 패널 생성 시 본문의 `.ui-sidebar-actions` 앞 형제들을
  마진·패딩 없는 `.ui-sidebar-scroll` 래퍼로 옮기고 body에 `ui-sidebar-split`을
  붙인다. 페이지 코드는 지금처럼 본문 끝에 액션 줄을 붙이기만 하면 된다.
- overlay.css: split body = 세로 플렉스 + overflow hidden + padding-bottom 0,
  스크롤 래퍼 = flex:1/min-height:0/overflow-y:scroll + gap:inherit(그룹 간격 유지),
  액션 줄 = flex:none(sticky·margin-top 제거).
- B08: 전용 sticky 규칙을 버리고 공용 ui-sidebar-actions로 통합 — B04~B08이 한 규칙.

스크롤바 자리 상시 예약(2026-08-17)은 래퍼가 이어받아 내용 폭은 그대로다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-18 17:21:40 +09:00
co-authored by Claude Opus 5
parent 74b1cd2143
commit a794252e2e
4 changed files with 57 additions and 22 deletions
+3 -3
View File
@@ -510,9 +510,9 @@ export async function renderB08DesignDetail(root: HTMLElement): Promise<void> {
);
drawingListEl = drawingPanel;
const confirmActions = document.createElement("div");
// b08-drawing-actions는 자체 sticky 하단 고정(B08 Style.css) — 공용 ui-sidebar-actions와
// 같은 동작이라 중복 적용하지 않는다(2026-08-05 확인).
confirmActions.className = "b08-drawing-actions";
// 하단 고정은 공용 ui-sidebar-actions로 통일 — 사이드 본문이 [스크롤 영역][액션 줄]로
// 쪼개지고 액션 줄은 스크롤 밖에 남는다(2026-08-18 사용자 지시, B04~B08 공통).
confirmActions.className = "b08-drawing-actions ui-sidebar-actions";
confirmActions.append(confirmButton);
drawingPanel.append(infoPanelHost, confirmActions);
@@ -113,12 +113,10 @@
color: var(--color-success);
}
/* 하단 고정·배경·상단 구분선은 공용 ui-sidebar-actions가 맡는다(2026-08-18 통합).
여기서는 B08 고유 여백만 남긴다. */
.b08-drawing-actions {
position: sticky;
bottom: 0;
margin-top: auto;
padding-top: var(--spacing-12);
background: var(--color-surface-raised);
}
.b08-drawing-actions > button {
+32 -15
View File
@@ -232,31 +232,48 @@
}
}
/* 사이드 패널 최하단 고정 액션 줄(계산·확정 버튼) — 스크롤에서 제외된다.
B04·B05·B06·B07 공통(2026-08-05 사용자 지시). 스크롤로 지나가는 다른 버튼·내용이
틈으로 노출되지 않게 불투명 배경 + 사이드 body의 하단 패딩(16px)까지 덮는다. */
/* 사이드 패널 최하단 고정 액션 줄(계산·확정 버튼) — B04~B08 공통(2026-08-05 사용자
지시). 스크롤 영역(.ui-sidebar-scroll) 바깥의 형제라 자리가 흔들리지 않는다
(2026-08-18 개편 — sticky로만 붙이던 때는 본문 높이가 패널 높이를 넘나들 때마다
붙었다 떨어졌다 했다). 스크롤로 지나가는 내용이 틈으로 비치지 않게 배경은 불투명. */
.ui-sidebar-actions {
position: sticky;
bottom: calc(-1 * var(--spacing-16));
z-index: 5;
flex: none;
display: flex;
gap: var(--spacing-8);
/* 내용이 패널보다 짧아도 액션 줄은 패널 최하단에 붙는다 — 위 여백을 이 줄이
전부 먹는다(2026-08-18 사용자 지시, B04~B08 공통). 내용이 길면 auto가 0이 돼
기존처럼 sticky로 따라온다. */
margin-top: auto;
padding: var(--spacing-8) 0 var(--spacing-16);
background: var(--color-surface-raised);
border-top: 1px solid var(--color-border);
}
/* margin-top:auto가 먹히려면 액션 줄을 담은 사이드 body가 세로 플렉스여야 하고
높이가 정해져 있어야 한다(--panel--title의 body는 flex:1로 이미 정해진다).
B04·B05·B06은 페이지 CSS가 이미 세로 플렉스지만, 규칙 없이 블록으로 남은
패널(B07)도 같이 걸리게 여기서 공통으로 준다. */
.ui-workflow-overlay__body:has(> .ui-sidebar-actions) {
/* 액션 줄이 있는 사이드 body는 [스크롤 영역][액션 줄] 두 칸(overlay.ts가 래퍼를
끼운다). body 자신은 스크롤하지 않는다 — 스크롤은 안쪽 래퍼 몫. */
.ui-workflow-overlay__body.ui-sidebar-split {
display: flex;
flex-direction: column;
overflow: hidden;
/* 액션 줄이 바닥까지 내려온다 — 아래 여백은 액션 줄 자신의 패딩(16px)이 만든다. */
padding-bottom: 0;
}
/* 마진·패딩 없는 순수 스크롤 래퍼. 그룹 간격(gap)은 페이지가 body에 준 값을 그대로
물려받아 기존 간격이 유지된다. */
.ui-sidebar-split > .ui-sidebar-scroll {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
gap: inherit;
margin: 0;
padding: 0;
overflow-y: scroll;
scrollbar-gutter: stable;
}
/* 스크롤은 안쪽 래퍼가 맡으므로 body 자신의 상시 스크롤(위 --panel--title 규칙)은
끈다. 오른쪽 패딩 보정(-8px)은 그대로 둔다 — 래퍼가 그 폭 안에서 스크롤바 자리를
예약하므로 내용 폭은 개편 전과 같다. */
.ui-workflow-overlay__panel--title > .ui-workflow-overlay__body.ui-sidebar-split {
overflow-y: hidden;
}
.ui-sidebar-actions > * {
+20
View File
@@ -81,6 +81,25 @@ function readOpenState(key: string): boolean {
return sessionStorage.getItem(key) !== "false";
}
/**
* 사이드 패널 본문을 [스크롤 영역][고정 액션 영역] 두 칸으로 쪼갠다.
*
* 액션 줄(`.ui-sidebar-actions`)을 sticky로만 붙여 두면 본문 높이가 패널 높이를
* 넘나들 때마다 자리가 흔들린다(2026-08-18 사용자 보고). 액션 줄을 스크롤 대상에서
* 아예 빼고, 나머지 그룹만 마진·패딩 없는 래퍼(`.ui-sidebar-scroll`)에 담아 그쪽만
* 스크롤시킨다. 페이지는 지금처럼 본문 마지막에 액션 줄을 붙이기만 하면 된다.
*/
function splitSidebarActions(body: HTMLElement): void {
const actions = body.querySelector<HTMLElement>(":scope > .ui-sidebar-actions");
if (!actions) return;
const scroll = document.createElement("div");
scroll.className = "ui-sidebar-scroll";
// 액션 줄 앞의 형제만 옮긴다 — 뒤에 붙은 요소(없어야 정상)는 그대로 둔다.
while (body.firstChild && body.firstChild !== actions) scroll.append(body.firstChild);
body.prepend(scroll);
body.classList.add("ui-sidebar-split");
}
function createPanel(
variant: "title" | "progress",
titleText: string,
@@ -115,6 +134,7 @@ function createPanel(
}
if (body.childElementCount > 0) {
body.classList.add("ui-workflow-overlay__body");
if (isSidebar) splitSidebarActions(body);
root.append(body);
}