LESS/SASS中的主题变量

5
我希望在我的应用程序中支持多个主题 - 此外,我希望能够动态更改主题,无论是通过更改元素上的类还是使应用程序的不同部分使用不同的主题。
在我的上一个项目中,每次需要使用特定于主题的变量时,我都会添加明确的规则:
.theme-light & { background-color: @theme-light-background; }
.theme-dark & { background-color: @theme-dark-background; }

然而,这种方法不具有良好的可扩展性,并且会使源文件变得臃肿。

现在,我正在寻找更自动化的方法。例如以下:

.button {
  border-radius: 4px;
  background-color: @ui-background;
  color: @ui-foreground;
  border: 1px solid mix(@ui-background, @ui-foreground, 50%);
}

将变成类似于以下的内容

.button {
  border-radius: 4px;
  border: 1px solid #808080;
    /* normally we wouldn't expect this to appear here, but in our case
    both themes have the same border color so we can't tell the difference */
}
.theme-light .button {
  background-color: #fff;
  color: #000;
}
.theme-dark .button {
  background-color: #000;
  color: #fff;
}

据我所知,LESS和SASS都无法以自然的方式实现这一点。似乎将其作为单独的后处理器实现并为每个主题构建样式表,然后比较它们并将差异定位到相应的“命名空间”中不会太困难。我怀疑这样的东西可能已经存在,但我找不到任何信息。
有什么建议吗?

请查看 https://stackoverflow.com/questions/25875846、https://stackoverflow.com/questions/23551080、https://stackoverflow.com/questions/32676764 等相关内容。请不要在同一个问题中同时添加 Less 和 Sass 标签,这没有任何意义。 - seven-phases-max
1
感谢提供链接;为什么不把两种处理器都加上呢?如果我还没决定用哪个处理器,如果其中一个有方便的实现这个功能的方式,它可能会影响我的决定。 - riv
因为每个问题只能有一个被接受的答案。同样的道理,您不会将Fortran、C++、JavaScript、PHP或其他任何标签(请注意,它们也都可以生成CSS)放入任意的问题中。 - seven-phases-max
无论如何,作为用户,我对您上面的方法有完全相反的看法。想象一下,如果您有18个主题,现在(再次作为用户)假设我将一次只使用唯一的主题来制作页面,那么当渲染时,我不会很高兴我的浏览器下载一个包含所有18个主题的巨大CSS,然后遍历它们。 18个主题-> 18个不同的CSS-> <link switch>将是大多数用例的最佳/轻量级解决方案。 - seven-phases-max
1个回答

7

我不确定Less是否可以实现,但在Sass中,可以通过将主题信息存储到映射中,并使用传递内容块到Mixin的能力,使用@content。这是一个示例,看起来相当快速的解决方案,但你可以得到一个想法:

// Themes definition
//  - First level keys are theme names (also used to construct theme class names)
//  - Second level keys are theme settings, can be referred as theme(key)
$themes: (
    light: (
        background: #fff,
        foreground: #000,
    ),
    dark: (
        background: #000,
        foreground: #fff,
    ),
);

// Internal variable, just ignore 
$_current-theme: null;

// Function to refer to theme setting by name
// 
// @param string $name  Name of the theme setting to use
// @return mixed
@function theme($name) {
    @if ($_current-theme == null) {
        @error "theme() function should only be used into code that is wrapped by 'theme' mixin";
    }
    @if (not map-has-key(map-get($themes, $_current-theme), $name)) {
        @warn "Unknown theme key '#{$name}' for theme '#{$_current-theme}'";
        @return null;
    }
    @return map-get(map-get($themes, $_current-theme), $name);
}

// Theming application mixin, themable piece of style should be wrapped by call to this mixin 
@mixin theme() {
    @each $theme in map-keys($themes) {
        $_current-theme: $theme !global;
        .theme-#{$theme} & {
            @content;
        }
    }
    $_current-theme: null !global;
}

.button {
    border-radius: 4px;
    @include theme() {
        background-color: theme(background);
        color: theme(foreground);
    }
}

这段代码将会给你以下输出结果:
.button {
  border-radius: 4px;
}
.theme-light .button {
  background-color: #fff;
  color: #000;
}
.theme-dark .button {
  background-color: #000;
  color: #fff;
}

看起来非常接近你想要实现的效果。你可以在Sassmeister上尝试这段代码。


啊,谢谢,我没想到可以在mixin内容中使用函数。虽然我可能仍然会尝试寻找后处理解决方案以获得更好的可读性。 - riv
太棒了,谢谢!@riv,有什么提高可读性的建议吗?我正在处理一个类似的任务。 - Ozan Mudul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接