直接用 prefers-reduced-motion、prefers-contrast 和 color-scheme 媒体查询可覆盖80%基础适老化需求,无需JS或双CSS逻辑;现代浏览器已原生支持系统级辅助偏好检测,比手动维护状态更可靠。

直接用 prefers-reduced-motion、prefers-contrast 和 color-scheme 这三个媒体查询就能覆盖 80% 的基础适老化需求,不需要额外 JS 或两套 CSS 切换逻辑。
用原生媒体查询检测系统级偏好
现代浏览器(Chrome 84+、Firefox 97+、Safari 15.4+)已支持通过 CSS 媒体查询读取用户在操作系统中设置的辅助功能选项。这比自己维护 isOldMode 状态更可靠,也避免了 localStorage 同步延迟或跨设备不一致的问题。
常见组合写法:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
<p>@media (prefers-contrast: high) {
body {
background: #000 !important;
color: #fff !important;
}
a, button, input {
outline: 2px solid #ff0 !important;
}
}</p><p>@media (color-scheme: dark) {
:root {
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
}
}注意:prefers-contrast: high 在 Safari 中仍为实验性支持,需加 -webkit- 前缀;而 prefers-reduced-motion 兼容性最好,建议优先落地。
立即学习“前端免费学习笔记(深入)”;
字体放大不能只靠 rem 或 zoom
单纯设置 html { font-size: 18px } 或用 zoom: 1.2 会导致表单控件、图片、SVG 图标等元素比例失真,且部分安卓 WebView 会忽略 zoom。
更稳妥的做法是:
- 用
clamp(1rem, 4vw, 1.5rem)控制正文行内字体,兼顾小屏可读与大屏不溢出 - 对按钮、输入框等交互元素单独用
min-height和padding保证点击热区 ≥ 44×44px - 禁用
user-scalable=no,允许用户双指缩放(iOS Safari 会自动禁用该 meta 标签的强制限制) - 所有图标用 SVG 内联或
<img>+height属性,避免background-image缩放模糊
语音播报要绕过 aria-live 的坑
很多开发者直接给容器加 aria-live="polite" 就以为能朗读,但实际中屏幕阅读器(如 VoiceOver、TalkBack)对动态插入文本的响应有延迟,且不触发语音合成 API。
真正可控的方案是显式调用 SpeechSynthesis.speak():
function speak(text) {
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
speechSynthesis.speak(utterance);
}
}关键点:
- 必须由用户主动触发(如点击按钮),否则 Chrome 会静音
- 不要在
mounted或onLoad里自动调用,会被浏览器拦截 - 同一时间只允许一个
utterance播放,重复调用前需先speechSynthesis.cancel() - Android WebView 需要额外申请
android.permission.RECORD_AUDIO才能启用语音识别输入
无障碍焦点管理常被忽略的细节
适老化页面最常出问题的不是颜色或字体,而是键盘导航断裂:Tab 键跳过按钮、跳入 iframe 后出不来、模态框关闭后焦点丢失。
必须手动控制的三件事:
- 所有可交互元素必须有
tabindex="0"(包括<div>按钮),且禁用tabindex="-1"以外的负值 - 打开模态框时,用
element.focus()把焦点移到第一个可操作项;关闭时,用previousElement.focus()回退到触发源 - 禁用
outline: none,改用outline: 2px solid #007aff+outline-offset: 2px保证高对比可见性
真正的难点不在“怎么加”,而在“什么时候删”——比如切换适老模式时,旧焦点状态不会自动重置,得手动清理并重新分配。



















