你以为选项卡键盘导航只能靠 JS 写 roving tabindex?今天 scroll-marker-group: tabs 用三行 CSS 把这件事彻底原生化了

写过选项卡的工程师,大概都踩过这个坑——每次加个键盘支持,都要写一套 roving tabindex:记当前索引、监听左右方向键、动态切换 tabIndex、维护 ARIA 属性。代码写完二三十行,回头一看,核心交互逻辑全埋在这些样板代码里了。

Chrome 154 稳定版正式支持了 CSS scroll-marker-group 的 tabs 模式,三行 CSS 把这件事彻底原生化了。

核心区别:roving tabindex vs scroll-marker-group: tabs

传统 roving tabindex 方案(JS):

const tabs = document.querySelectorAll('[role="tab"]');
let currentIndex = 0;

function updateTabs() {
  tabs.forEach((tab, i) => {
    tab.tabIndex = i === currentIndex ? 0 : -1;
    tab.setAttribute('aria-selected', i === currentIndex);
  });
}

tabs.forEach((tab, i) => {
  tab.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowRight') {
      currentIndex = (currentIndex + 1) % tabs.length;
      updateTabs();
      tabs[currentIndex].focus();
    }
    // ... ArrowLeft 等同理
  });
});

scroll-marker-group: tabs 方案(CSS):

.tab-container {
  scroll-marker-group: before tabs;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.tab-item {
  scroll-snap-align: start;
  scroll-target-item: auto;
}

.tab-item::scroll-marker {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: gray;
}

.tab-item::scroll-marker:current {
  background: blue;
}

三行核心声明,浏览器自动接管:tablist/tab/tabpanel ARIA 语义 + roving tabindex 键盘导航 + 方向键循环切换 + 屏幕阅读器树剪枝。

tabs 模式的五个内置能力

tabs 模式不是「简化写法」,是完整的 WAI-ARIA Tabs Pattern 原生化:

语义自动推断: ::scroll-marker-group 获得 tablist 角色,::scroll-marker 获得 tab 角色,对应原素获得 tabpanel 角色——不需要写任何 aria-* 属性。

键盘导航自动实现: 只激活状态下的 ::scroll-marker 是 tab stop,用户用左右方向键在 tab 组内循环切换。Tab 键从当前 marker 直接进入对应 tabpanel 内容——原生支持「tab trap」行为,不需要手动管理焦点。

屏幕阅读器树剪枝: 非活跃 tabpanel 内容自动从无障碍树隐藏,模拟 aria-hidden="true"inert 效果,不需要 JS 控制。

焦点保留规则不同: 激活 link marker 时焦点从 marker 移出;激活 tab marker 时焦点保留在 marker 上——tabs 模式自动遵循 tab 交互规范。

scroll-snap 天然配合: tabs 模式原生支持 scroll-snap-type,滚动和 tab 激活天然对齐,适合做 tabbed carousel。

两个模式怎么选

scroll-marker-group 有两种模式:

  • links 模式(默认): 适合导航列表,语义对应 <nav> + 锚点链接,焦点激活后焦点移到目标元素
  • tabs 模式: 适合选项卡界面,语义对应 tablist/tab/tabpanel,焦点保留在 tab 上

选 tabs 模式的判断标准:用户是否在操作「切换面板」——是,就用 tabs。

浏览器支持现状

Chrome 154 稳定版已支持,Firefox/Safari 暂无信号。生产环境使用记得加 @supports 渐进增强:

.tab-container {
  scroll-marker-group: before tabs;
}

@supports not (scroll-marker-group: tabs) {
  /* 降级方案:传统 JS roving tabindex */
}

三步上手

  1. 列清单: 找出项目中所有用 roving tabindex 实现的选项卡组件
  2. 迁一行: 把 JS 键盘逻辑删掉,加上 scroll-marker-group: before tabs
  3. 验证两项: 键盘左右方向键是否循环、Tab 键是否直接进入 tabpanel 内容

CSS 的进化方向越来越清晰:交互逻辑正在被浏览器接管,开发者只需要声明「我要什么」,不需要再写「怎么做」。scroll-marker-group: tabs 是这个方向上最新的例子。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 96 阅读