CSS 教程 / 使用 min、max 和 clamp 构建流式 CSS 布局
流式布局使尺寸能够适应可用空间,同时施加有用的限制。相对宽度、最大宽度以及 clamp() 等函数可以保持内容可读,而无需将相同的固定尺寸强加到每个屏幕上。
区分视口宽度和可用宽度
在浏览器中显示网页的区域称为视口。然而,正文内卡片的可用宽度可能小于视口宽度。这是因为卡片必须适应剩余空间,排除父元素的外边距、与其他列的间距以及最大宽度限制。
width: 100% 中的百分比使用普通块布局中包含块的宽度。100vw 基于视口宽度,因此即使将其放在一个小卡片中,它也可以与整个屏幕一样大。当垂直滚动条和外边距添加时,可能会发生水平溢出。对于正文内元素的宽度,首先考虑百分比或自动大小。
| 指定 | 窄父元素 | 宽父元素 | 使用标准 |
|---|---|---|---|
| width: 48rem | 可能大于父元素 | 保持48rem | 不适合作为可缩短文本的默认值 |
| width: 100% | 缩小以匹配父元素 | 继续增加到父元素宽度 | 需要填充父元素的元素 |
| width: 100%; max-width: 48rem | 缩小到小于48rem | 最多48rem | 需要有限阅读宽度的文本 |
| min-width: 48rem | 不会小于48rem | 根据不同的尺寸规则决定 | 仅在需要最小尺寸时使用 |
约束并居中文本区域
*, *::before, *::after { box-sizing: border-box; }
.content {
width: 100%;
max-width: 48rem;
margin-inline: auto;
padding-inline: 1rem;
}
使用border-box时,内边距和边框适合在声明的宽度内。如果根字体为16px,父元素宽度为390px,则 .content 元素的边框盒宽度为 390px,每侧的16px内边距剩余358px用于内容。在父元素宽度为1000px时,外部宽度停止在768px,自动边距使其居中。更多细节,请参见CSS 宽度、高度、min/max 限制与 box-sizing。
将 content-box 应用于同一个元素会给 width: 100% 添加内边距,使其比父元素更宽。另一方面,边距也不包含在 border-box 内。当同时指定 width: 100% 和固定的两侧边距时,必须检查总和。如果 min-width 和 max-width 冲突,也要检查 min-width,因为 min-width 可能会导致元素变大。
使用 min()、max() 与 clamp()
min() 从候选值中选择较小值。width: min(100%, 48rem) 使用父元素宽度和 48rem 中较小的值,因此之前指定的 100% 和最大宽度限制可以在一条声明中表达。max() 则选择候选值中的较大者。可以在下方完整示例的 .cards 规则中,把 gap: 1rem 临时改为 gap: max(12px, 2vw),观察间距保持至少 12px,并随视口继续增大;比较后恢复为 1rem。max() 选择较大值,并不等同于设置上限的 max-width。
.content { width: min(100%, 48rem); }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); }
clamp(minimum, preferred, maximum) 在最小值不大于最大值时,将首选值限制在这两个边界之间。在上面的标题中,rem 基于根字体大小,vw 基于视口宽度的 1%。如果根字体大小为 16px,最小值为 24px,最大值为 40px,首选值为 16px 加上屏幕宽度的 2%。
| 视口宽度 | 计算中心值 | 实际标题大小 |
|---|---|---|
| 320px | 16 + 6.4 = 22.4px | 最小值 24px |
| 600px | 16 + 12 = 28px | 28px |
| 1200px | 16 + 24 = 40px | 40px |
| 1600px | 16 + 32 = 48px | 最大 40px |
如果你让正文文本仅依赖屏幕宽度,它在窄屏或放大情况下可能会变得过小。在标题中使用流式尺寸时,一个好的起点是设置一个可读的下限,并保持正文使用基于rem的大小。仅仅因为你混用了rem,并不自动保证放大可访问性,所以你还需要检查实际浏览器的放大和默认字体设置。你可以查看MDN的clamp()定义和无障碍解释中每个参数的作用。
运行流式卡片与图片示例
将以下代码以 UTF-8 格式保存为 fluid-size.html 并在浏览器中打开。图片是包含在文件中的 SVG 数据,因此无需下载单独的图像或外部库。在宽屏和大约 390px 的窄屏上比较 body 的宽度、卡片列数和标题大小。如果您想灵活地放置多个卡片,请查看 CSS Grid:repeat、auto-fit、auto-fill 与命名区域 了解如何设置列的最小宽度。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>fluid-size.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.content { width: min(100%, 48rem); margin-inline: auto; padding: 1rem; }
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); line-height: 1.3; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1rem; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.card img { display: block; max-width: 100%; height: auto; margin-bottom: 1rem; }
.token { font-family: monospace; }
</style>
</head>
<body>
<main class="content">
<h1>Size elements to fit the screen and content</h1>
<p>The content is at most 48rem wide and shrinks to fit its parent on smaller screens.</p>
<div class="cards">
<article class="card"><img src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22400%22%20height%3D%22300%22%20viewBox%3D%220%200%20400%20300%22%3E%3Crect%20width%3D%22400%22%20height%3D%22300%22%20fill%3D%22%23287699%22%2F%3E%3Crect%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%23205c32%22%2F%3E%3Crect%20y%3D%22265%22%20width%3D%22400%22%20height%3D%2235%22%20fill%3D%22%239e3131%22%2F%3E%3Ccircle%20cx%3D%22200%22%20cy%3D%22150%22%20r%3D%2270%22%20fill%3D%22%23f6ae4c%22%2F%3E%3Crect%20x%3D%2212%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3Crect%20x%3D%22358%22%20y%3D%22120%22%20width%3D%2230%22%20height%3D%2260%22%20fill%3D%22white%22%2F%3E%3C%2Fsvg%3E" width="400" height="300" alt="Practice drawing with upper and lower bands and center circle"><h2>card with picture</h2><p>The image stays within the card width and preserves its original aspect ratio.</p></article>
<article class="card"><h2>A heading and description that wrap onto several lines</h2><p>Without a fixed height, the card grows as its text gets longer.</p></article>
<article class="card"><h2>Long identifier</h2><p class="token">ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</p></article>
</div></main>
</body>
</html>
卡片的 minmax(min(100%, 14rem), 1fr) 使用默认的最小宽度 14rem,但如果父元素更窄,最小轨道尺寸会缩小到父级的可用宽度。auto-fit 按可容纳的列数建立重复轨道,并在折叠空轨道后由 1fr 分配剩余空间。由于卡片的内边距,文本和图片的实际宽度小于卡片的外部宽度。不要根据设备名称判断列数,而应根据父元素可用的宽度判断。
min-width: 0 移除了可能阻止卡片缩小的最小尺寸约束。overflow-wrap: anywhere 在需要时为长 URL 或标识符提供换行。它们的作用不同:仅允许盒子缩小并不会自动换行文本。换行改变的是展示效果,而不是复制的字符串值。
一次测试一个尺寸更改
- 将屏幕缩小到390px。文本应适应屏幕,卡片应垂直排列,图片上下的条带以及中心的圆形都应可见。
- 只将
.content的宽度更改为48rem。检查文本为何在窄屏幕上溢出后,将其恢复为min(100%, 48rem)。 - 只将标题的最大值更改为
3rem,并在更宽的屏幕上查看差异。计算中心值可以解释为何窄屏幕尺寸不变化,并恢复到2.5rem。 - 只从
.card中移除overflow-wrap: anywhere。检查长标识符是否超出卡片,再恢复该声明。之后分别改变根字号和浏览器缩放,逐项观察并记录结果,避免同时改变多个条件。
在将隐藏溢出的 overflow-x: hidden 插入整个页面之前,检查宽度、边距、最小宽度和字符串。如果将其从视图中隐藏,内容或操作元素可能会被截断而无法解决原因。关于响应式配置的基本原则,你也可以参考 MDN 的响应式设计指南 。




