Files
jiapuapp/docs/superpowers/specs/2026-07-21-project-mobile-viewport-adaptation-design.md
T
2026-07-21 09:30:34 +08:00

165 lines
10 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.
# 全项目手机视口自适应设计
## 背景与根因
2026-07-21 在 MuMu Android `720×1280 / 320dpi` 中复核 A01 时,默认登录态无法在首屏完整显示。设备应用可用区约为 `360×616dp`,而 A01 仍以固定 `1665rpx` 画布组织纵向布局;该画布在 `412×915` 中接近 915px,在 360dp 宽设备中仍约为 799dp,因此必然产生滚动。
问题不是 A01 单页偏差,而是项目级合同缺失:
- 52 个活动页面虽都有 `100vh``100dvh`,但只有 4 页直接声明顶部安全区、9 页声明底部安全区;
- 22 页只有宽度断点,项目没有统一的高度连续适配规则;
- A01、A04、A05、G01、G03、G05 仍存在大块固定 `rpx` 高度;
- 现有容量合同检查固定高度、裁切和内容是否可达,但允许自然滚动,未检查固定流程页的核心操作是否在默认手机首屏完整可见;
- 旧 A01 设计记录明确允许矮屏滚动,与用户本轮确认的产品要求冲突。
用户确认的新合同是:整个项目必须适配手机可用视口;固定流程页在默认字体下首屏展示全部核心操作,长列表、详情和文章等长内容页自然滚动;所有页面都不得裁切、遮挡或依赖固定设计稿高度。
## 目标与边界
- 覆盖 `pages.json` 的全部 52 个活动路由,封存 A06 同步接受静态合同约束但不恢复路由。
- 支持当前产品使用的竖屏手机形态;基础验证宽度为 320–480 CSS px,高度从 568 CSS px 起。
- 默认系统字号下,固定流程页无需纵向滚动即可看到全部核心内容和主操作。
- 长内容页允许自然滚动,必须保证末项可达、滚动所有权清晰、固定导航不遮挡内容。
- 全部页面统一处理状态栏、刘海、底部手势区、固定 Header/Tabbar、软键盘和系统字体放大。
- 不改变页面业务语义、路由、字段、状态和视觉主题;不重绘或删除当前资产。
- 横屏不纳入本轮视觉排版目标;本轮不得通过未声明的横屏规则影响现有竖屏布局。
## 选择方案
采用“统一视口合同 + 页面滚动分类 + 路由保留业务布局”。
不把 52 页全部重包进一个通用页面组件,避免再次形成业务与布局耦合;不允许每页自行维护机型断点。共享层只拥有视口、安全区、触控下限和连续间距原语,每个路由继续拥有自己的内容结构。
## 单一所有者
### 运行时视口原语
`styles/global.scss` 是以下规则的唯一运行时所有者:
- 动态可用视口高度及 `100vh` 兼容回退;
- 顶部、底部安全区变量;
- 固定 Header 和 Tabbar 的占位变量;
- 约 44dp 的最小触控尺寸;
- 小屏到高屏连续变化的垂直间距、装饰尺寸和内容留白变量;
- 三类页面壳的基础行为。
页面可以消费这些变量,不能重新定义同义常量或按具体手机型号复制一套视口规则。
### 路由分类
新增一个精确路由清单作为页面分类唯一所有者。`pages.json` 中每个活动路由必须且只能属于一类;缺失路由、重复路由和已删除路由均使合同失败。
初始分类:
- `viewport-fit`A01、A04、A05、T08、F10。默认态内容有确定上限,默认字号下必须完整进入首屏。
- `split-scroll`:G01、T01、T07。页面壳固定,只有指定内容区域滚动;T01 继续保留明确的横向世系画布滚动。
- `document-scroll`:其余 44 个活动路由。内容随数据自然增长,由页面文档流承担唯一纵向滚动。
封存 A06 按 `viewport-fit` 静态检查,但不计入 52 页活动路由统计。
分类只决定视口和滚动合同,不提供标题、字段、状态或动作,不能成为新的业务目录配置。
## 三类页面行为
### `viewport-fit`
- 页面最小高度等于实际可用动态视口,默认态不得依赖固定设计稿高度。
- 默认字体、无键盘状态下,`scrollHeight` 不超过可用 `clientHeight` 的像素舍入容差。
- 背景、印章、标题和留白优先使用 `clamp()` 随可用高度连续缩放。
- 输入框、按钮、切换项、注册链接和协议勾选保持约 44dp 触控下限。
- 不允许整页 `transform: scale(...)`,不允许隐藏核心入口,不允许把核心操作移出首屏。
- 错误文案、软键盘或系统字体放大使内容超出时,页面回退为自然滚动,不裁字、不重叠。
### `document-scroll`
- 页面根容器至少填满动态可用视口,正文由正常文档流自然撑高。
- 页面只保留一个纵向滚动所有者;列表、详情、文章和长表单不得使用固定设计稿高度限制内容容量。
- 主操作不要求始终固定在首屏,但必须按流程顺序可达,且末项和底部按钮位于安全区及固定 Tabbar 之上。
- 空、加载、失败和短内容状态至少填满可用内容区,不能因内容少而露出错误背景或产生无意义双滚动。
### `split-scroll`
- Header、摘要、筛选或代际栏等固定区域明确占位,指定内容区获得剩余可用高度。
- 页面文档本身不得与内部内容区同时纵向滚动。
- G01 列表、T07 目录和 T01 世系画布保持各自唯一滚动所有权;固定区域不得遮住首项或末项。
- T01 横向画布滚动与纵向视口分工继续由页面专属合同维护。
## 安全区与固定导航
- `PageHeader` 继续统一拥有顶部安全区和页面顶部占位。
- `AppTabbar` 继续统一拥有底部安全区和页面底部占位。
- 没有 Header 或 Tabbar 的页面从全局页面壳消费安全区变量,不得硬编码状态栏或手势区高度。
- 同一安全区只能由一个层级计入,禁止页面与共享导航重复叠加。
- 页面背景可以延伸到系统区域,交互内容和关键文字不能进入不可安全触控区域。
## 连续适配规则
- 使用 `clamp(最小值, 视口相关计算值, 设计基准值)` 连续调整纵向尺寸,不按品牌、型号或单一分辨率写专用规则。
- `rpx` 继续用于与屏宽相关的横向比例和资产尺寸,但不得单独承担整页纵向高度合同。
- 纵向压缩顺序为:装饰留白 → 装饰尺寸 → 非关键间距 → 非关键说明字号;核心文字、输入和操作触控尺寸最后压缩且不得低于下限。
- 固定像素仅用于最小触控面积、1px 线条或经审计的资产槽位,不用于限制可变正文容量。
-`1665rpx` 等设计画布高度、重复的 `100vh` 页面壳和失效风险白名单在迁移后删除,不保留兼容分支。
## 字体、键盘与异常状态
- 默认字号执行页面分类的首屏或滚动合同。
- 约 1.3 倍系统字号下允许 `viewport-fit` 页面回退为自然滚动,但所有文字必须完整、操作必须可达。
- 输入框聚焦时,软键盘不得遮挡当前字段、就近错误文案或当前主操作;页面应能滚动到焦点并在键盘收起后恢复合理位置。
- Toast、Dialog、底部弹层和全屏预览拥有独立覆盖层,不计入页面默认内容高度;它们各自处理安全区、长文案和内部滚动。
- 加载、空、失败、权限和成功状态必须按所属页面类别验证,不能只验证正常态。
## 合同迁移
### 静态合同
新增项目级手机视口合同并先在当前代码上失败,至少检查:
- 52 条活动路由与分类清单一一对应;
- 页面使用全局视口原语,不重新声明旧页面壳常量;
- 禁止活动页面以固定大块 `rpx/px` 高度承担整页或正文布局;
- 禁止整页缩放、重复安全区、双纵向滚动和无所有者的固定导航避让;
- 允许图标、缩略图、印章、按钮槽位和 T01 节点等经过精确说明的固定视觉边界;
- 白名单必须精确到文件、选择器、风险类型和原因,并拒绝失效项。
现有数据容量合同继续负责数据数量和正文增长;新合同负责手机视口、首屏、滚动所有权和安全区。两者职责不得混写。
### H5 运行合同
全部 52 个活动路由至少验证:
- `320×568``360×616``360×640``360×800``412×915``480×1040`
- 无横向溢出、无关键控件越界、无双纵向滚动;
- 安全区和固定导航不遮挡首项、末项及主操作;
- `viewport-fit` 默认态无纵向滚动;
- `document-scroll` 末项可达且滚动位置稳定;
- `split-scroll` 只有声明的内容区滚动;
- 默认字号和约 1.3 倍字号均不裁字、不重叠。
流程页还需覆盖表单错误、提交中、失败、成功和返回保护等适用状态。
### Android 验证
- 先以当前 MuMu `720×1280 / 320dpi` 作为 Android 基线,逐模块复核代表页和全部 `viewport-fit` 页面。
- A01 必须重新截取密码、验证码、错误、协议和键盘状态;默认密码与验证码状态无需滑动即可看到完整协议区。
- 每个模块至少检查一个短内容状态和一个长内容状态;固定 Header/Tabbar、系统返回、软键盘和底部手势区必须真实操作。
- H5 自动结果不能替代 Android 证据或用户视觉确认。
## 执行顺序
1. 建立失败的路由分类、静态视口和全路由运行合同。
2. 建立全局视口、安全区、触控和三类页面壳原语。
3. 先修 A01 并在 MuMu 证明新合同可解决已复现问题。
4. 迁移 A04、A05、封存 A06,再迁移 G01、T01、T07 三个特殊滚动页。
5. 按 G → T → F → R → N → M 迁移剩余 `document-scroll` 页面。
6. 运行全路由六档 H5、字体压力、状态合同和 MuMu 模块复核。
7. 用户逐页确认发生可见变化的页面;自动验证不得直接新增或维持 `[x]`
## 完成条件
- 52 个活动路由全部纳入且通过项目级手机视口合同,无失效白名单。
- A01 在当前 MuMu 默认密码和验证码状态首屏完整,无需滑动。
- 六档 H5 的全部路由通过对应类别合同。
- 默认字号、1.3 倍字号、软键盘、安全区、固定导航和返回行为均有相应证据。
- 旧固定页面高度和旧自适应说明从运行时、验证器、设计记录、验收规划和交接记录中同步移除。
- Android 结论只覆盖实际验证的设备和状态,不把模拟器结果扩写为所有真机最终通过。