如何解决嵌套的可组合组件:根据最接近主题的父主题的主题类应用样式,而忽略具有主题类的更深的父元素
我有一些可以主题化的组件:
<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类,以便将样式应用于子级并覆盖祖父母的样式。
由于CSS非常有限,因此我们使用的是Sass预处理程序。如果通过Sass非常优雅地抽象出CSS,我不会介意使用产生混乱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”
这是如何工作的:
在主题蓝色的类中,您定义CSS常量的值(有些人称为CSS变量)。此值将按照级联规则传播,因此所有子级和后代将具有相同的值。但是,与所有CSS继承一样,如果子级重新声明此值,则子级将继承重新声明的值。
现在所需要做的就是将标准CSS属性设置为该继承的值,这是使用var()语法完成的
,通过回答您的问题,我认为您想选择一个孩子,而不是孙子。为此,在CSS中,我们有>
选择器。
例如:
.test_class_1 > .test_class_2
此处将选择类别为test_class_2
的孩子,但不会选择test_class_2
div内部的孙子。
据此,我对您的CSS进行了一些修改:
.theme-blue>.ComponentBaz,.theme-blue>.ComponentBaz {
border-color: blue;
color: blue;
background-color: white;
}
.theme-red>.ComponentBaz,.theme-red>.ComponentBar {
border-color: red;
color: red;
background-color: white;
}
我在这里添加了>
,它选择了ComponentBaz
作为一个类的div,但不是该div中的div。我还用“;”替换了“,”也许那只是错字。
这是JSfiddle链接:https://jsfiddle.net/o20dLy7k/
,如果您可以将css
限制为主题background
,组件color
和border
,那么您很幸运。
一切都可以简化为2种颜色-在我的摘录中,第一幅画为lblue和black-只有3种微妙的情况,而且它们都是微妙的,仅是因为文本的可见性:
- 浅蓝色主题+浅蓝色(或黑色和黑色)
- 继承的浅蓝色主题+浅蓝色组件
- 浅蓝色主题+继承的组件浅蓝色
不再担心。认真地说,所有内容都具有魅力,validator是绿色的,没有任何注释。
第一幅图-2种颜色-测试可能的烦恼,第二幅图丑陋,但内容丰富-多种组合的十几种颜色。我使用::before{ content: attr(class)}
作为内容,因此CSS看上去比平常差。
我认为,非常非常复杂的情况(如在图形的中心)不应该纠正其文字颜色(特殊处理更改了旧版),代码过多。如果您愿意,可以使用不同于color
的东西,问题将消失。
当然,使用的颜色必须彼此可见。
function switcher(){
var element = document.getElementById("body");
if(element.classList){ element.classList.toggle("shadow");}
else{
var classes = element.className.split(" ");
var i = classes.indexOf("shadow");
if(i >= 0) classes.splice(i,1);
else classes.push("shadow");
element.className = classes.join(" ");}}
body{ color: #000}
p,span,h1,h2,h3,h4,h5,h6,div::before{ background: #fff}
/* themes */
.shadow .black{ background: #000; color: #fff}
.black{ background: #000;}
.red{ background: #f00;}
.green{ background: #0d0;}
.blue{ background: #00f}
.white{ background: #fff}
.gold{ background: gold}
.purple{ background: purple}
.grey{ background: grey}
.teal{ background: teal}
.lblue{ background: #5ae}
/* components */
.Cblack{ border-color: black; color: black}
.Cred{ border-color: red; color: red}
.Cgreen{ border-color: green; color: green}
.Cblue{ border-color: blue; color: blue}
.Cwhite{ border-color: white; color: white}
.Cgold{ border-color: gold; color: gold}
.Cpurple{ border-color: purple; color: purple}
.Cteal{ border-color: teal; color: teal}
.Clblue{ border-color: #5ae; color: #5ae}
.Cwhite::before,.Cwhite>::before{ background: #aaa}
/* shadow */
.shadow .lblue .Clblue::before,.shadow .red .Cred::before,.shadow .green .Cgreen::before,.shadow .blue .Cblue::before,.shadow .white .Cwithe::before,.shadow .gold .Cgold::before,.shadow .purple .Cpurple::before,.shadow .grey .Cgrey::before,.shadow .teal .Cteal::before,.shadow .black .Cblack::before,.shadow .Clblue .lblue::before,.shadow .Cred .red::before,.shadow .Cgreen .green::before,.shadow .Cblue .blue::before,.shadow .Cwhite .withe::before,.shadow .Cgold .gold::before,.shadow .Cpurple .purple::before,.shadow .Cgrey .grey::before,.shadow .Cteal .teal::before,.shadow .Cblack .black::before,.shadow .lblue.Clblue::before,.shadow .red.Cred::before,.shadow .green.Cgreen::before,.shadow .blue.Cblue::before,.shadow .white.Cwhite::before,.shadow .gold.Cgold::before,.shadow .purple.Cpurple::before,.shadow .grey.Cgrey::before,.shadow .teal.Cteal::before,.shadow .black.Cblack::before{
border: 1px dotted black;
text-shadow: -1px 0 1px black,0 1px 1px black,1px 0 1px black,0 -1px 1px black;}
.shadow .black .Cblack::before,.shadow .black.Cblack::before{
border: 1px dotted white;
text-shadow: -1px 0 1px white,0 1px 1px white,1px 0 1px white,0 -1px 1px white}
.shadow ::before,.shadow p,.shadow span,.shadow h4{ background: 0}
/* for snippet only */
button{ position: fixed; top: 45vh; right: 0; background: #acf; border: 12px solid white; color: black; padding: 25px; border-radius: 50%; outline: 0}
div{ margin: 10px 8px; padding: 10px 8px; border: 3px solid transparent;}
div::before{ content: attr(class); border-radius: 8px}
p,div::before{ padding: 2px 8px;}
<body id="body" class="x">
<div class="lblue Clblue"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="Cblack">
<div class="black">
<div class="Cblack">
<div class="Clblue">
<div class="lblue">
<div class="Cblack"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="Clblue"><h4>h4</h4><p>paragraph</p><span>span</span></div>
<div class="black"><h4>h4</h4><p>paragraph</p><span>span</span>
</div></div></div></div></div></div></div></div>
<div class="red Cgold">
<div class="black">
<div class="black Cblack">
<div class="red Cblack">
<div class="green">
<div class="blue ">
<div class="white ">
</div></div></div>
<div class="gold ">
<div class="purple ">
<div class="grey Cred"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="teal ">
<div class="white Cwhite">
</div></div></div>
<div class="teal Cteal">
<div class="white Cwhite">
<div class="blue">
<div class="black Cteal">
<div class="grey Cred">
</div></div></div>
<div class="Cpurple"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="Cred"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="Cgold"><h4>h4</h4><p>paragraph</p><span>span</span>
</div></div></div>
<div class="green">
<div class="white Cgreen">
<div class="red Cred"><h4>h4</h4><p>paragraph</p><span>span</span>
<div class="Cwhite"><h4>h4</h4><p>paragraph</p><span>span</span>
</div></div></div>
</div></div></div>
</div></div></div>
</div></div></div>
<button onclick="switcher()"><b>switch</b></button></body>
补充
如果可以确定,该文本将始终放在标签内,请不要忘记阴影
p,h6{ background: #fff}
如果您可以排除太亮的颜色-就是这样。如果没有...例外...
我将此添加到了代码段中,但.Cwhite::before
您需要确定您的意思。如果您以传统意义上的主题表示,则这些解决方案将引入难以发现的错误!。如果多个规则匹配,则将应用规则之间不共有的所有属性。例如,假设一个黑暗的主题更改了前景和背景,而霓虹灯主题更改了前景和调色板。无论顺序如何,背景(深色)和调色板(霓虹色)都将适用。
一种解决方案是对主题进行规范化,以使每个主题都可以设置由任何主题设置的所有属性。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。