我有可以主题化的组件:
<div class="ComponentFoo theme-blue">
</div>
组件可以相互嵌套。
我希望将应用于父组件的主题传播到其所有子组件。
简单的实现可能如下所示:
<div class="ComponentFoo theme-blue">
<div class="ComponentBar">
<div class="ComponentBaz">
</div>
</div>
<div class="ComponentBar">
</div>
<div class="ComponentBar">
</div>
</div>
.theme-blue.ComponentFoo,
.theme-blue .ComponentFoo {
background-color: blue;
}
.theme-blue.ComponentBar,
.theme-blue .ComponentBar {
color: white;
}
这对于简单的情况来说效果很好,但当主题嵌套时会严重失败:
<div class="ComponentFoo theme-red">
<div class="ComponentBar theme-blue">
<div class="ComponentBaz">
</div>
</div>
</div>
.theme-blue.ComponentBaz,
.theme-blue .ComponentBaz {
border-color: blue,
color: blue,
background-color: white;
}
.theme-red.ComponentBaz,
.theme-red .ComponentBaz {
border-color: red,
color: red,
background-color: white;
}
在这种情况下,我期望 ComponentBaz
假设蓝色主题,因为最近的主题父级是蓝色的。但事实并非如此!
这是因为 .theme-blue .ComponentBaz
和.theme-red .ComponentBaz
选择器与 Baz 组件匹配。 CSS 不关心嵌套的深度。
当两个选择器匹配时,CSS 代码中的声明顺序很重要:最后一个获胜。 😫
我可以想象通过以下方式解决这个问题:
使用极其大量且冗长的选择器来利用
>
父组合器和覆盖 CSS 特异性的东西,以便.theme-red > *
战胜.theme-red > * > *
等我不喜欢这个解决方案,因为它会使 CSS 不可读。
使用编程/模板将父级的主题传递给其所有子级:
<ComponentFoo @theme="red" as |theme|> <ComponentBar @theme={{theme}}> <ComponentBaz @theme="blue" as |theme2|> <ComponentQuux @theme={{theme2}}/> </ComponentBaz> </ComponentBar> </ComponentFoo>
我不喜欢这个解决方案,因为它也非常冗长并且引入了太多的耦合。
只需将主题 HTML 类显式应用到每个主题组件即可。
这就是我正在做的事情,但我不认为这是一个解决方案。更像是一种解决方法,弊大于利。
实现这一目标的更优雅的方法是什么?我想要一个纯 CSS 解决方案,它允许我在父级上使用 HTML 类,以便它将样式应用于子级并覆盖祖 parent 的样式。
由于 CSS 非常有限,我们使用 Sass 预处理器。如果使用 Sass 非常优雅地抽象出来,我不介意使用生成困惑 CSS 的解决方案。
最佳答案
我认为您在 CSS 中设置了太多规则。
为什么不只为主题设置选择器,并让继承来完成这项工作?
.theme-blue {
border-color: blue;
color: blue;
background-color: white;
}
.theme-red {
border-color: red;
color: red;
background-color: white;
}
div {
border-width: 1px;
border-style: solid;
padding: 4px;
}
<div class="ComponentFoo theme-red">I am red
<div class="ComponentBar theme-blue">I am blue
<div class="ComponentBaz">I am nested
</div>
</div>
</div>
<div class="ComponentFoo theme-blue">I am blue
<div class="ComponentBar theme-red">I am red
<div class="ComponentBaz">I am nested
</div>
</div>
</div>
使用 CSS 常量的替代解决方案
.theme-blue {
--border-color: blue;
--color: blue;
--background-color: white;
}
.theme-red {
--border-color: red;
--color: red;
--background-color: white;
}
.ComponentFoo, .ComponentBar, .ComponentBaz {
border-color: var(--border-color);
color: var(--color);
background-color: var(--background-color);
}
.other {
border-color: black;
color: green;
background-color: silver;
}
div {
border-width: 1px;
border-style: solid;
padding: 4px;
}
<div class="ComponentFoo theme-red">I am red
<div class="ComponentBar theme-blue">I am blue
<div class="other">other
<div class="ComponentBaz">I am nested
</div>
</div>
</div>
</div>
<div class="ComponentFoo theme-blue">I am blue
<div class="ComponentBar theme-red">I am red
<div class="other">other
<div class="ComponentBaz">I am nested
</div>
</div>
</div>
</div>
当然,您可以添加另一个类“themable”,并将选择器 .ComponentFoo .ComponentBar .ComponentBaz 更改为“.themable”
这是如何工作的:
在 theme-blue 类中,您定义 CSS 常量的值(有些人称之为 CSS 变量)。该值按照级联规则传播,因此所有子代和后代都将具有相同的值。但是,正如所有 CSS 继承一样,如果子级重新声明此值,则该子级的子级将继承重新声明的值。
现在需要做的就是将标准 CSS 属性设置为此继承值,这是使用 var() 语法完成的
关于css - 嵌套主题组件 : apply styles depending on a theme class of the nearest themed parent, 忽略承载主题类的更深层次父级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63489582/