响应式布局应当源于内容约束,而不是设备型号列表。Intrinsic sizing、flexbox、grid、媒体查询和容器查询提供了相互补充的控制手段。
建立响应式基础 #
viewport 声明会使 CSS viewport 与设备宽度匹配:
<meta name="viewport" content="width=device-width, initial-scale=1">不要添加 user-scalable=no 或较低的 maximum-scale;页面缩放是一种无障碍机制。围绕屏幕键盘定位控件的应用可能还需要 visual viewport API,因为可见区域可能发生变化,而 layout viewport 不一定变化。
使用 border-box sizing,并限制替换内容的尺寸:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
img,
video,
svg {
display: block;
max-inline-size: 100%;
block-size: auto;
}没有这些规则时,padding 可能会意外增加组件宽度,而具有较宽 intrinsic 尺寸的媒体可能溢出狭窄的 viewport。
居中并限制阅读内容 #
可读的列需要最大行长和适用于小屏幕的内边距:
main {
inline-size: min(100%, 72ch);
margin-inline: auto;
padding-inline: 1rem;
}ch 近似表示 0 字形的 advance width。它适用于正文的行长;对于尺寸不应依赖所选字体的控件,rem 和像素能够表达得更好。在全局 border-box 策略下,padding-inline 会在受限宽度内部创建适应书写模式的内边距;无需选择物理方向的左、右属性,即可适用于从左到右、从右到左以及垂直文本。
避免为页面的主要内容设置固定的 min-width。例如 min-width: 480px 这样的声明会在窄于 480 CSS 像素的 viewport 上强制水平滚动。min()、max() 和 clamp() 可以在不对 viewport 作出不受支持的假设的情况下保留约束:
h1 {
font-size: clamp(2rem, 1.25rem + 3vw, 4rem);
}选择 Flexbox 或 Grid #
Flexbox 沿主轴排列一维序列。Grid 定义行和列,并且可以跨两个轴对齐项目。
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
}flex-direction 决定主轴。justify-* 属性沿主轴分配空间;align-* 属性控制交叉轴。align-items 为子元素设置默认值,而 align-self 可以覆盖单个项目的设置。flex-flow 组合了 flex-direction 和 flex-wrap。
Grid 适用于需要自动形成列的卡片:
.cards {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(18rem, 100%), 1fr)
);
gap: 1rem;
}内部的 min() 可以防止 18 rem 的最小值溢出窄于 18 rem 的容器。auto-fit 会折叠空轨道,使有内容的卡片能够占用可用宽度。
当 Grid 内容需要允许缩小到其 intrinsic minimum 以下时,使用 minmax(0, 1fr):
.sidebar-layout {
display: grid;
grid-template-columns: minmax(12rem, 1fr) minmax(0, 3fr);
}针对环境约束添加媒体查询 #
媒体查询应当在内容不再适合当前布局时引入布局变化:
@media (width < 48rem) {
.sidebar-layout {
grid-template-columns: 1fr;
}
}Range syntax 可以直接表达边界。常见的媒体特性包括 width、height、orientation、aspect-ratio、resolution、prefers-color-scheme、prefers-reduced-motion、hover 和 pointer。
输入特性很重要,因为窄触摸屏与窄桌面窗口并不提供相同的交互方式:
@media (hover: hover) and (pointer: fine) {
.menu-item:hover {
text-decoration: underline;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto;
animation-duration: 1ms;
animation-iteration-count: 1;
transition-duration: 1ms;
}
}不要使用 hover: none 证明设备是手机。混合设备可能具有多种输入机制。
使用容器查询实现可复用组件 #
组件可能出现在宽页面列中,也可能出现在窄侧边栏中。因此,相比 viewport,组件自身可用的空间更有用:
.card-region {
container-type: inline-size;
}
.card {
display: grid;
gap: 1rem;
}
@container (width >= 32rem) {
.card {
grid-template-columns: 10rem minmax(0, 1fr);
}
}Containment 应设置在被查询组件的祖先元素上。避免查询同一元素的尺寸,同时又通过查询改变该元素的样式,因为这样会产生循环 sizing logic。
容器查询用于设置后代元素的样式,而不是查询容器自身。子项目仍可能由于其 intrinsic minimum sizes 而溢出,因此在内容必须缩小时应用 min-inline-size: 0 或适当的 wrapping policy。
测试布局行为 #
应在内容开始换行、溢出或留下过多空白的宽度下进行测试,而不只是测试命名设备预设。测试应包括:
- 长单词、URL、翻译文本和较大的字体设置。
- 键盘焦点,以及至少放大到 200% 的缩放。
- 减少动画和两种配色方案。
- 粗指针和细指针。
- 空、最小和最大数据集。
响应式布局应在连续的宽度范围内保持有效,而不是只在少数截图之间切换。
评论
在 GitHub 上阅读或评论(发表评论需要登录)