首页 文章

Bootstrap SASS变量覆盖挑战

提问于
浏览
13

EDIT: 这个问题被标记为this one的副本,但请参阅本答案末尾附近的附录,以了解该问题的答案是什么't ask, and what the answer doesn' .

我正在使用一个使用Bootstrap 3的Web应用程序 . 我有一个基本的3层覆盖架构,其中1)Bootstrap的_variables.scss包含核心变量,2)_app-variables.scss包含覆盖Bootstrap的基本app变量_variables.scss和3)_client-variables.scss包含覆盖_app-variables.scss的特定于客户端的自定义 . #2或#3(或两者)都可以是空白文件 . 所以这是覆盖顺序:

_variables.scss // Bootstrap's core
_app-variables.scss // App base
_client-variables.scss // Client-specific

在理论上足够简单,但由于我称之为“变量依赖”而出现问题 - 变量被定义为其他变量 . 例如:

$brand: blue;
$text: $brand;

现在,让's say the above variables are defined in _variables.scss. Then let'在_app-variables.scss中说,我只覆盖$ brand变量使其变为红色: $brand: red . 由于SASS按顺序逐行解释代码,它首先将$ brand设置为蓝色,然后将$ text设置为蓝色(因为$ brand在此时为蓝色),最后将$ brand设置为红色 . 因此最终结果是,之后更改$品牌不会影响任何基于旧品牌 Value 的变量:

_variables.scss
---------------------
$brand: blue;
$text: $brand; // $text = blue
.
.
.

_app-variables.scss
---------------------
$brand: red; // this does not affect $text, b/c $text was already set to blue above.

但显然这不是我想要的 - 我希望我的$品牌改变能够影响所有依赖它的东西 . 为了正确覆盖变量,我现在只是将_variables.scss的完整副本复制到_app-variables.scss中,然后在_app-variables中进行修改 . 同样地,我将_app-variables.scss的完整副本复制到_client-variables.scss中,然后在_client-variables.scss中进行修改 . 从维护的角度来看,这显然不太理想(轻描淡写) - 每当我对_variables.scss(在Bootstrap升级的情况下)或_app-variables.scss进行修改时,我都必须手动涓流改变文件覆盖堆栈 . 此外,我不得不重新宣布一些我甚至可能无法克服的变量 .

