嵌套的可组合组件:根据最接近主题的父主题的主题类应用样式,而忽略具有主题类的更深的父元素

如何解决嵌套的可组合组件:根据最接近主题的父主题的主题类应用样式,而忽略具有主题类的更深的父元素

我有一些可以主题化的组件:

<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代码中的声明顺序:最后一个获胜。 ?


我可以想象通过以下方式解决此问题:

  1. 使用极其繁琐的选择器,利用>父组合器和一些东西来覆盖CSS特殊性,以便.theme-red > *胜过.theme-red > * > *,等等。

    我不喜欢这种解决方案,因为它会使CSS不可读。

  2. 使用编程/模板将父级主题传递给所有子级:

    <ComponentFoo @theme="red" as |theme|>
      <ComponentBar @theme={{theme}}>
        <ComponentBaz @theme="blue" as |theme2|>
          <ComponentQuux @theme={{theme2}}/>
        </ComponentBaz>
      </ComponentBar>
    </ComponentFoo>
    

    我不喜欢这种解决方案,因为它也很冗长,并且引入了过多的耦合。

  3. 只需将主题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,组件colorborder,那么您很幸运。
一切都可以简化为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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-