Nolan Lawson 有一个非常方便且 非常易于使用的小型 emoji-picker-element。但是考虑到您可能在自己的应用中使用它,因此它应该是可样式化的,以便可以很好地整合到任何地方。 如何允许这种样式化并不完全明显。
然而,我不清楚的是如何允许用户对其进行样式化。如果他们想要不同的背景颜色怎么办?如果他们想让表情符号更大怎么办?如果他们想为输入字段使用不同的字体怎么办?
Nolan 列出了四种可能性(我将以一种有助于我理解的方式稍微重命名它们)。
- CSS 自定义属性:为样式化内容(如
background: var(--background, white);
)。自定义属性穿透 Shadow DOM,因此您实际上是在添加样式挂钩。 - 预构建变体:您可以向自定义元素添加
class
属性,这很容易在 Shadow DOM 内部的 CSS 中访问,这要归功于伪选择器,例如:host(.dark) { background: black; }
。 - Shadow 部分:您将属性添加到想要样式化的内容中,例如
<span part="foo">
,然后外部的 CSS 可以像custom-component::part(foo) { }
那样访问。 - 用户强制:尽管 Shadow DOM 具有“输入为空/输出为空”的特点,但您始终可以访问
element.shadowRoot
并注入<style>
,因此始终有一种方法可以将样式注入。
可能值得一提的是,您 slot
到位的 DOM 可以 从“外部”CSS 进行样式化。
这是一个非常奇怪的问题。我喜欢 Shadow DOM,因为它是在 Web 平台上最接近作用域样式的东西,作用域样式绝对是一个好主意。但我不喜欢任何这些样式解决方案。它们似乎都强迫我考虑我想提供什么样的样式 API 并对其进行文档化,同时又不鼓励组件之间的一致性。
对我而言,DOM 本身就是一个样式 API。我喜欢作用域保护,但如果我想这样做,应该有一种简单的方法可以进入并为内容设置样式。似乎应该有一种非常简单的纯 CSS 方法可以进入内部并仍然使用级联等。也许用连字符分隔的自定义元素名称就足够了?my-custom-elemement li { }
。或者也许它更明确,例如 @shadow my-custom-element li { }
。我只是认为它应该更简单。 可构造样式表 似乎也不是朝着简化方向迈出的一步。
上次我 思考 Web Components 的样式化时,我只是试图弄清楚它最初是如何工作的,并没有考虑如何向组件的使用者公开样式选项。
这在日常工作中是否真的会出现问题?确实如此。
在该线程中(目前)我没有看到任何特别好的样式方法选项。如果我是 Dave,我会倾向于什么都不做。提供最少的样式,如果人们想为其设置样式,他们可以根据自己的组件副本以任何方式进行设置。或者他们可以“强制”样式,这意味着您可以完全自由。
我刚刚开始深入研究所有 Shadow DOM 主题,并在几天前问了自己同样的问题。
我认为“@shadow name-of-element …”是一个很好的解决方案。
为什么不向 CSS 标准化提议它呢?
这也是我关于 Shadow DOM 的主要症结之一。我完全赞成封装,并且对我来说,组件内部定义的样式不会泄漏出去是完全合理的。但是,大多数情况下,我确实希望全局样式进入(不仅仅是诸如
color
之类的基本可继承属性)。是的,自定义属性是一种解决方案,::part()
是另一种(与样式化 UA 提供的组件的内部元素类似,例如范围输入和媒体控件,例如::-webkit-slider-runnable-track
)。问题在于,这些增加了每个组件的 API 表面积,并且命名约定没有保证的一致性。假设我想以相同的方式为页面上的所有
<button>
设置样式。在 light DOM 中,我可以简单地执行以下操作但是,假设在来自不同来源的各种自定义元素中都有按钮,我需要知道所有这些组件的属性。规则将变成以下内容
这将成为维护噩梦。而且,也许其中一些组件没有公开用于设置圆角的属性,因此它们将不符合我们的设计系统。
只有在我们不被诱惑去深入组件并重新实现或分叉它们以适应我们的需求时,封装才能带来可重用性。
已废弃的 CSS
/deep/
组合器只会稍微减轻维护自定义元素(具有 Shadow Roots)的全局样式的负担。出于这个原因,我发现自己创建了没有 Shadow Roots 的自定义元素(使用 lit-element 的原生元素)。但是,Shadow Roots 在处理 ID 作用域时很有用,因此我错过了它。
我希望看到的解决方案是
attachShadow()
的一个选项。就像指定{mode: "open"}
以启用脚本访问 Shadow Roots 一样,传递一个选项(可能是{mode: "open", styles: "inherit"}
),它将允许页面样式流入 Shadow Roots,从而允许自定义元素作者选择加入全局样式,而不会破坏现有实现。这不需要在 CSS 方面为组件使用者担心任何额外的语法,尽管可能存在特异性问题。如果我想为所有可样式化的按钮设置样式,我只需要一个简单的选择器,而无需考虑任何自定义元素。元素内部的样式仍然可以防止泄漏出去。当我制作 Mimcss 库以支持它们时,我开始研究自定义 Web 元素的样式化。我也认为当前的方法存在问题。我对它存在的主要问题是,在全局级别定义的常规样式不适用于 Shadow DOM。我真的认为这是一个错误。自定义 CSS 属性确实可以穿透 Shadow DOM 这一事实更令人困惑:如果自定义 CSS 属性可以,为什么常规样式不行?
自定义 Web 元素肯定会被用于交付组件库。作为库作者,我希望我的组件能够轻松适应宿主应用程序的外观和风格。在每个组件中,可能都会有一些我(作为库作者)想要样式化的“内部”UI 方面和机制,但其余部分(可能是大多数)应该反映宿主应用程序的样式。
对我来说,Shadow Root 下定义的样式的封装意味着它们不会泄漏到外部:从我的组件的样式到宿主应用程序。也就是说,我只希望看到单向封装。
就可构造样式表而言,它纯粹是一个优化功能。如果您有多个自定义 Web 元素实例,则所有样式仅定义一次,但会被所有元素实例“采用”。可构造样式表可以在文档和自定义 Web 元素的实例之间“共享”这一事实很好,如果自定义 Web 元素是与应用程序一起构建的。但是,如果自定义 Web 元素来自第三方库,则此共享将毫无用处。库开发者不知道文档采用的哪些样式表应该被组件采用。