如何阻止:root样式渗透到Shadow DOM组件并重置rem计算基准?
问题(翻译自英文)
我用Stencil创建了一个采用Shadow DOM的Web组件,需要完全忽略页面添加的所有CSS,只使用组件自身的样式表。为此我在组件样式表中添加了常规修复代码:
:host { all: initial; }
这个修复能重置页面通过<style>标签或外部样式表设置的基础样式,但如果页面的:root或html元素定义了font-size这类继承样式,计算rem等相对单位时,仍然会使用外部设置的值,而不是浏览器默认大小或我在组件CSS中指定的字号。比如,我已经添加了上述CSS重置,并试图设置16px的基准字号,但开发者工具显示,外部:root上设置的font-size:160px仍会被用来计算组件内font-size:0.875rem元素的最终字号(结果变成了140px)。我针对这个问题做了大量研究,但没找到解决方案,请问有没有办法覆盖:root设置的相对值计算基准样式?
核心原因是:rem单位的计算基准始终是文档根元素(即页面的<html>/:root),而非Shadow DOM内部的任何元素,哪怕你用了all: initial重置组件宿主样式,也改变不了rem的这个特性。
下面是几种可行的解决办法:
1. 改用em单位替代rem
在组件内部统一使用em,把组件宿主的font-size设为你想要的基准值,em会基于父元素(这里就是组件宿主)的字号计算:
:host { all: initial; font-size: 16px; /* 组件内部的基准字号 */ } /* 组件内元素用em */ .component-text { font-size: 0.875em; /* 0.875*16px = 14px,不受外部root影响 */ }
2. 使用CSS变量定义基准值,配合calc转换
在组件内部定义自己的基准字号变量,然后用calc把rem转成基于这个变量的绝对值,或者直接用变量作为基准:
:host { all: initial; --component-base-font: 16px; } .component-text { /* 直接用变量作为基准 */ font-size: calc(var(--component-base-font) * 0.875); /* 14px */ }
如果要兼容已用rem的样式,也可以先计算出外部rem的实际值,再反向转换:
:host { all: initial; --external-rem-size: calc(1rem); /* 获取外部root的rem值 */ --component-base-font: 16px; /* 计算组件内的"自定义rem"比例 */ --component-rem: calc(var(--component-base-font) / var(--external-rem-size)); } .component-text { font-size: calc(0.875rem * var(--component-rem)); }
3. 完全避免相对单位,使用px或独立视窗单位
如果组件样式不需要响应外部字号变化,直接用绝对单位px,或者基于视窗的单位,彻底摆脱外部根元素的影响:
.component-text { font-size: 14px; /* 直接用绝对像素值 */ }
内容的提问来源于stack exchange,提问作者Ed Lucas

