简体   繁体   English

带有动态变量的 SCSS 主题

[英]SCSS Theming with Dynamic Variables

Hi All!大家好!

I'm currently working on a theming feature for a CSS Framework and have run into some issues that I'm hoping you might be able to help out with.我目前正在为 CSS 框架开发主题功能,但遇到了一些问题,希望您能提供帮助。


I have created a SASS Map called $themes , which contains colors for different themes.我创建了一个名为$themes的 SASS 地图,其中包含不同主题的颜色。 I copy-pasted some code from a medium article(who doesn't) and boom my theming works!我从一篇中等文章(谁没有)中复制粘贴了一些代码,然后轰动了我的主题作品! But...但...
This:这个:

@include themify($themes) {
    .btn {
        color: themed('blue');
    }
}

on.在。 every.每一个。 component.成分。 is sloppier code than what I deem maintainable across the copious amounts of styling I'll be doing.比我认为在我将要做的大量样式中可维护的代码更草率。
So...所以...

My Goal我的目标


I'd like to do something super hacky and awesome like this:我想做一些像这样超级hacky和很棒的事情:

@include themify($themes) {
    $blue: themed(blue);
}

I want to theme the variables so all I have to do is add $blue and not a lot of calling mixins and unnecessary mumbo jumbo.我想为变量设置主题,所以我所要做的就是添加$blue而不是很多调用 mixin 和不必要的 mumbo jumbo。
If I could get something like this to work it would look something like this:如果我能让这样的东西工作,它看起来像这样:

.btn {
    background: $blue;
}

All of the theming would be taken care of beforehand!所有的主题都会事先处理好!
But of course it's never that easy because it doesn't work... It would be a godsend if one of you awesome sass magicians could pull some magic with this, I will include you in the source code of awesome contributors.但是当然这从来都不是那么容易,因为它不起作用......如果你们中的一个很棒的sass魔术师可以用这个来施展魔法,那将是天赐之物,我会把你包括在很棒的贡献者的源代码中。

The Code编码


The $themes sass-map: $themes sass-map:

$themes: (
    light: (
        'blue': #0079FF
    ),
    dark: (
        'blue': #0A84FF
    )
);

The copypasta mixin from this awesome Medium Article :这篇很棒的Medium 文章中的 copypasta mixin:

@mixin themify($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;
    }
  }
}

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

Any suggestions on how to accomplish this would be 100% appreciated.任何关于如何实现这一目标的建议将 100% 表示赞赏。 Appreciated enough to add you into the source code as an awesome contributor.非常感谢您将您作为出色的贡献者添加到源代码中。

Thanks in Advance!提前致谢!

Sass does not allow you to create variables on the fly – why you need to manually declare the variables in the global scope. Sass 不允许你动态创建变量——为什么你需要在全局范围内手动声明变量。

$themes: (
    light: (
        'text': dodgerblue,
        'back': whitesmoke 
    ),
    dark: (
        'text': white,
        'back': darkviolet
    )
);


@mixin themify($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;
    }
  }
}

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

@mixin themed {
    @include themify($themes){
        $text: themed('text') !global;
        $back: themed('back') !global;      
        @content;
    }
}

@include themed {
    div {
        background: $back;
        color: $text; 
        border: 1px solid; 
    }
}

The problem about this approach (apart from being tedious to maintain) is that it will bloat your CSS with things that are not related to theming – in the example above border will be repeated.这种方法的问题(除了维护起来很乏味)是它会用与主题无关的东西来膨胀你的 CSS——在上面的例子中,边框会重复。

.theme-light div {
  background: whitesmoke;
  color: dodgerblue;
  border: 1px solid; //  <= 
}

.theme-dark div {
  background: darkviolet;
  color: white;
  border: 1px solid; // <=
}

While I think it is possible to create a setup that scopes each theme to it's own individual stylesheet (eg light.css and dark.css) I think you should consider using CSS variables to handle this虽然我认为可以创建一个设置,将每个主题范围限定到它自己的单独样式表(例如 light.css 和 dark.css),但我认为您应该考虑使用CSS 变量来处理这个问题

$themes: (
    light: (
        'text': dodgerblue,
        'back': whitesmoke 
    ),
    dark: (
        'text': white,
        'back': darkviolet
    )
);

@each $name, $map in $themes {
    .theme-#{$name} {
        @each $key, $value in $map {
            --#{$key}: #{$value};
        }
    }
} 

div {
    background: var(--back);
    color: var(--text); 
    border: 1px solid;
}

CSS output CSS 输出

.theme-light {
  --text: dodgerblue;
  --back: whitesmoke;
}

.theme-dark {
  --text: white;
  --back: darkviolet;
}

div {
  background: var(--back);
  color: var(--text);
  border: 1px solid;
}

Note!笔记! You only need to add the theme class to eg the body tag and the nested elements will inherit the values :)您只需要将主题类添加到例如 body 标签,嵌套元素将继承值:)

I would use such approach (its looks a bit simplier):我会使用这种方法(它看起来有点简单):

$themes-names: (
    primary: (
        background: $white,
        color: $dark-grey,
        font-size: 18px,
    ),
    dark: (
        background: $dark,
        font-size: 10px,
    )
);
$primaryName: "primary";
$darkName: "dark";

@function map-deep-get($map, $keys...) {
    @each $key in $keys {
        $map: map-get($map, $key);
    }
    @return $map;
}

@mixin print($declarations) {
    @each $property, $value in $declarations {
        #{$property}: $value
    }
}

@mixin wrappedTheme($declarations) {
    @each $theme, $value in $declarations {
        $selector: "&.theme-#{$theme}";
        #{$selector} {
            @include print(map-deep-get($themes-names, $theme));
        }
    }
}



$color-default: map-deep-get($themes-names, "primary", "color");

.button-themed {
    /*extend some shared styles*/
    @extend %button;

    /* generate &.theme-primary{...} and &.theme-dark{...} */
    @include wrappedTheme($themes-names); 

    /*override*/
    &.theme-#{$darkName} {
        border: 5px solid $color-default;
        color: $white;
    }

    /* can override/extend specific theme  --modifier */
    &.theme-#{$primaryName}--modifier {
        @include print(map-deep-get($themes-names, $primaryName)); 
        /* will add all from: $themes-names[$primaryName]
        /---
            background: $white,
            color: $dark-grey,
            font-size: 18px,
        */
        font-size: 22px;
    }


    color: $color-default;
}

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

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