文字缩放与适配
文字缩放能力是 Web 无障碍的重要组成部分。许多用户需要放大文字才能阅读,包括低视力用户、老年用户以及在远距离使用设备的用户。本节将介绍如何使用相对单位(rem/em)替代固定的 px 值、响应式字体设计策略,以及如何确保页面在文字缩放后仍然布局正常、功能完整。
前置知识
阅读本节前,建议先了解:色彩对比度
为什么文字缩放重要
- WCAG 1.4.4(AA 级):文本可以放大到 200% 而不丢失内容或功能
- WCAG 1.4.10(AA 级):内容在 320px CSS 像素宽度下可用(响应式)
- 用户需求:低视力用户可能需要 400% 甚至更高的缩放级别
CSS 单位选择
px vs rem vs em
| 单位 | 类型 | 说明 | 推荐程度 |
|---|---|---|---|
px | 绝对单位 | 固定像素值,不随用户设置变化 | 不推荐用于文本 |
rem | 相对单位 | 相对于根元素的 font-size | 推荐 |
em | 相对单位 | 相对于父元素的 font-size | 视情况使用 |
% | 相对单位 | 相对于父元素的尺寸 | 适用于特定场景 |
vw/vh | 视口单位 | 相对于视口尺寸 | 适用于大标题 |
clamp() | 函数 | 在最小和最大值之间限制 | 推荐用于响应式字体 |
rem 单位
rem(Root em)相对于根元素 <html> 的 font-size,浏览器默认为 16px。
css
/* 设置根字体大小 */
html {
font-size: 100%; /* 通常等于 16px */
/* 或显式设置 */
font-size: 16px;
}
/* 使用 rem 单位 */
h1 { font-size: 2rem; } /* 32px */
h2 { font-size: 1.5rem; } /* 24px */
h3 { font-size: 1.25rem; } /* 20px */
p { font-size: 1rem; } /* 16px */
.small { font-size: 0.875rem; } /* 14px */当用户在浏览器中增大文字大小时,rem 单位会相应缩放:
css
/* 用户将浏览器字号设为 20px(放大 125%) */
/* rem 基准变为 20px */
/* h1 = 2rem = 40px */
/* h2 = 1.5rem = 30px */
/* p = 1rem = 20px */em 单位
em 相对于当前元素的 font-size(或父元素的 font-size):
css
/* em 的特殊性:嵌套时叠加 */
div {
font-size: 1.2em; /* 19.2px */
}
div div {
font-size: 1.2em; /* 19.2 * 1.2 = 23.04px(叠加!) */
}
/* 使用 rem 避免叠加问题 */
div {
font-size: 1.2rem; /* 始终是 19.2px */
}em 嵌套问题
em 在嵌套元素中会叠加计算,可能导致字体大小不可控。推荐使用 rem 作为基础字号,em 用于 padding、margin 等与当前元素字号相关的间距。
响应式字体
clamp() 函数
clamp() 允许字体大小在最小值和最大值之间随视口宽度变化:
css
/* clamp(最小值, 首选值, 最大值) */
h1 {
font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
}
h2 {
font-size: clamp(1.25rem, 3vw + 0.75rem, 2rem);
}
p {
font-size: clamp(1rem, 1.5vw + 0.75rem, 1.25rem);
}媒体查询断点
css
/* 基础字号 */
html {
font-size: 16px; /* 默认 */
}
/* 大屏幕放大基础字号 */
@media (min-width: 1200px) {
html {
font-size: 18px;
}
}
/* 超大屏幕 */
@media (min-width: 1800px) {
html {
font-size: 20px;
}
}视口单位
css
/* 大标题使用 vw 单位,随视口变化 */
h1 {
font-size: clamp(2rem, 5vw, 4rem);
line-height: 1.1;
}
/* 英雄区域文本 */
.hero-title {
font-size: clamp(2.5rem, 8vw, 6rem);
font-weight: 700;
}避免固定 px 的场景
需要避免的常见错误
html
<!-- 错误:使用固定 px 作为文字大小 -->
<style>
h1 { font-size: 32px; }
p { font-size: 16px; }
.small { font-size: 12px; }
</style>
<!-- 正确:使用 rem 或相对单位 -->
<style>
h1 { font-size: 2rem; }
p { font-size: 1rem; }
.small { font-size: 0.75rem; }
</style>行高和间距
css
/* 行高也使用无单位值(相对值) */
p {
font-size: 1rem;
line-height: 1.6; /* 行高 = 1rem * 1.6 = 1.6rem */
}
/* 间距使用 rem */
.section {
padding: 2rem 1.5rem; /* rem 间距 */
margin-bottom: 1.5rem;
}按钮、输入框等
css
/* 按钮、输入框的 padding 使用 rem */
.btn {
padding: 0.75rem 1.5rem;
font-size: 1rem;
border-radius: 0.25rem;
}
input[type="text"] {
padding: 0.625rem 0.875rem;
font-size: 1rem;
height: auto; /* 不要固定高度 */
min-height: 2.5rem; /* 使用最小高度 */
}Viewport 缩放与页面缩放
viewport meta 标签
html
<!-- 允许用户缩放(默认行为) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 错误:禁止用户缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- 这会严重影响低视力用户 -->不要禁止缩放
user-scalable=no 和 maximum-scale=1.0 会使低视力用户无法放大页面,违反 WCAG 1.4.4。始终允许用户缩放。
检测缩放级别
javascript
// 检测页面缩放级别
function detectZoom() {
const ratio = window.outerWidth / window.innerWidth;
console.log('缩放级别:', ratio);
return ratio;
}
// 使用 matchMedia 检测是否放大
if (window.matchMedia('(prefers-contrast: more)').matches) {
// 用户偏好高对比度
document.documentElement.classList.add('high-contrast');
}
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// 用户偏好减少动画
document.documentElement.classList.add('reduce-motion');
}实战示例:无障碍友好的排版
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文字缩放友好页面</title>
<style>
/* 基础设置 */
:root {
/* 使用 rem 作为基础单位 */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'SF Mono', Monaco, 'Cascadia Mono', monospace;
/* 字号 */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 2rem;
--text-4xl: 2.5rem;
/* 行高 */
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.75;
/* 间距 */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
}
body {
font-family: var(--font-sans);
font-size: var(--text-base);
line-height: var(--leading-normal);
color: #1a1a2e;
/* 最大宽度避免阅读疲劳 */
max-width: 72ch; /* ch 单位基于字符宽度 */
margin: 0 auto;
padding: var(--space-lg);
}
/* heading 使用 clamp() 响应式字体 */
h1 {
font-size: clamp(var(--text-2xl), 5vw + 1rem, var(--text-4xl));
line-height: var(--leading-tight);
margin: var(--space-xl) 0 var(--space-md);
}
h2 {
font-size: clamp(var(--text-xl), 3vw + 0.75rem, var(--text-3xl));
line-height: var(--leading-tight);
margin: var(--space-lg) 0 var(--space-sm);
}
h3 {
font-size: var(--text-xl);
line-height: var(--leading-tight);
margin: var(--space-md) 0 var(--space-sm);
}
p {
margin-bottom: var(--space-md);
}
/* 按钮使用 rem */
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
font-size: var(--text-base);
font-weight: 600;
border: none;
border-radius: 0.375rem;
cursor: pointer;
text-decoration: none;
}
/* 表单使用 rem */
.form-group {
margin-bottom: var(--space-md);
}
.form-group label {
display: block;
margin-bottom: var(--space-xs);
font-weight: 600;
font-size: var(--text-base);
}
.form-group input {
width: 100%;
padding: 0.625rem 0.875rem;
font-size: var(--text-base);
border: 2px solid #ccc;
border-radius: 0.375rem;
box-sizing: border-box;
}
/* 代码块 */
code {
font-family: var(--font-mono);
font-size: 0.875em;
padding: 0.125rem 0.25rem;
background: #f0f0f0;
border-radius: 0.25rem;
}
/* 响应式布局使用相对单位 */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
gap: var(--space-lg);
}
/* 不要固定高度 */
.card {
padding: var(--space-lg);
border: 1px solid #e0e0e0;
border-radius: 0.5rem;
min-height: 0; /* 允许内容撑开 */
}
/* 图片自适应 */
img {
max-width: 100%;
height: auto;
}
</style>
</head>
<body>
<h1>文字缩放友好页面</h1>
<p>
本页面使用 rem 和 clamp() 等相对单位,当用户在浏览器中放大文字时,
所有排版会等比例缩放,不会出现文字溢出或布局错乱。
</p>
<h2>排版示例</h2>
<p>
这是正文文本,使用 <code>1rem</code> 字号和 <code>1.5</code> 行高。
内容区域使用 <code>max-width: 72ch</code> 限制每行约 72 个字符宽度,
提升阅读体验。
</p>
<h3>子标题</h3>
<p>子标题下的正文内容...</p>
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" id="name" placeholder="请输入姓名" />
</div>
<button type="submit" class="btn">提交</button>
</form>
<div class="grid">
<div class="card">
<h3>卡片一</h3>
<p>内容自适应文字缩放...</p>
</div>
<div class="card">
<h3>卡片二</h3>
<p>内容自适应文字缩放...</p>
</div>
</div>
</body>
</html>注意事项
- 不要使用
user-scalable=no:这会阻止用户缩放页面 - 避免固定高度的容器:使用
min-height代替height - 避免固定宽度的文本容器:文字缩放后可能溢出
- 图标和装饰也要使用相对单位:确保等比例缩放
- 测试 200% 缩放:WCAG AA 要求页面在 200% 缩放下仍然可用
最佳实践
- 所有文字大小使用
rem而非px - 使用
clamp()实现响应式字体 - 行高使用无单位值(如
1.5) - 间距使用
rem单位 - 内容区域使用
max-width: 72ch限制每行字符数 - 容器使用
min-height而非固定height - 图片设置
max-width: 100%; height: auto; - 在多种缩放级别下测试页面布局
下一节
继续学习:lang 属性与语言声明