我发现LESS有他们所谓的"lazy loading"(http://lesscss.org/features/#variables-feature-lazy-loading),其中变量的最后一个定义在任何地方使用,甚至在最后一个定义之前 . 我相信这会解决我的问题 . 但有没有人知道使用SASS的适当的变量覆盖解决方案?

ADDENDUM:
这里's one technique I'已经考虑过:以相反的顺序包含文件,使用 !default 表示所有变量(这个技术也在this question的答案中提出) . 所以这是如何发挥作用:

_app-variables.scss
---------------------
$brand: red !default; // $brand is set to red here, overriding _variables.scss's blue.
.
.
.


_variables.scss
---------------------
$brand: blue !default; // brand already set in _app-variables.scss, so not overridden here.
$text: $brand !default; // $text = red (desired behavior)

所以这个解决方案是完美的 . However ,现在在我的覆盖文件中,我没有't have access to variables defined in Bootstrap' s _variables.scss,如果我想使用其他Bootstrap变量来定义我的变量覆盖(或我自己的其他自定义变量),我将需要它 . 例如,我可能想要这样做: $custom-var: $grid-gutter-width / 2;

4 回答

  • 1

    解决了,但我不知道这个版本的工作原理 . 我相信解决方案始终可用 . 测试:

    > sassc --version
    
    sassc: 3.2.1
    libsass: 3.2.5
    sass2scss: 1.0.3
    

    我们将使用简化的环境,因此文件名与Bootstrap不匹配 .


    挑战

    给定一个我们无法控制的框架(例如,仅在Continuous Integration环境中安装,在我们的机器中不可用),它以下列方式表示SCSS变量:

    // bootstrap/_variables.scss
    
    $brand-primary: #f00 !default;
    $brand-warning: #f50 !default;
    
    $link-color: $brand-primary !default;
    

    并在同一框架中给出一个使用变量的文件:

    // boostrap/main.scss
    
    a:link, a:visited {
      color: $link-color;
    }
    

    挑战是:

    将框架包含在您自己的应用程序的SCSS中,使框架中的变量依赖性得到保留和尊重;您可以依赖于默认值,但仍然可以更改框架依赖项的结果 .

    更确切地说:

    在您的应用程序的SCSS中包含框架,使得$ brand-color始终是$ brand-warning的反转,无论其在框架中的 Value 如何 .

    解决方案

    主文件看起来像这样:

    // application.scss
    
    @import "variables";
    @import "bootstrap/variables";
    @import "bootstrap/main";
    

    你的变量文件看起来像这样:

    // _variables.scss
    
    %scope {
      @import "boostrap/variables";
    
      $brand-primary: invert($brand-warning) !global;
    }
    

    结果:

    > sassc main.scss
    
    a {
      color: blue; }
    

    解释

    %scope 部分不是SCSS的神奇之处,它只是一个名为 scope 的隐藏类,专门用于 @extend 的后续扩展 . 我们正在使用它来创建一个变量范围(因此名称) .

    在范围内我们 @import 框架的变量 . 因为此时每个变量都没有值,每个变量都被创建并分配给它 !default 值 .

    But here’s the gimmick. 变量 are not global, but local . 我们可以访问它们,但它们不会污染全局范围,后者将用于在框架内派生变量 .

    实际上,当我们想要定义变量时,我们希望它们是全局变量,实际上我们使用 !global 关键字来指示SCSS将它们存储在全局范围内 .


    警告

    有一个主要的警告: you cannot use your own variables while you define them .

    这意味着在这个文件中

    %scope {
      @import "boostrap/variables";
    
      $brand-primary: black !global;
    
      @debug $brand-primary;
    }
    

    @debug 语句将打印在boostrap / _variables.scss中定义的 the default value ,而不是 black .

    解决方案

    拆分变量分为两部分:

    %scope {
      @import "boostrap/variables";
    
      $brand-primary: black !global;
    
      @debug $brand-primary;
    }
    
    @debug $brand-primary;
    

    第二个 @debug 确实会正确打印 black .

  • 4

    使用Bootstrap 4或bootstrap-sass,使用!default标志在_variables.scss中设置所有变量 .

    因此,如果在包含bootstrap的_variables.scss之前设置变量,则在包含该变量时,将忽略_variables.scss中的值 .

    所以我的sass条目文件可能看起来像这样......

    @import "bootstrap-overrides"; @import "bootstrap/scss/bootstrap-flex"; @import "mixins/module";

  • 16

    在Bootstrap 4的alpha 6中,_variables.scss中的所有变量都可以在_custom.scss中被覆盖,就像mryarbles描述的那样 . 但是,覆盖不会级联到其他元素,因为包含顺序是:

    @import "variables";
    @import "mixins";
    @import "custom";
    

    当我改变这个

    @import "custom";
    @import "variables";
    @import "mixins";
    

    它按预期工作 .

  • 1

    BS4 dev分支中的 _custom.scss 文件已被删除 . 尝试使用此订单重新排序您的导入:

    Setup

    @import "client-variables";
    @import "app-variables";
    @import "boostrap";
    @import "app-mixins";
    @import "client-mixins";
    

    确保将boostrap变量文件 _variables.scss 的内容复制到 app-variablesclient-variables . 在每个变量旁边留下!default以允许进一步覆盖 .

    Explanation

    所有引导变量都使用 !default 声明 . 来自Sass reference

    如果尚未通过将!default标志添加到值的末尾来分配变量,则可以将其分配给变量 . 这意味着如果已经为变量赋值,则不会重新赋值,但如果它还没有值,则会给出一个 .

    • Bootstrap将尊重已在顶部定义的所有变量,使 app_variables 具有更高优先级, client_variables 具有最高优先级 .

    • 您需要将bootstrap _variables 中的所有变量声明复制到 app-variablesclient-variables 中,以便您可以根据需要使用自定义变量 . (缺点是每次引导更新都很难维护)

    • 所有变量现在都在 app-mixinsclient-mixins 中可用

相关问题