hax.github.com icon indicating copy to clipboard operation
hax.github.com copied to clipboard

预处理器支持CSS的@support的可能性

Open hax opened this issue 10 years ago • 19 comments

@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赋值实际会重新格式化,且需要考虑简写属性被展开为多个属性的情况。

hax avatar Jun 29 '15 11:06 hax

先检查 e.style[prop] 是不是 undefined 确定支不支持属性,然后赋值完看值是不是 '' 来决定支不支持属性值?

Justineo avatar Jun 29 '15 13:06 Justineo

@Justineo 是应该这样。本文只是列一个初步的结构。话说你知道有什么预处理器支持@supports的转换吗?

hax avatar Jun 29 '15 20:06 hax

理论上是可行的,不过实际操作会遇到一些问题。参考以下代码:

var decl = 'margin: 0 0 0 0'
document.body.style.cssText = decl
console.log(document.body.style.cssText)  // => 'margin: 0px;'

这样写着写着发现要在前端实现一套 CSS 属性解析器了。

如果需要 JS 参与的话,大多数人应该会选择 Modernizr 这样的方案吧。


不好意思,第一次读此文时没有看到这一句(道理类似):

cssText赋值实际会重新格式化,且需要考虑简写属性被展开为多个属性的情况。

cssmagic avatar Jun 30 '15 03:06 cssmagic

:root 排除了所有不支持 :root 的老浏览器如 IE6,我们一般可假设所有需要 @supports 的特性均是 IE6 不支持的。

IE9 才支持 :root。当然这问题也不大。

编译后specificity增加了2个类/伪类,可简单的在所有选择器前添加 :root:root 以保持一致。

:root 自己也可能会有样式。预处理器(或后处理器)无法获知某个选择符是否是 :root 本身。这是一个大问题。

cssmagic avatar Jun 30 '15 03:06 cssmagic

@cssmagic

所有在选择器里明确写出:roothtml的是可以知道的。问题在于没有它们的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 avatar Jun 30 '15 03:06 hax

@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 avatar Jun 30 '15 03:06 Justineo

@Justineo 嗯,我正是觉得现有的预处理器只能编译出css,是大大削弱了其能力啊,尤其是polyfill的能力。

hax avatar Jun 30 '15 03:06 hax

在Shadow DOM里面的样式应该也有问题,:root[data-cssrules-1] h1 没有对跨边界做处理。

sapjax avatar Jun 30 '15 03:06 sapjax

@sapjax shadow dom 本来就不该处理啊。

hax avatar Jun 30 '15 04:06 hax

@hax 额,难道我理解错了? 你给文档的根节点加属性选择器,然后通过属性选择器来匹配后代节点, 如果我的一个css文件样式是应用到shadow dom内部的,使用了@support,这样转换之后,:root[data-cssrules-1] h1就不能匹配到shadow dom内部的元素了啊(预处理的时候,实际上是无法知道css要应用到什么地方的)。

不过存在一种状况是:可能不支持@support和不支持shadow dom的浏览器有很大一部分是重合的,那么反而没有问题了。

sapjax avatar Jun 30 '15 04:06 sapjax

@sapjax shadow dom本来就是要特殊语法的啊,比如/deep/之类的。它跟其他特性是正交的。

hax avatar Jun 30 '15 05:06 hax

@hax 我指的是那些本身就在shadow dom内部的样式, 开发的时候可能是一个独立css文件,最终会在本地编译或者执行时inlineshadow domshadow root中。

这样的样式因为意图是只作用于shadow dom内部,并不存在跨作用域边界的需求,所以是不需要写/deep/的,但是经过你上面的处理后,反而需要写/deep/才能保持原来的样式生效,并且改变了语义,作用到所有的元素上去了,因为css预处理器是不知道shadow domnodeName是什么的。

sapjax avatar Jun 30 '15 05:06 sapjax

@sapjax 我理解你的意思了。shadow dom的样式应该需要单独处理,比如把 :root 换成 :host 之类的?

hax avatar Jun 30 '15 06:06 hax

@hax 嗯,但是问题是没有办法给:host对应的节点增加[data-cssrules-1]这样的属性了,因为无法确定:host对应的节点是什么。

sapjax avatar Jun 30 '15 06:06 sapjax

@sapjax 嗯,scoped style跟本方法的假设是矛盾的。

不过如你所言,shadow dom的支持浏览器基本上是@supports的真子集。且shadow dom没有比较可靠的polyfill。所以几乎可以不管这个事情。

hax avatar Jun 30 '15 08:06 hax

所以这个想法是我之前说的提供 「浏览器运行时」 的 CSS 处理器?:ghost:

yisibl avatar Oct 26 '15 15:10 yisibl

@hax /deep/ 改成了 >>> https://drafts.csswg.org/css-scoping/?c=M%3BO%3DD#deep-combinator

yisibl avatar Oct 26 '15 15:10 yisibl

@yisibl 这个并不是运行时的css processor,只是一个需要运行时配合的css processor :joy_cat:

hax avatar Nov 04 '15 06:11 hax

@hax :joy: 我差不多就是这个意思。

yisibl avatar Nov 04 '15 06:11 yisibl