css - 嵌套主题组件 : apply styles depending on a theme class of the nearest themed parent, 忽略承载主题类的更深层次父级

标签 css sass css-selectors themes css-specificity

我有可以主题化的组件:

<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 类,以便它将样式应用于子级并覆盖祖 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/

相关文章:

jquery - 删除未选择的类别选择选项

html - 居中的 div 内容导致 div 向上离开屏幕?

css - CSS 中无效的属性值使用 : Bourbon SASS. +retinaimage 从 V4 使用 V6 弃用

css - 如何使用 CSS 选择以下布局中的 "a"标记?

css - 将第 n 个 child 中的 CSS 应用到第 n 个 child

css - 如何更改 MS Bot Framework 中的消息字体?

html - 如何默认启用深色主题,而不是切换按钮?

javascript - 如何检查 CSS 背景色是否为白色?

javascript - 当父组件已定义样式时,CSS 未按预期在 React Native 中应用

css - 响应式网格中的固定宽度元素?