写过前端的都踩过这个坑——CSS 想读 HTML 里的 data-* 属性,以前只能靠 JS 先去读再写到 CSS 变量里,attr() 在 content 以外全是摆设。今天 Firefox 155 把这件事彻底原生化了

写过前端的都踩过这个坑——CSS 想读 HTML 里的 data-* 属性,以前只能靠 JS 先去读再写到 CSS 变量里,attr() 在 content 以外全是摆设。今天 Firefox 155 把这件事彻底原生化了。

这个坑踩得有多深?

以前你想让一个元素的宽度由 HTML 属性直接决定,得这么写:

document.querySelectorAll('.meter').forEach(el => {
  const pct = el.dataset.pct;
  el.style.setProperty('--pct', pct + '%');
});
.meter { width: var(--pct); }

五年前是这样,三年前还是这样,现在你打开 MDN 查 attr(),Chrome 133 就支持了,Firefox 155 刚发布,Safari 还在路上。但 90% 的前端项目里,这套 JS 桥接代码还在线上跑着。

现在的写法:attr() 直接出类型值

Firefox 155 稳定版把 CSS Values and Units Level 5 的 attr() 类型推导落地了,核心就是两个新语法:

单位后缀速写:

.card { width: attr(data-width px); }
.progress { height: attr(data-pct %); }
.rotate { transform: rotate(attr(data-deg deg)); }

type() 显式声明 + 回退值:

.meter { width: attr(data-pct type(), 60%); }
.badge { background: attr(data-color type(), #eee); }
.icon { font-size: attr(data-size rem, 1rem); }

Firefox 155 之前,这个写法在 Chrome 里写是能用的,但 Firefox 一直是实验性功能,需要手动开 layout.css.attr.enabled从今天起,写完不用开任何 flag,线上直接用。

三类典型场景,JS 全部省掉

进度条——最常见的需求:

<div class="meter" data-pct="75"></div>
.meter { width: attr(data-pct type(), 0%); }

属性值直接驱动视觉,进度改了只要改 HTML,不用动一行 JS。

动态主题色——以前靠 CSS 变量桥接:

<div class="chip" data-tone="#f43f5e" data-radius="8px"></div>
.chip {
  background: attr(data-tone type(), #eee);
  border-radius: attr(data-radius type(), 4px);
}

单个 data 属性驱动多个 CSS 属性,属性一改样式全变。

带 type() 的 if() 条件样式——这个组合让 CSS 真正能做状态机:

.box {
  background: if(attr(data-state) == loading, gold,
            if(attr(data-state) == error, tomato,
            steelblue));
}

以前这种逻辑只能写 class + JS 切换,现在纯 CSS 就能跑。

为什么这套方案比 CSS 变量更好

这里有个微妙的问题:既然 @property + CSS 变量也能做类型推导,attr() 的价值在哪?

@property + var() attr() + type()
值写在哪 CSS 变量 HTML 属性
JS 修改 style.setProperty() setAttribute('data-x', val)
DOM 结构 需要配合 HTML 直接从 HTML 读,不跨元素
渐进增强 需要注册 @property 内置回退值,天然降级

attr() 的核心优势:HTML 本身就是数据源,不需要额外部署 CSS 变量同步机制。对于服务端渲染、静态生成、多人协作场景,让设计师直接在 HTML 里配置值,比维护一套 CSS 变量命名约定要直观得多。

渐进增强写法

Firefox 155 + Chrome 133 + Safari 15.4 以下的浏览器不支持 type() 语法,但回退值让降级天然安全:

.meter { width: 60%; }
@supports (width: attr(data-pct type())) {
  .meter { width: attr(data-pct type(), 60%); }
}

不支持的浏览器走第一条,支持的浏览器走第二条,两个世界的行为都是合理的。

接下来怎么做

Firefox 155 已经稳定,下一步看看你项目里那些 element.style.setProperty('--xxx', value) 的代码,有多少是可以直接用 attr() 替代的。进度条、主题配置、动态尺寸——这三个场景优先级最高。

下次写组件需要动态样式,先问自己一句:能不能直接写在 HTML 属性里?

评论区

0 条评论

登录后可评论。

阿柯·前端架构 13 阅读