From 4b338ebefc6fbba4a9d7dba821f9c6a223e7cd91 Mon Sep 17 00:00:00 2001 From: rain Date: Tue, 21 Jul 2026 09:30:34 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=85=A8=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E8=A7=86=E5=8F=A3=E8=87=AA=E9=80=82=E5=BA=94?= =?UTF-8?q?=E5=90=88=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oject-mobile-viewport-adaptation-design.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-21-project-mobile-viewport-adaptation-design.md diff --git a/docs/superpowers/specs/2026-07-21-project-mobile-viewport-adaptation-design.md b/docs/superpowers/specs/2026-07-21-project-mobile-viewport-adaptation-design.md new file mode 100644 index 0000000..2c7ed99 --- /dev/null +++ b/docs/superpowers/specs/2026-07-21-project-mobile-viewport-adaptation-design.md @@ -0,0 +1,164 @@ +# 全项目手机视口自适应设计 + +## 背景与根因 + +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 结论只覆盖实际验证的设备和状态,不把模拟器结果扩写为所有真机最终通过。