fix(fe): PageLayout grid-based layout - eliminate flex gap height calculation error

Root cause: PageLayout's flex-direction: column + gap was not counted in flex: 1 height calculations, causing children to overflow and trigger scroll.

Solution: Convert PageLayout from flexbox to CSS Grid with explicit grid-template-rows. Grid automatically accounts for gaps in row sizing.

Changes:
- PageLayout.vue: display: flex → display: grid
- grid-template-rows: auto auto auto auto 1fr auto auto
- .ks-page__content/.ks-page__aside: height: 100% → flex: 1
- .ks-page__workspace: removed flex: 1 (grid cell, not flex)

Impact:
- models-master fits viewport without page-level scroll ✓
- All screen-types layouts auto-fit with correct height propagation
- Fix applies to all pages using PageLayout

AGENTS.md v16.0: Added Layout Rule #7 (PageLayout grid requirement)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 00:41:30 +09:00
parent b2aaf3ed75
commit 1d0fdcc013
2 changed files with 30 additions and 24 deletions
+5
View File
@@ -259,6 +259,11 @@ pnpm dev
4. **Scrollable containers:** `overflow-y: auto; min-height: 0;` (enables internal scroll without page scroll)
5. **Grid layouts:** `align-items: start;` (NOT center/stretch) to prevent column stretching at different heights
6. **Max-width constraint:** Wrap pages in `.page-wrapper { max-width: var(--ks-content-max); margin: 0 auto; }` to prevent 2560px+ distortion
7. **PageLayout must use CSS Grid (NOT flexbox).** Flex + gap breaks `flex: 1` height propagation in children:
-**DON'T:** `display: flex; flex-direction: column; gap: var(--ks-space-2);` (gap is not counted in flex: 1 calculations)
-**DO:** `display: grid; grid-template-rows: auto auto auto auto 1fr auto auto; gap: var(--ks-space-2);` (gap auto-calculated)
- **Reason:** Grid gap is accounted for in row sizing; flex gap is invisible to flex: 1 child height calculations, causing overflow → unwanted scroll
- **Template rows:** header (auto) | subtitle (auto) | commandBar (auto) | summary (auto) | filters (auto) | workspace (1fr) | footer (auto)
#### Page Structure Rule: NO Footers (Global or Page-Level)
**CRITICAL:** Footers are EXCLUDED at all levels:
+25 -24
View File
@@ -39,7 +39,8 @@ const showHelp = ref(false)
</section>
</template>
<style scoped>
.ks-page { width: 100%; max-width: var(--ks-content-max); margin: 0 auto; height: 100%; flex: 1; display: flex; flex-direction: column; gap: var(--ks-space-2); }
/* Grid-based page layout: gap is automatically calculated and doesn't break flex: 1 height propagation */
.ks-page { width: 100%; max-width: var(--ks-content-max); margin: 0 auto; height: 100%; flex: 1; display: grid; grid-template-rows: auto auto auto auto 1fr auto auto; gap: var(--ks-space-2); }
.ks-page__header { display: flex; align-items: center; justify-content: space-between; gap: var(--ks-space-4); flex-shrink: 0; min-height: 32px; border-bottom: 1px solid var(--ks-color-neutral-200); padding-bottom: var(--ks-space-2); }
.ks-page__title-group { display: flex; flex-direction: column; gap: 2px; }
.ks-page__breadcrumb { font-size: 11px; color: var(--ks-color-text-muted); line-height: 14px; }
@@ -47,35 +48,35 @@ const showHelp = ref(false)
.ks-page__header-utilities { display: flex; align-items: center; gap: var(--ks-space-2); flex-shrink: 0; }
.ks-page__subtitle-box { margin: 0; }
.ks-page__subtitle { margin: 0; color: var(--ks-color-text-muted); font-size: var(--ks-font-body); padding: var(--ks-space-2); background: var(--ks-color-canvas); border-radius: var(--ks-radius-sm); border: 1px solid var(--ks-color-border); }
.ks-help-btn, .ks-ai-btn {
border: 1px solid var(--ks-color-action);
background: var(--ks-color-surface);
border-radius: var(--ks-radius-sm);
padding: 2px 8px;
font-size: 11px;
.ks-help-btn, .ks-ai-btn {
border: 1px solid var(--ks-color-action);
background: var(--ks-color-surface);
border-radius: var(--ks-radius-sm);
padding: 2px 8px;
font-size: 11px;
font-weight: 600;
color: var(--ks-color-action);
cursor: pointer;
color: var(--ks-color-action);
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
transition: all 0.15s ease;
transition: all 0.15s ease;
}
.ks-help-btn:hover, .ks-help-btn.active, .ks-ai-btn:hover {
background: var(--ks-color-action);
color: #ffffff;
border-color: var(--ks-color-action);
.ks-help-btn:hover, .ks-help-btn.active, .ks-ai-btn:hover {
background: var(--ks-color-action);
color: #ffffff;
border-color: var(--ks-color-action);
box-shadow: 0 1px 3px rgba(37, 99, 235, 0.25);
}
.ks-page__command-bar {
display: flex;
align-items: center;
.ks-page__command-bar {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 4px 8px;
background: var(--ks-color-surface);
border: 1px solid var(--ks-color-border);
border-radius: var(--ks-radius-sm);
flex-shrink: 0;
padding: 4px 8px;
background: var(--ks-color-surface);
border: 1px solid var(--ks-color-border);
border-radius: var(--ks-radius-sm);
flex-shrink: 0;
min-height: 34px;
box-sizing: border-border-box;
}
@@ -106,8 +107,8 @@ const showHelp = ref(false)
box-sizing: border-box;
font-family: inherit;
}
.ks-page__workspace { min-width: 0; flex: 1; display: grid; min-height: 0; gap: var(--ks-space-4); }
.ks-page__workspace { min-width: 0; min-height: 0; display: grid; gap: var(--ks-space-4); }
.ks-page__workspace.has-aside { grid-template-columns: minmax(0, 1fr) var(--ks-aside-width); }
.ks-page__content, .ks-page__aside { min-width: 0; min-height: 0; height: 100%; display: flex; flex-direction: column; }
.ks-page__content, .ks-page__aside { min-width: 0; min-height: 0; display: flex; flex-direction: column; flex: 1; }
@media (max-width: 1100px) { .ks-page__workspace.has-aside { grid-template-columns: 1fr; } }
</style>