简体   繁体   English

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

[英]How to implement switchable themes in scss?

I have an existing project with a scss file that uses semantic variables:我有一个带有使用语义变量的 scss 文件的现有项目:

$background-color: white;

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

I would like to change the background to black when I add a theming class to the body:当我向正文添加主题类时,我想将背景更改为黑色:

<body class="theme-dark">...</body>

and back to white if I remove the class (or switch to a theme-light).如果我删除课程(或切换到主题灯),则返回白色。

I haven't found any light-weight methods to do this in scss (parametrizing a class for each theme seems like a very hard to maintain approach).我还没有在 scss 中找到任何轻量级的方法来做到这一点(为每个主题参数化一个类似乎是一种很难维护的方法)。

I've found a hybrid scss/css-custom properties solution:我找到了一个混合 scss/css-custom 属性解决方案:

original:原来的:

.theme-light {
    --background-color: white;
}

update (based on Amar's answer):更新(基于 Amar 的回答):

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

$background-color: var(--background-color);
body {
    background-color: $background-color;
}

Defining the scss variable as having a css-variable expansion as the value, ie (from above):将 scss 变量定义为具有 css 变量扩展作为值,即(从上面):

$background-color: var(--background-color);

generates the following css:生成以下CSS:

:root { --background-color: white; }
.theme-dark { --background-color: black; }
body { background-color: var(--background-color); }

which seems to be what we want...?这似乎是我们想要的......?

I like it since it only requires changing the definition of $background-color (not every usage in a very large scss file), but I'm unsure if this is a reasonable solution?我喜欢它,因为它只需要更改$background-color的定义(不是非常大的 scss 文件中的所有用法),但我不确定这是否是一个合理的解决方案? I'm pretty new to scss, so maybe I've missed some feature..?我对scss很陌生,所以也许我错过了一些功能..?

Doing this with SCSS is possible but you would have to add styles to all elements you want to theme.可以使用 SCSS 执行此操作,但您必须为所有要设置主题的元素添加样式。 That is because SCSS is compiled at build-time and you can't toggle the variables with classes.这是因为 SCSS 是在构建时编译的,您不能使用类切换变量。 An example would be:一个例子是:

$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;
  }
}

The best way to currently do it is by using CSS variables.目前最好的方法是使用 CSS 变量。 You would define the default variables like this:您将像这样定义默认变量:

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

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

Then, you would use these variables in your elements like this:然后,您将在元素中使用这些变量,如下所示:

body {
  background-color: var(--background-color);
  color: var(--text-color);
}

If the body element has the theme-dark class, it will use the variables defined for that class.如果body元素具有theme-dark类,它将使用为该类定义的变量。 Otherwise, it will use the default root variables.否则,它将使用默认的根变量。

All credit goes to Dmitry Borody所有功劳归功于德米特里·博罗迪

I would recommend an approach like what is mentioned in this Medium article .我会推荐一种类似于这篇 Medium 文章中提到的方法。 With this approach, you can assign what classes need to be themed without specifically mentioning the theme name so multiple themes can be applied at once.使用这种方法,您可以分配需要主题的类,而无需特别提及主题名称,因此可以一次应用多个主题。

First, you set up a SASS map containing your themes.首先,您设置一个包含主题的 SASS 地图。 The keys can be whatever makes sense to you, just make sure that each theme is using the same name for the same thing.键可以是对您有意义的任何内容,只需确保每个主题对同一事物使用相同的名称。

$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,
  ),
);

Then use the mixin and function pair to add theme support.然后使用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' );
    }
  }
}

If you're going to be adding a lot of themes or a theme will be touching a lot of stuff, you might want to set up your SCSS files a little differently so that all the theming doesn't bloat your main CSS file (like the example above would do).如果你要添加很多主题或者一个主题会涉及很多东西,你可能需要稍微不同地设置你的 SCSS 文件,这样所有的主题都不会膨胀你的主 CSS 文件(比如上面的例子就可以了)。 One way to do this might be to create a themes.scss file and replicate any selector paths that need theming and have a second build script that outputs just the themes.scss file.一种方法可能是创建一个themes.scss文件并复制任何需要主题化的选择器路径,并使用第二个构建脚本仅输出themes.scss文件。

The Mixin混音

@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;
    }

  }
}

The Function功能

@function themed( $key ) {
  @return map-get( $theme-map, $key );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM