docs: add ADR-LAYOUT-HEIGHT-PROPAGATION standard

- Defines height propagation chain for all pages
- Establishes PageLayout → QueryBoundary → Content pattern
- Documents KsSplitter master-detail implementation
- Includes verification checklist for new pages
- Rationale: flex children need 'min-height: 0' to respect parent constraints
This commit is contained in:
2026-08-16 00:03:17 +09:00
parent 4ff5aaf97f
commit 09abf45c13
+171
View File
@@ -0,0 +1,171 @@
# 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
<KsSplitter storageKey="unique-key" initialRatio="25">
<template #left>
<aside class="request-list">
<h2>Requests</h2>
<div class="items"><!-- flex: 1; min-height: 0; overflow-y: auto; --></div>
</aside>
</template>
<template #right>
<main class="detail-panel">
<!-- height: 100%; min-height: 0; overflow-y: auto; -->
</main>
</template>
</KsSplitter>
```
**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.