hax.github.com
hax.github.com copied to clipboard
预处理器支持CSS的@support的可能性
@supports (text-shadow: 0 0 .3em gray) {
h1 {
color: transparent;
text-shadow: 0 0 .3em gray;
}
}
可编译为:
:root[data-cssrules-1] h1 {
...
}
cssSupports('text-shadow: 0 0 .3em gray')
let id = 0
function cssSupports(cssDecl) {
let e = document.createElement('div')
e.style.cssText = cssDecl
if (e.style.cssText === cssDecl) {
document.documentElement.dataset[`cssrules-${++id}`] = true
}
}
注意:
- 此代码为简单想法,未经验证。
:root应该可获得优化从而解决属性选择器慢的问题。:root排除了所有不支持:root的老浏览器如IE6,我们一般可假设所有需要@supports的特性均是IE6不支持的。- 编译后specificity增加了2个类/伪类,可简单的在所有选择器前添加
:root:root以保持一致。 - cssText赋值实际会重新格式化,且需要考虑简写属性被展开为多个属性的情况。
先检查 e.style[prop] 是不是 undefined 确定支不支持属性,然后赋值完看值是不是 '' 来决定支不支持属性值?
@Justineo 是应该这样。本文只是列一个初步的结构。话说你知道有什么预处理器支持@supports的转换吗?
理论上是可行的,不过实际操作会遇到一些问题。参考以下代码:
var decl = 'margin: 0 0 0 0'
document.body.style.cssText = decl
console.log(document.body.style.cssText) // => 'margin: 0px;'
这样写着写着发现要在前端实现一套 CSS 属性解析器了。
如果需要 JS 参与的话,大多数人应该会选择 Modernizr 这样的方案吧。
不好意思,第一次读此文时没有看到这一句(道理类似):
cssText赋值实际会重新格式化,且需要考虑简写属性被展开为多个属性的情况。
:root排除了所有不支持:root的老浏览器如 IE6,我们一般可假设所有需要@supports的特性均是 IE6 不支持的。
IE9 才支持 :root。当然这问题也不大。
编译后specificity增加了2个类/伪类,可简单的在所有选择器前添加
:root:root以保持一致。
:root 自己也可能会有样式。预处理器(或后处理器)无法获知某个选择符是否是 :root 本身。这是一个大问题。
@cssmagic
所有在选择器里明确写出:root或html的是可以知道的。问题在于没有它们的class选择器或属性选择器之类的。一个粗暴的办法是所有选择器前加两种 :root:root——
:root:root.xxx, :root:root .xxx { ... } 。(为了不支持 :root 的浏览器,可改为 .-pc-root。)
更简单的办法是要求所有针对根的样式规则明确写出:root,以及减少这样的样式(写到body上去)。这可视为一个limitation,好在写到root上样式的use cases比较少。我们最常见的一种用法是Modernizr。而本方式实际上是取代Modernizr的特性class方式的。而针对user-agent的hack也可以通过扩展一个类似@supports的机制达成。
@hax 好像没有耶,这个除了 CSS 以外还要引入 JS,预处理器一般都不会有这个吧。
@cssmagic 用我说的方法应该不需要处理 cssText 格式了。Modernizr 对开发 CSS 代码是不透明的,而且 @supports 可以查询的 CSS feature 要比 Modernizr 细吧。样式是不是 :root 本身在编译时肯定判断不了,但是也不是不能解决:
:root[data-cssrules-1] h1,
h1:root[data-cssrules-1] {
// ...
}
只是感觉丑陋了点...不然就是得限制只能用 html 或者 :root 而不能用 class 或者其他选择器。
@Justineo 嗯,我正是觉得现有的预处理器只能编译出css,是大大削弱了其能力啊,尤其是polyfill的能力。
在Shadow DOM里面的样式应该也有问题,:root[data-cssrules-1] h1 没有对跨边界做处理。
@sapjax shadow dom 本来就不该处理啊。
@hax 额,难道我理解错了?
你给文档的根节点加属性选择器,然后通过属性选择器来匹配后代节点, 如果我的一个css文件样式是应用到shadow dom内部的,使用了@support,这样转换之后,:root[data-cssrules-1] h1就不能匹配到shadow dom内部的元素了啊(预处理的时候,实际上是无法知道css要应用到什么地方的)。
不过存在一种状况是:可能不支持@support和不支持shadow dom的浏览器有很大一部分是重合的,那么反而没有问题了。
@sapjax shadow dom本来就是要特殊语法的啊,比如/deep/之类的。它跟其他特性是正交的。
@hax
我指的是那些本身就在shadow dom内部的样式, 开发的时候可能是一个独立css文件,最终会在本地编译或者执行时inline到shadow dom的shadow root中。
这样的样式因为意图是只作用于shadow dom内部,并不存在跨作用域边界的需求,所以是不需要写/deep/的,但是经过你上面的处理后,反而需要写/deep/才能保持原来的样式生效,并且改变了语义,作用到所有的元素上去了,因为css预处理器是不知道shadow dom的nodeName是什么的。
@sapjax 我理解你的意思了。shadow dom的样式应该需要单独处理,比如把 :root 换成 :host 之类的?
@hax 嗯,但是问题是没有办法给:host对应的节点增加[data-cssrules-1]这样的属性了,因为无法确定:host对应的节点是什么。
@sapjax 嗯,scoped style跟本方法的假设是矛盾的。
不过如你所言,shadow dom的支持浏览器基本上是@supports的真子集。且shadow dom没有比较可靠的polyfill。所以几乎可以不管这个事情。
所以这个想法是我之前说的提供 「浏览器运行时」 的 CSS 处理器?:ghost:
@hax /deep/ 改成了 >>> https://drafts.csswg.org/css-scoping/?c=M%3BO%3DD#deep-combinator
@yisibl 这个并不是运行时的css processor,只是一个需要运行时配合的css processor :joy_cat:
@hax :joy: 我差不多就是这个意思。