antislop-layoutmobile
反劣化:AI编码代理规则。移动布局技能
属于 antislop 系统的一部分。请与
antislop.md(核心)一起阅读。本技能深入探讨响应式布局问题:布局如何跨屏幕尺寸(从手机到桌面)重新流动。包括断点、缩放、网格、溢出、点击目标和导航。它通过编号引用核心规则,绝不重复或重新编号。当任务需要构建或编辑一个必须在任何屏幕宽度下都能正常显示的布局时,加载此技能。
如何使用此技能
- 每当任务是移动端或响应式布局工作时,请与
antislop.md一起加载。核心包含机制(目的测试、三个层级、交付门);本技能包含移动布局深度。 - 每个条目都具有相同的结构:告知(模式)、原因(为什么它被视为劣化)、修复(应该怎么做),并引用相应的核心规则(R-XX)。
- 本技能背后的原则:移动布局是一种不同的布局,而不是桌面布局的缩小版本。 它必须重新流动:重新堆叠、重新缩放并有意图地重新排序。下面的每个模式都是布局未能重新流动的表现。
- 核心中的交付门仍然是最终关卡。本文件末尾的“Layoutmobile 技能检查清单”是在此基础上运行的移动专用补充。
断点
仅桌面布局
- 告知: 所有屏幕只有一个布局状态;移动视图是桌面布局被压扁到手机中。
- 原因: R-03 要求移动布局必须完美,而非事后补丁。一张只会缩小的页面根本没有移动设计:本应并排的卡片重叠了,为宽屏设计的文本拥挤在窄屏中。
- 修复: 在内容停止有效工作的断点处定义真正的移动状态。移动布局会重新流动:列堆叠、尺寸减小、根据内容需要改变顺序。如果移动视图只是桌面视图在较小宽度下的样子,那么布局工作尚未完成。
由设备列表驱动的断点
- 告知: 断点以手机宽度命名(375px, 414px, 768px),选择这些值是因为“那是 iPhone 的尺寸”,而不是因为内容在那里中断。
- 原因: 设备宽度每年、每个型号都在变化。断点是布局无法再保持的点;强制使其匹配设备列表会使布局遵循规格表而非内容(R-03)。
- 修复: 将断点放置在内容实际中断的位置:当一列变得不可读时,当一排卡片变得太窄时。通过缩小视口并观察它在哪里断裂来测试,然后将断点设置在那里。
最后再处理移动端
- 告知: 移动规则作为事后覆盖添加:一个长的桌面样式表末尾有一个小的 media query 修复一两个问题。
- 原因: 覆盖补丁不是移动设计。它修复了被报告的症状,但留下了下一个问题,而且基础样式仍然为宽屏调优(R-03)。
- 修复: 将移动端视为一个经过设计的状态,而不是覆盖。为窄视口提供其自身经过深思熟虑的尺寸和堆叠方式,并验证整个布局,而不仅仅是修补过的部分(R-35)。
两状态布局
- 告知: 一种布局恰好有两个状态:在一个断点以下为单列堆叠,在其之上为宽多列网格,中间没有任何定义。平板或小型笔记本电脑宽度因此继承最接近的状态:要么是手机堆叠被荒谬地拉宽,要么是桌面网格被塞入其预期画布的一小部分。
- 原因: 页面是宽度的连续范围,而 R-03 的要求是在每一个宽度上它都必须正常,而不仅仅是两个选定的断点。两个状态使得整个中间波段(大约 600 到 1024 px,平板和小型笔记本电脑所在区域)成为偶然:内容既不是有意图地堆叠,也不是放在合适的网格中。布局只有在两个采样点上才显得经过设计,而在中间的所有地方都是损坏的。
- 修复: 在内容停止有效工作的宽度处定义真实的状态,并让它们根据内容需要尽可能多。典型的重新流动有 3 个状态,而不是 2 个:单列,然后当卡片作为单列堆叠过宽时变为两列网格,最后只有当完全合适时才变为全多列网格。通过拖动视口遍历整个范围来验证,而不是只检查两个宽度就宣布完成(R-35)。
缩放与尺寸
桌面尺寸的万物
- 告知: 内边距、间距、英雄区高度和卡片尺寸从桌面到移动端保持不变,导致每个部分在手机上看起来都被放大了。
- 原因: 为 1440px 画布设计的元素在 375px 的画布上占据主导地位。在桌面上显得自信的内容在移动端变得过大:没有东西合适,没有东西透气,页面感觉就像是为一个不是手中的屏幕设计的(R-03)。间距和排版应遵循设计节奏(R-05),而该节奏在移动端有更小的刻度。
- 修复: 为移动端提供其自身的尺寸阶梯:更小的字号体系、更紧密的区块内边距、更小的间距。保持点击目标的最小尺寸(见点击目标),但在断点处有意图地缩小其他所有内容。
固定像素文字
- 告知: 使用固定 px 的字体大小,在桌面和移动端之间从不改变,导致标题和正文在手机上仍然过大。
- 原因: 不响应用户视口的文字是为单一屏幕设计的。R-03 要求移动布局保持正常,而 R-06 要求排版提高可读性。占据整个手机宽度的标题或为宽行调优的正文大小破坏了这两条规则。
- 修复: 使用流畅文字(
clamp())使尺寸随视口缩放,或在断点处设置更小的字号阶梯。在窄宽度下验证结果(R-35),而不仅仅是在桌面预览中。
100vh 区块
- 告知: 英雄区和区块高度设置为
100vh,导致一个区块填满整个手机屏幕并将其他所有内容推送到下方不可见。 - 原因: 为桌面显示器设计的全视口区块在手机上变成了巨大的板块,而
100vh包含浏览器 chrome,因此在移动浏览器上会超出可见区域。它主导了布局而非介绍布局(R-03)。 - 修复: 让区块根据内容自适应(
auto),或者在确实需要全高区块时使用动态视口单位(dvh)。折线以下的内容不应成为视口单位的意外。
巨大的空白内边距
- 告知: 桌面尺度的区块内边距(96px, 128px)保留在移动端,在手机上造成区块之间巨大的空白间隙。
- 原因: 为大画布调优的内边距在小屏幕上变成了浪费的垂直空间。页面滚动穿越空虚,而 R-05 要求的韵律变成了每个区块之间的空白。
- 修复: 在断点处将区块内边距减小为移动端寄存器(大约一半或更少),并检查页面以自然的密度滚动,而不是穿过空旷的空间。
网格与堆叠
不折叠的列
- 告知: 多列网格在移动端保持并排列,导致列缩小、文本怪异换行、元素碰撞。
- 原因: 网格是对可用宽度的承诺。当视口变窄而网格不重新堆叠时,每一列都变成一条细带,文本变得不可读,卡片重叠(R-03)。这是碰撞失败:桌面的并排变成了移动端的堆叠坍塌。
- 修复: 在断点处将网格折叠为单列流动。并排变成堆叠,每个项目再次获得完整宽度。在窄宽度下重新验证(R-35)。
固定宽度网格
- 告知:
grid-template-columns以固定 px 设置,或网格区域无法重新流动,导致网格在视口缩小时保持刚性。 - 原因: 固定 px 轨道不关心视口;它保持其宽度并强制溢出或碰撞。布局是为一个画布构建的,无法改变形状(R-03)。
- 修复: 使用
minmax()或auto-fit和auto-fill调整轨道大小,使列随内容缩小和换行,并在断点处定义折叠的网格区域。网格默认应该是流动的,只有刻意需要时才保持刚性。
强制 12 列
- 告知: 将 12 列网格强加到只需要 1 或 2 列的移动内容上,导致跨度看起来任意,数学运算与布局对抗。
- 原因: 12 列系统适用于需要多列内容的宽画布。将其强加在手机上会使每个元素都成为用户永远看不到的不可见网格的一部分,内容被拟合到网格上,而不是网格配合内容(R-03)。
- 修复: 让列跟随内容。在移动端,内容通常只需要 1 列,最多 2 列;只有布局真正有那么多并排内容时,12 列跨度才有意义。
溢出
水平滚动泄漏
- 告知: 页面因某个元素比视口宽而横向滚动:表格、代码块、图片、长不间断字符串。
- 原因: 水平滚动是移动端的破坏性承诺。用户看不到页面在何处结束,布局明显溢出屏幕(R-03)。这是最常见的溢出劣化,因为违规元素在桌面预览中位于屏幕外,直到通过手机打开才会被发现。
- 修复: 找到比视口更宽的元素(需要重新流动布局或滚动容器的表格、需要换行的代码块、带
max-width: 100%的图片),然后容纳或重新流动它。验证整个页面在最窄目标宽度处没有水平滚动(R-35)。
溢出隐藏裁剪
- 告知: 对在窄宽度下裁剪内容的容器使用
overflow: hidden,隐藏文本或控件而不是让它们适应。 - 原因: 裁剪是隐藏失败。当容器因布局无法容纳而截断其内容时,用户会失去信息和交互能力(R-03)。
antislop-human涵盖了缩放和文本大小调整方面的相关问题。 - 修复: 让内容重新流动而不是裁剪:允许容器增长、换行其内容,或在断点处折叠。仅在裁剪是设计意图(如缩略图)时才进行裁剪,绝不在隐藏内容的地方使用。
固定宽度子元素
- 告知: flex 或 grid 的子元素具有固定 px 宽度或
min-width,在窄屏幕上撑破其父容器。 - 原因: 使用绝对 px 尺寸的子元素不关心其父元素有多大空间。在移动端,父元素缩小而子元素保持宽大,导致它溢出容器和页面(R-03)。
- 修复: 使用相对单位设置子元素尺寸,让它们换行(
flex-wrap、流动宽度、grid 子元素上的min-width: 0)。子元素应允许随其容器缩小,而不是保持桌面尺寸。
点击目标
尺寸过小的目标
- 告知: 按钮、链接和控件小于约 44 x 44 px,在桌面上用光标容易点击,但用拇指很难。
- 原因: 桌面光标具有像素级精度;拇指则不然。16px 的目标在手机上变成令人沮丧的体验,这违背了 UI 在移动端可用的承诺(R-03)。
- 修复: 为交互式目标提供至少 44 x 44 px 的最小触摸区域,使用内边距或更大的点击区域(即使视觉元素较小)。在手机宽度下验证整个控件集(R-35)。
目标太近
- 告知: 44px 的目标紧密相邻,没有间隙,导致拇指点击到错误的目标。
- 原因: 目标大小只有在目标间距合适时才有意义。两个接触在一起的大控件表现得像一个大的控件:用户无法可靠地选择任何一个(R-03)。
- 修复: 在相邻的交互式目标之间留下清晰的间隙,至少几个 px,最好能让手指按压区域不重叠。间距是触摸友好的另一半。
仅悬停交互
- 告知: 菜单、揭示和工具提示仅存在于悬停时,导致触摸用户永远无法打开它们。
- 原因: 触摸屏上没有悬停。仅响应悬停的交互对于移动用户来说根本不存在,任何依赖悬停的控件都是死胡同(R-03)。
- 修复: 为每个仅悬停的交互提供点击等价物:在悬停时打开的菜单也在点击时打开,揭示也在点击时显示,交互元素显示可见的
:active反馈,使点击得以确认。仅使用触摸测试 UI(R-35)。
移动导航
保持桌面的导航
- 告知: 桌面顶部栏及其链接行在移动端保持并排,导致链接拥挤、换行成两行或溢出视口。
- 原因: 桌面导航是为宽画布设计的。在手机上保持一行会变成一团拥挤的链接,这是移动用户首先看到的东西(R-03)。导航是重新流动最重要的地方:用户在去任何地方之前必须知道去哪里。
- 修复: 在断点处将导航折叠为移动模式:底部导航用于少数主要目的地,或菜单用于其余部分。链接从行中重新流动出来,主要目的地保持在一指可及的位置。验证它在窄宽度下保持正常(R-35)。
纯汉堡菜单
- 告知: 所有内容都隐藏在一个没有标签和提示的汉堡图标后面,导致用户从未意识到菜单存在,或不知道它能打开什么。
- 原因: 纯汉堡菜单假设用户已经知道图标的含义以及背后隐藏着菜单。这是移动用户可能不具备的知识,而在移动端,隐藏菜单可能包含了在应用内导航的唯一途径(R-03)。
- 修复: 保持菜单可发现:给汉堡图标加标签(“菜单”),或者保持主要目的地可见,只隐藏次要目的地。如果菜单是到达某个重要内容的唯一途径,那么这种可达性必须显而易见。
吞噬内容的底部导航
- 告知: 固定的底部导航栏覆盖内容,遮盖了最后的列表项、最后的按钮或用户正在尝试填写的表单。
…
[已截断用于翻译]