Files
obsidian-vault/AI Coding/inbox/20260724-bounded-list-flex-height-chain.md
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

7.8 KiB
Raw Permalink Blame History

id, title, created, updated, status, scope, category, confidence, last_verified, promotion_target, projects, tags
id title created updated status scope category confidence last_verified promotion_target projects tags
20260724-bounded-list-flex-height-chain 有界列表页使用连续 Flex 高度链并收敛滚动视口 2026-07-24 2026-07-24 candidate global frontend-layout medium 2026-07-24 none
usercenter-react
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

<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/ 下的前端布局模式或可执行浏览器检查。