# ADR: Layout Height Propagation Standard **Status:** ACCEPTED **Date:** 2026-08-16 **Authors:** Claude Code --- ## Context Multi-level frontend layouts (PageLayout → QueryBoundary → content) require explicit height propagation to ensure: 1. **Single-screen principle**: First load fits viewport without scroll 2. **Internal scrolling**: Only nested containers scroll, not the page 3. **Consistent behavior**: All pages follow the same height rules Previous bugs stemmed from missing `flex: 1; min-height: 0;` constraints at various levels. --- ## Decision Establish a **required height propagation chain** for all pages: ``` PageLayout (.ks-page__content) ├─ height: 100% ├─ min-height: 0 └─ display: flex ↓ (child must propagate height) QueryStateBoundary (.ks-query-boundary) ├─ flex: 1 ├─ height: 100% ├─ min-height: 0 └─ display: flex ↓ (child must propagate height) Content Container (grid, splitter, or flex child) ├─ flex: 1 (if flex child) ├─ height: 100% (if direct child of flex parent) ├─ min-height: 0 (always required for flex children) └─ overflow: (auto|hidden) ↓ (internal scrollable panes) Internal Panes (.items, .detail-panel, etc.) ├─ flex: 1 ├─ min-height: 0 └─ overflow-y: auto ``` --- ## Rationale 1. **Flex layout principle**: Flex children must have `min-height: 0` to respect parent constraints 2. **Height inheritance**: `height: 100%` only works when parent has explicit height 3. **Single responsibility**: Each layer only enforces its own constraints, children handle overflow --- ## Implementation ### For Grid-Based Pages (.ks-stack) ```css .ks-stack { display: grid; gap: var(--ks-space-4); flex: 1; /* Required: flex child must expand */ min-height: 0; /* Required: allow internal scroll */ height: 100%; /* Required: inherit parent height */ } ``` ### For Master-Detail Pages (KsSplitter) ```vue ``` **Key**: KsSplitter pane uses `overflow: hidden`; each slot's scrollable child must have `overflow-y: auto; min-height: 0`. ### For Flex-Column Containers ```css .container { display: flex; flex-direction: column; flex: 1; /* Expand to fill parent */ min-height: 0; /* Allow internal overflow */ height: 100%; /* Inherit parent height (optional if flex: 1 works) */ } .child { flex: 1; /* Share space with siblings */ min-height: 0; /* Don't prevent scrolling */ overflow-y: auto;/* Internal scroll */ } ``` --- ## Standard CSS Classes (Optional Utilities) Defined in `frontend/src/design-system/base.css`: ```css .ks-flex-column-1 { display: flex; flex-direction: column; flex: 1; min-height: 0; } .ks-flex-row-1 { display: flex; flex-direction: row; flex: 1; min-height: 0; } .ks-overflow-auto { overflow-y: auto; } ``` **Note**: These are *opt-in* utilities. Prefer explicit CSS in scoped styles for clarity. --- ## Related Artifacts - **Commits:** - 61ca979: `.ks-stack` flex: 1 fix - b299939: ApprovalQueue flex layout - 05e2791: ApprovalQueue → KsSplitter refactor - 4ff5aaf: `.request-detail` height fix - a1eccad: Standard CSS class definitions - **Updated Components:** - VersionGovernancePage.vue - ApprovalQueue.vue (KsSplitter migration) - ModelOperationsPage.vue - **Memory:** [[layout_standardization_framework]] --- ## Verification Checklist For each new page: - [ ] First load fits viewport (no page scroll) - [ ] Internal panes have `overflow-y: auto; min-height: 0` - [ ] Height chain: PageLayout → QueryBoundary → content - [ ] Master-detail uses KsSplitter or equivalent flex layout - [ ] All flex children have `flex: 1; min-height: 0` --- ## Future Considerations 1. **Breakpoints**: Responsive layouts (mobile) may need `flex-direction: column` at small widths 2. **Height variants**: Consider separate classes for different flex ratios (1:2, 1:1, etc.) 3. **Template enforcement**: CI/CD gate to catch missing height constraints --- ## Questions? See CLAUDE.md §"Frontend: Vue 3 + Vite + KBX Foundation v4" for component architecture.