Files
KArtSell.Aegis/docs/ADR-LAYOUT-HEIGHT-PROPAGATION.md
kjh2064 09abf45c13 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
2026-08-16 00:03:17 +09:00

4.5 KiB

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)

.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)

<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

.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:

.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.


  • 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.