SASS Несколько уровней приоритета/наследования переменных

Я использую Laravel Mix и Webpack для предварительной обработки SASS.

У меня есть две «темы» на моем веб-сайте, которые я хочу сделать компактными, наследуя переменные там, где они нужны. Например, моя основная тема будет включать в себя в следующем порядке:

// Primary theme
@import "./primary-variables.scss";
@import "/path/to/default/theme/main.scss";

Моя тема по умолчанию будет выглядеть так:

// Default theme
@import "./default-variables.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap";

Как и в этом вопросе, я сначала включил основные переменные, а затем переменные темы по умолчанию, затем бутстрап в последнюю очередь.

В моей теме по умолчанию я добавляю !default ко всем переменным, поэтому там, где они переопределяют Bootstrap, они будут использоваться в приоритете, а там, где они новые, они будут значением по умолчанию. Основная тема вообще не использует !default.

Рабочий пример

Если Bootstrap определяет $brand-danger как, скажем, red !default, моя тема по умолчанию переопределяет его как blue !default, а моя основная тема переопределяет его как yellow, мой отображаемый вывод будет желтым — отлично!

Эта проблема

Когда мне нужно ссылаться на переменные, которые определены только на других уровнях моей основной темы. Например:

// Primary theme:
// This fails since I haven't defined $brand-primary in my primary theme
$my-primary-theme-variable: $brand-primary;

Теперь сборка завершается с ошибкой primary-theme/src/scss/main.scss doesn't export content.

Обходной путь

Я могу обойти эту проблему, скопировав весь файл переменных Bootstrap в мою основную тему и изменив переменные по мере необходимости, но я действительно не хочу этого делать.

Вопрос

Как на самом деле работает процессор переменных SASS? Могу ли я просто изменить одну из переменных Bootstrap в моей теме без необходимости переопределять весь файл?


Этот вопрос очень похож.


person scrowler    schedule 21.02.2017    source источник


Ответы (2)


Похоже, вы используете @include для импорта SCSS, попробуйте вместо этого использовать @import. Если это просто опечатка в вопросе, сообщите мне об этом :-)

@import "./primary-variables.scss",
        "/path/to/default/theme/main.scss"
;

Я добавил несколько кратких заметок по вопросу, который вы имели в виду. . Важно знать о флаге !default: он вступает в силу в тот момент, когда он используется в селекторе, и не переопределяет переменные.

Sass не смотрит вперед при обработке переменных — он выводит текущее значение. В этом примере .class-1 будет красным, так как переопределение происходит после его использования в селекторе, а .class-2 будет синим, поскольку флага по умолчанию нет.

$brand-color: red !default; // defined
.class-1 { background-color: $brand-color; }  // red

$brand-color: blue;         // re-defined
.class-2 { background-color: $brand-color; }  // blue

Флаги по умолчанию заставят Sass пропускать переопределение переменных. В этом примере результат будет красным, поскольку он определен первым. Два следующих переопределения игнорируются из-за флагов по умолчанию.

$brand-color: red !default;   // defined
$brand-color: blue !default;  // ignored 
$brand-color: green !default; // ignored
.class-1 { background-color: $brand-color; }  // red

В этом случае будут использоваться все переменные из конфига — тогда переменные из partial-1, если они не определены в конфиге, и last partial-2 будут определять любую переменную, не определенную в двух других.

@import '_config.scss';    // definition
@import '_partial-1.scss'; // contains defaults
@import '_partial-2.scss'; // contains defaults

Надеюсь, это имеет смысл :-)


Структура импорта

//  _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss';

//  _primary-theme.scss
//  primary variables will override defaults or use defaults if not defined
@import '_primary-variables.scss', '_default-theme.scss';

// style.scss
@import '_primary-theme.scss'; // or '_default-theme.scss'

Объем

В случае, если ваш контент по умолчанию и первичный контент уникален для каждой темы, вы можете создать примесь области действия для обработки того, что скомпилировано.

Вот очень примитивная версия:

