如何在scss中实现可切换主题?

the*_*orn 17 css sass

我有一个现有项目,其中包含一个使用语义变量的 scss 文件:

$background-color: white;

body {
    background-color: $background-color;
}
Run Code Online (Sandbox Code Playgroud)

当我向正文添加主题类时,我想将背景更改为黑色:

<body class="theme-dark">...</body>
Run Code Online (Sandbox Code Playgroud)

如果我删除课程(或切换到主题灯),则返回白色。

我还没有找到任何轻量级的方法来在 scss 中执行此操作(为每个主题参数化一个类似乎是一种非常难以维护的方法)。

我找到了一个混合 scss/css-自定义属性解决方案:

原来的:

.theme-light {
    --background-color: white;
}
Run Code Online (Sandbox Code Playgroud)

更新(基于阿马尔的回答):

:root {
    --background-color: white;
}
.theme-dark {
    --background-color: black;
}

$background-color: var(--background-color);
body {
    background-color: $background-color;
}
Run Code Online (Sandbox Code Playgroud)

将 scss 变量定义为具有 css 变量扩展作为值,即(从上面):

$background-color: var(--background-color);
Run Code Online (Sandbox Code Playgroud)

生成以下 css:

:root { --background-color: white; }
.theme-dark { --background-color: black; }
body { background-color: var(--background-color); }
Run Code Online (Sandbox Code Playgroud)

这似乎就是我们想要的......?

我喜欢它,因为它只需要更改$background-color(不是非常大的 scss 文件中的每个用法)的定义,但我不确定这是否是一个合理的解决方案?我对 scss 还很陌生,所以也许我错过了一些功能..?

Ama*_*yla 13

使用 SCSS 可以做到这一点,但您必须向所有想要设置主题的元素添加样式。这是因为 SCSS 是在构建时编译的,您无法使用类切换变量。一个例子是:

$background-color-white: white;
$background-color-black: black;

body {
  background-color: $background-color-white;
}

.something-else {
  background-color: $background-color-white;
}

// Dark theme
body.theme-dark {
  background-color: $background-color-black;

  .something-else {
    background-color: $background-color-black;
  }
}
Run Code Online (Sandbox Code Playgroud)

目前最好的方法是使用 CSS 变量。您可以像这样定义默认变量:

:root {
  --background-color: white;
  --text-color: black;
}

.theme-dark {
  --background-color: black;
  --text-color: white;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在元素中使用这些变量,如下所示:

body {
  background-color: var(--background-color);
  color: var(--text-color);
}
Run Code Online (Sandbox Code Playgroud)

如果body元素具有theme-dark类,它将使用为该类定义的变量。否则,它将使用默认的根变量。


hun*_*tar 8

所有功劳都归于德米特里·博罗迪 (Dmitry Borody)

我会推荐一种类似于这篇 Medium 文章中提到的方法。通过这种方法,您可以指定哪些类需要主题化,而无需特别提及主题名称,因此可以一次应用多个主题。

首先,您设置一个包含主题的 SASS 地图。键可以是任何对您有意义的键,只需确保每个主题对同一事物使用相同的名称即可。

$themes: (
  light: (
    backgroundColor: #fff,
    textColor: #408bbd,
    buttonTextColor: #408bbd,
    buttonTextTransform: none,
    buttonTextHoverColor: #61b0e7,
    buttonColor: #fff,
    buttonBorder: 2px solid #fff,
  ),
  dark: (
    backgroundColor: #222,
    textColor: #ddd,
    buttonTextColor: #aaa,
    buttonTextTransform: uppercase,
    buttonTextHoverColor: #ddd,
    buttonColor: #333,
    buttonBorder: 1px solid #aaa,
  ),
);
Run Code Online (Sandbox Code Playgroud)

然后使用mixinfunction配对添加主题支持。

body {
  background-color: white;
  @include themify {
    background-color: theme( 'backgroundColor' );
  }
}

.button {
  background-color: lightgray;
  color: black;

  @include themify {
    background-color: theme( 'buttonBackgrounColor' );
    color: theme( 'buttonTextColor' );
  }

  &:focus,
  &:hover {
    background-color: gray;
    
    @include themify {
      background-color: theme( 'buttonBackgroundHoverColor' );
      color: theme( 'buttonTextHoverColor' );
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您要添加大量主题或主题将涉及很多内容,您可能需要稍微不同地设置 SCSS 文件,以便所有主题不会使您的主 CSS 文件变得臃肿(例如上面的例子就可以了)。实现此目的的一种方法可能是创建一个themes.scss文件并复制需要主题化的任何选择器路径,并使用第二个仅输出该themes.scss文件的构建脚本。

混音

@mixin themify( $themes: $themes ) {
  @each $theme, $map in $themes {

    .theme-#{$theme} & {
      $theme-map: () !global;
      @each $key, $submap in $map {
        $value: map-get(map-get($themes, $theme), '#{$key}');
        $theme-map: map-merge($theme-map, ($key: $value)) !global;
      }

      @content;
      $theme-map: null !global;
    }

  }
}
Run Code Online (Sandbox Code Playgroud)

功能

@function themed( $key ) {
  @return map-get( $theme-map, $key );
}
Run Code Online (Sandbox Code Playgroud)