响应式 CSS 与布局

最后更新: 2023-04-16 14:49:19 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

响应式布局应当源于内容约束,而不是设备型号列表。Intrinsic sizing、flexbox、grid、媒体查询和容器查询提供了相互补充的控制手段。

建立响应式基础 #

viewport 声明会使 CSS viewport 与设备宽度匹配:

html
<meta name="viewport" content="width=device-width, initial-scale=1">

不要添加 user-scalable=no 或较低的 maximum-scale;页面缩放是一种无障碍机制。围绕屏幕键盘定位控件的应用可能还需要 visual viewport API,因为可见区域可能发生变化,而 layout viewport 不一定变化。

使用 border-box sizing,并限制替换内容的尺寸:

css
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。

居中并限制阅读内容 #

可读的列需要最大行长和适用于小屏幕的内边距:

css
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 作出不受支持的假设的情况下保留约束:

css
h1 {
  font-size: clamp(2rem, 1.25rem + 3vw, 4rem);
}

选择 Flexbox 或 Grid #

Flexbox 沿主轴排列一维序列。Grid 定义行和列,并且可以跨两个轴对齐项目。

css
.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 适用于需要自动形成列的卡片:

css
.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):

css
.sidebar-layout {
  display: grid;
  grid-template-columns: minmax(12rem, 1fr) minmax(0, 3fr);
}

针对环境约束添加媒体查询 #

媒体查询应当在内容不再适合当前布局时引入布局变化:

css
@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。

输入特性很重要,因为窄触摸屏与窄桌面窗口并不提供相同的交互方式:

css
@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,组件自身可用的空间更有用:

css
.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 上阅读或评论(发表评论需要登录)