diff --git a/frontend/src/components/common/StepPanel.tsx b/frontend/src/components/common/StepPanel.tsx index c7ea84c9..955a2954 100644 --- a/frontend/src/components/common/StepPanel.tsx +++ b/frontend/src/components/common/StepPanel.tsx @@ -11,6 +11,13 @@ export const ActionPanel = styled.div` flex-direction: column; overflow: hidden; background: ${p => p.theme.colors.bg}; + /* 높이 제약 — flex chain이 정상이면 height:100% 만으로 충분. 그래도 안전차원에서 + viewport 기준 max-height(헤더 ~140px 추정)도 같이 걸어둔다. align-self:stretch를 명시하여 + row flex 부모에서 cross-axis가 컨텐츠 크기로 부풀지 않게 한다. */ + align-self: stretch; + height: 100%; + max-height: calc(100vh - 140px); + min-height: 0; `; export const PanelHeader = styled.div` @@ -50,14 +57,34 @@ export const PanelStatusBadge = styled.span<{ variant?: 'warning' | 'accent' | ' export const StepsScroll = styled.div` flex: 1; + /* min-height: 0 — flex column 안에서 overflow-y: auto가 실제로 작동하려면 필수. + 없으면 자식 컨텐츠의 내재 높이만큼 확장되어 스크롤이 활성화되지 않는다. */ + min-height: 0; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 5px; + /* 자식 Card들이 flex-shrink:1 기본값으로 줄어들어 컨텐츠를 잘라먹는 걸 방지. + 이게 빠지면 Card의 overflow:hidden이 CardBody 하단을 클립해 버튼이 사라진다. */ + > * { flex-shrink: 0; } + /* 스크롤 가능 여부를 사용자가 인지할 수 있도록 스크롤바를 충분히 보이게 */ + scrollbar-width: thin; + scrollbar-color: ${p => p.theme.colors.border2} transparent; - &::-webkit-scrollbar { width: 3px; } - &::-webkit-scrollbar-thumb { background: ${p => p.theme.colors.border2}; border-radius: 2px; } + &::-webkit-scrollbar { width: 8px; } + &::-webkit-scrollbar-track { background: transparent; } + &::-webkit-scrollbar-thumb { + background: ${p => p.theme.colors.border2}; + border-radius: 4px; + border: 2px solid transparent; + background-clip: padding-box; + } + &::-webkit-scrollbar-thumb:hover { + background: ${p => p.theme.colors.sub}; + background-clip: padding-box; + border: 2px solid transparent; + } `; export const ContentArea = styled.div` diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index df784e8b..721ad5e0 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -21,12 +21,19 @@ const DashboardRoot = styled.div` const Body = styled.div` display: flex; flex: 1; + /* min-height: 0 — column 부모(DashboardRoot)의 main축에서 컨텐츠보다 작게 줄어들 수 있도록. + 없으면 본인이 자식 컨텐츠의 내재 높이만큼 늘어 ActionPanel/StepsScroll의 높이 제약이 풀린다. */ + min-height: 0; overflow: hidden; `; const TabContent = styled.div<{ visible: boolean }>` display: ${p => (p.visible ? 'flex' : 'none')}; flex: 1; + /* TabContent는 row flex 컨테이너이지만 자체도 row(Body)의 자식. cross축(높이)은 Body 높이로 결정. + Body의 min-height:0 만으로 충분하지만 안전하게 자체에도 부여. */ + min-height: 0; + min-width: 0; overflow: hidden; `;