Files
obsidian-vault/AI Coding/inbox/20260724-bounded-list-flex-height-chain.md
T
yuxuanhui 91861565bb feat: add frontend development guidelines and structure documentation
- Introduced API guidelines for interface contracts and request handling.
- Added design tokens usage guidelines for consistent styling across the project.
- Established DTO guidelines for defining request parameters and response data types.
- Created frontend structure guidelines to clarify directory organization and code placement rules.
- Compiled a comprehensive frontend development guideline document covering various aspects of the development process.
- Implemented quality guidelines to ensure code maintainability and adherence to best practices.
2026-07-25 22:20:25 +08:00

108 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: 20260724-bounded-list-flex-height-chain
title: 有界列表页使用连续 Flex 高度链并收敛滚动视口
created: 2026-07-24
updated: 2026-07-24
status: candidate
scope: global
category: frontend-layout
confidence: medium
last_verified: 2026-07-24
promotion_target: none
projects:
- usercenter-react
tags:
- flex-layout
- min-height-zero
- bounded-workspace
- scroll-ownership
- data-table
---
# 有界列表页使用连续 Flex 高度链并收敛滚动视口
## Trigger
列表页、主从工作台或树表页面需要填满应用内容区,同时保持标题、筛选、操作栏和分页可见,只让表格数据区、树节点区或详情正文滚动;或者当前页面出现双滚动条、分页被挤出视口、`h-full` 不生效、窗口高度变化后布局失效。
## Context
`usercenter-react` 的商场管理页面曾需要移除固定像素高度推算,使应用外壳、路由页面、Tabs、组织树和数据表形成连续的有界高度链。修复后,普通内容页仍由共享 `<main>` 滚动,有界工作台则填满 `<main>` 并把滚动交给最小内容视口。
这项经验的核心不是“给页面加 `flex-col`”,而是同时解决两个契约:高度从视口向后代连续传递,滚动权从外层收敛到最小内容区域。任一中间层缺少明确高度或 `min-h-0`,后代的 `h-full`、`flex-1` 和 `overflow-auto` 都可能无法按预期工作。
## Evidence
- 2026-07-24:重新检查 `usercenter-react` 当前源码、项目规范和 Git 历史;中央经验库搜索“有界列表页 / 连续 Flex 高度链 / min-h-0 / 滚动视口”无重叠条目。
- `usercenter-react/src/shared/layout/app-layout.tsx:207-236`:应用根节点使用 `h-dvh overflow-hidden`,内容列使用 `flex h-full min-h-0 flex-col`,共享 `<main>` 使用 `min-h-0 flex-1 overflow-y-auto`。
- `usercenter-react/src/features/market-management/pages/market-management-page.tsx:181-230`:有界路由根节点、分栏、Tabs 和 TabPane 连续使用 `h-full`、`flex-1`、`min-h-0` 与受控 overflow。
- `usercenter-react/src/shared/table/data-table.tsx:40-45,168-232`:`fillHeight` 以 opt-in 方式把表格面板变成有界 Flex 列,工具栏和分页留在滚动视口之外,数据区域消费剩余高度。
- `usercenter-react/.trellis/spec/frontend/layout-guidelines.md:23-32,74-125`:记录了各布局边界的类契约、滚动归属、错误矩阵和禁止使用固定 `calc(100vh - Npx)` 的规则。
- `usercenter-react/specs/page-layout.spec.md:5-11`:产品级规范要求有界页面使用连续 Flex 高度链,并将 overflow 委托给最小树或数据视口。
- Git 提交 `ccf068c` 修复商场管理页面高度适配,`1bb14e8` 将实践整理为 bounded layout 指南,`849219a` 继续补充可伸缩侧栏场景下的表格滚动与 sticky 边界。
- 当前仓库的 `src/shared/list-templates/master-detail-list-template.tsx:34-60` 仍存在 `calc(100vh - 10.5rem)` 实现;这是需要结合响应式父级契约进一步验证的现有反例,不能仅凭规则直接判定或修改。
## Root cause
已验证:
- `flex-col` 和 `flex-1` 只负责空间分配,不会自动建立可解析的高度边界。
- Flex/Grid 子项默认的 `min-height: auto` 会阻止内容区域收缩;中间层缺少 `min-h-0` 时,内容会撑高页面而不是在指定视口内滚动。
- `h-full` 依赖祖先提供确定高度;高度链中断时,百分比高度不能可靠地代表剩余视口空间。
- 页面、共享 `<main>` 和表格数据区同时拥有纵向 overflow 时,会形成嵌套或竞争滚动条。
- `calc(100vh - Npx)`、JavaScript 测量和重复的标题高度常量容易在工具栏换行、外壳调整、缩放或窄屏布局下失效。
推断:把布局评审固定为“先从外向内检查高度链,再从内向外确认唯一滚动所有者”,比逐个添加高度类或 overflow 类更容易定位缺失环节,也更适合跨组件复用。
## Preferred action
1. 先区分页面模式:普通内容页继续由应用 `<main>` 滚动;只有需要固定控制区和内部滚动的页面才进入有界模式。
2. 从视口向目标滚动区域建立连续高度链:应用根节点建立动态视口边界,中间内容列使用 `flex h-full min-h-0 flex-col`,有界路由根节点使用 `flex h-full min-h-0 flex-col overflow-hidden`。
3. 为所有需要消费剩余高度或继续向下传递高度的 Flex/Grid 中间层添加 `min-h-0`;不要只修改最终表格容器。
4. 标题、筛选、操作栏、Tabs 头部和分页等固定区域使用 `shrink-0`。剩余内容区域使用 `min-h-0 flex-1`。
5. 将纵向滚动交给最小内容视口:通常是表格数据区、树节点区或详情正文。结构层使用 `overflow-hidden`,实际内容视口使用 `overflow-auto` 或 `overflow-y-auto`。
6. 共享表格的填高能力保持 opt-in;只有父级已经提供有界 `h-full/min-h-0` 区域时才启用,避免改变普通内容型页面的默认行为。
7. 优先使用 Flex 剩余空间,不使用固定 `calc(100vh - Npx)`、JavaScript 高度测量或跨组件共享的像素常量。
8. 在真实浏览器中验证滚动归属:有界页面不增加 body 高度,固定控制区和分页保持可见,只有目标数据区滚动;同时检查窄屏、工具栏换行、loading、empty 和 error 状态。类型检查、lint 和构建不能替代这项验证。
## Examples
```tsx
<div className="h-dvh overflow-hidden">
<div className="flex h-full min-h-0 flex-col">
<main className="min-h-0 flex-1 overflow-y-auto">
<section className="flex h-full min-h-0 flex-col overflow-hidden">
<header className="shrink-0">{header}</header>
<div className="shrink-0">{filtersAndActions}</div>
<div className="min-h-0 flex-1 overflow-hidden">
<DataTable fillHeight {...tableProps} />
</div>
</section>
</main>
</div>
</div>
```
记忆方式:高度从外向内连续传递,滚动权收敛到最小内容视口。
## Boundaries
- 普通详情页、表单页和自然增长的长页面通常应继续由共享 `<main>` 滚动,不要为了统一外观强制启用有界模式。
- 移动端主从区域改为纵向堆叠时,可能更适合自然页面滚动;有界模式可以只在桌面断点启用。
- `overflow-hidden` 只有在后代明确拥有滚动视口时才安全,否则会直接裁切内容。
- Tabs、Drawer、Spin、Table 和虚拟列表等第三方组件可能插入包装层,需要根据实际 DOM 补齐高度链或沿用组件自己的滚动机制。
- 可交互变宽的分栏可能触发第三方表格的 ResizeObserver 和 sticky 行为;`nativeStickyHeader`、固定列实现等属于组件或项目特例,不是连续 Flex 高度链的通用要求。
- 已存在的 `calc(100vh - Npx)` 不应机械替换;需要先确认页面父级是否能提供完整高度链,以及窄屏模式是否依赖自然内容高度。
- 当前证据主要来自 `usercenter-react` 的一次完整修复及后续演进。在另一个项目或独立场景复用并完成浏览器验证前,不提升为全局强制规则。
## Failed approaches
- 只在页面根节点增加 `flex-col` 或只在表格增加 `flex-1`:高度链仍可能在中间层中断。
- 只给最终内容区域增加 `overflow-auto`:默认 `min-height: auto` 仍可能让它撑高父级。
- 同时让 `<main>`、路由根节点和表格数据区滚动:容易出现双滚动条、滚轮焦点切换和分页被挤出视口。
- 通过 `calc(100vh - Npx)` 或 JavaScript 测量补偿标题高度:在标题、操作栏换行或应用外壳变化后容易产生新的固定偏差。
## Promotion record
- Not promoted. 当前已有一个项目中的修复、规范和后续演进证据,先保留为中央 candidate;待第二个项目或独立场景验证后,再考虑提升为 `patterns/` 下的前端布局模式或可执行浏览器检查。