//  _scope.scss
$scope-context: null !default;
@function scope($scopes: null, $true: true, $false: false) { 
  @each $scope in $scope-context {  
    @if index($scopes, $scope) { @return $true }  
  }  
  @return $false;  
} 
@mixin scope($scopes: null) { 
  @if scope($scopes) or length($scopes) == 0 and not $scope-context { 
    @content; 
  } 
}

Как это работает

Миксин области видимости принимает аргумент контекста и блок контента @content. Если переданный контекст соответствует глобальной переменной ($scope-context), блок содержимого будет обработан.

//  _default-theme.scss
.class { content: 'Will show in both themes'; }

@include scope(default-theme){
  .class { content: 'Will only show in the default theme'; }
}

@include scope(primary-theme){
  .class { content: 'Will only show in the primary theme'; }
}

//  can also be used as "if" function
.class {
  content: scope(default-theme, 'Is default', 'Not default')
}

В вашем случае определите $scope-контекст как в переменных по умолчанию, так и в основных переменных.

// _default-variables.scss
$scope-context: default-theme !default;

// _primary-variables.scss
$scope-context: primary-theme;

... и добавьте _scope.scss в _default-theme.scss

//  _default-theme.scss
@import '_default-variables.scss', '_bootstrap.scss', '_scope.scss';   
person Jakob E    schedule 21.02.2017
comment
Извините, да, это просто опечатка :-) - person scrowler; 21.02.2017
comment
Спасибо - это имеет смысл и легко следовать. Мой вопрос больше об использовании переменных, которые не определены в то время, но позже. Например, в вашем примере я мог бы захотеть определить только $brand-color в _partial-2.scss, но я мог бы использовать его в _config.scss в другом определении, например, $my-var: $brand-color. Этот пример специфичен для начальной загрузки, где я мог бы захотеть повторно использовать переменную начальной загрузки в пользовательском определении без необходимости переопределять весь файл переменных начальной загрузки, чтобы переменные находились в одной области. Это возможно? Или я мечтаю? - person scrowler; 22.02.2017
comment
Два вопроса для уточнения: ваши темы скомпилированы в один или два файла CSS? В теме по умолчанию используются только переменные по умолчанию? - person Jakob E; 22.02.2017
comment
Да, один скомпилированный файл CSS. Его можно скомпилировать из любой из тем, в зависимости от того, какую вы используете (вторая тема наследует части первой, но первая является автономной). Тема по умолчанию имеет некоторые измененные переменные, как и вторая тема. - person scrowler; 22.02.2017
comment
Я обновил ответ (ниже горизонтальной линии) — надеюсь, мы приближаемся :-) - person Jakob E; 22.02.2017
comment
Спасибо за вашу помощь! Это редактирование на самом деле меньше похоже на то, что я хочу сделать, к сожалению, потому что, например, я хочу, чтобы фактические исходные файлы sass были простыми таблицами стилей. Вчера мне повезло поиграть с этим, отделив все переменные (в том числе из бутстрапа) от реальных таблиц стилей, сначала импортировав все переменные, а затем все стили. Это означает воссоздание основного файла sass начальной загрузки (со всем импортом), но он работает. - person scrowler; 22.02.2017

Проблема, которую я обнаружил, заключалась в том, что я неправильно предполагал, как работает SASS.

Когда вы определяете объявление переменной, его значение компилируется во время его записи. Например, $my-var: $brand-primary назначит текущее значение $brand-primary для $my-var во время его обработки.

Это просто означает, что я не могу добиться того, чего хотел, а именно включить файл с минимальными переменными поверх Bootstrap, потому что он будет обновлять только саму переменную, но не какие-либо другие переменные, которые ссылаются на эту переменную в Bootstrap.

Решение

Это не элегантно, но продублируйте весь файл переменных для каждой темы и настройте их по мере необходимости в каждом месте.

person scrowler    schedule 22.02.2017
comment
Вы также должны проверить ответ с наивысшим рейтингом на вопрос 31329177 (включая мой комментарий). Решение там лучше IMO (но все же не идеально). Я думаю, что это недостаток SASS по сравнению с LESS, который будет вести себя так, как хотелось бы в вашем вопросе. Хотя это обычное поведение для всех языков программирования, я знаю, что это кажется мне неправильным в контексте CSS. - person bayerphi; 17.08.2017