带有动态变量的 SCSS 主题

Posted

技术标签:

【中文标题】带有动态变量的 SCSS 主题【英文标题】:SCSS Theming with Dynamic Variables 【发布时间】:2020-01-08 22:25:26 【问题描述】:

大家好!

我目前正在开发 CSS 框架的主题功能,遇到了一些问题,希望您能提供帮助。


我创建了一个名为$themes 的 SASS 地图,其中包含不同主题的颜色。我从一篇中等文章(谁没有)中复制粘贴了一些代码,然后我的主题作品就大功告成了! 但是... 这个:

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

打开。每一个。零件。比我认为在我将要做的大量样式中可维护的代码更草率。所以...

我的目标


我想做一些超级 hacky 和很棒的事情:

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

我想对变量进行主题化,所以我所要做的就是添加 $blue 而不是很多调用 mixins 和不必要的胡言乱语。 如果我能得到这样的东西,它会看起来像这样:

.btn 
    background: $blue;

所有主题都将事先处理好! 但当然它从来没有那么容易,因为它不起作用......如果你们中的一个很棒的sass魔术师可以用这个来发挥一些魔法,那将是天赐之物,我会将你包含在很棒的贡献者的源代码中。

代码


$themes sass-map:

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

来自这个很棒的Medium Article的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);


对于如何实现这一点的任何建议都将 100% 感激。非常感谢您将您作为出色的贡献者添加到源代码中。 提前致谢!

【问题讨论】:

【参考方案1】:

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; 
    

这种方法的问题(除了维护繁琐之外)是它会用与主题无关的东西使你的 CSS 膨胀——在上面的例子中,边框将被重复。

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


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

虽然我认为可以创建一个设置,将每个主题的范围限定为它自己的单独样式表(例如 light.css 和 dark.css),但我认为您应该考虑使用 CSS variables 来处理这个

$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 输出

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


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


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

注意!您只需要将主题类添加到例如body 标签和嵌套元素将继承这些值:)

【讨论】:

动态创建变量是什么意思? 假设您有一个映射 $map:( foo: 1, bar: 2 ),您将无法遍历键并创建 $foo: 1;$bar: 2; - 您必须像 $foo: map-get($map, foo); 一样手动创建它们【参考方案2】:

我会使用这种方法(它看起来有点简单):

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

【讨论】:

以上是关于带有动态变量的 SCSS 主题的主要内容,如果未能解决你的问题,请参考以下文章

Vue项目动态主题切换

scss 来自TaroUI的常见主题变量

如何在其他 scss 文件中使用角度材质主题颜色变量

_Class.scss:Sencha Touch 主题中未定义的变量“$font-family”

Scss换肤

Angular:如何动态更改 scss 变量?