Задача переопределения переменной SASS при загрузке

ОБНОВЛЕНИЕ: Этот вопрос был помечен как дубликат этого, но посмотрите приложение в конце этого ответа, чтобы узнать, что этот вопрос не задает, а какой ответ не отвечает.

Я работаю над веб-приложением, которое использует Bootstrap 3. У меня есть базовая 3-уровневая архитектура переопределения, где 1) Bootstrap _variables.scss содержит основные переменные, 2) _app-variables.scss содержит базовые переменные приложения, которые переопределяют Bootstrap _variables.scss и 3) _client-variables.scss содержит специфичные для клиента настройки, которые переопределяют _app-variables.scss. Либо # 2 или # 3 (или оба) могут быть пустыми файлами. Итак, здесь порядок переопределения:

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

Теоретически достаточно просто, но проблема возникает из-за того, что я назову "переменными зависимостями" - где переменные определяются как другие переменные. Например:

$brand: blue;
$text: $brand;

Теперь допустим, что указанные выше переменные определены в _variables.scss. Затем, скажем, в _app-variables.scss, я переопределяю только переменную $ brand, чтобы сделать ее красной: $brand: red. Так как SASS интерпретирует код построчно, сначала он установит $ brand в синий, затем он установит $ text в синий (потому что $ brand в этот момент синий), и, наконец, он установит $ brand в красный. Таким образом, конечный результат заключается в том, что изменение $ brand впоследствии не влияет на переменные, которые основывались на старой стоимости $ brand:

_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-переменных. И точно так же я делаю полную копию _app-variables.scss в _client-variables.scss, а затем делаю изменения в _client-variables.scss на этом этапе. Очевидно, что это не идеально (занижение) с точки зрения обслуживания - каждый раз, когда я делаю изменения в _variables.scss (в случае обновления Bootstrap) или _app-variables.scss, мне приходится вручную вносить изменения вниз стек переопределения файлов. Кроме того, мне нужно переопределить тонну переменных, которые я даже не могу переопределить.

Я обнаружил, что LESS имеет то, что они называют "отложенной загрузкой" (http://lesscss.org/features/#variables-feature-lazy-loading), где последнее определение переменной используется везде, даже перед последним определением. Я верю, что это решит мою проблему. Но кто-нибудь знает правильное решение для переопределения переменных с использованием SASS?

ДОПОЛНЕНИЕ:
Вот одна техника, которую я уже обдумал: включите файлы в обратном порядке, используя !default для всех переменных (эта техника также была предложена в ответе на этот вопрос). Итак, вот как это закончится:

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


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

Так что это решение почти идеально. Однако, теперь в моих файлах переопределения у меня нет доступа к переменным, определенным в Bootstrap _variables.scss, которые мне понадобились бы, если бы я хотел определить свои переопределения переменных (или мои собственные дополнительные пользовательские переменные), используя другие переменные Bootstrap. Например, я могу захотеть сделать: $custom-var: $grid-gutter-width / 2;

Ответ 1

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

> sassc --version

sassc: 3.2.1
libsass: 3.2.5
sass2scss: 1.0.3

Мы собираемся использовать упрощенную среду, поэтому имена файлов не совпадают с Bootstraps.


вызов

Учитывая структуру, которую мы не контролируем (например, устанавливаем только в среде Continuous Integration и недоступной на наших машинах), которая выражает переменные SCSS следующим образом:

// bootstrap/_variables.scss

$brand-primary: #f00 !default;
$brand-warning: #f50 !default;

$link-color: $brand-primary !default;

И предоставленный файл в той же самой структуре, который использует переменные:

// bootstrap/main.scss

a:link, a:visited {
  color: $link-color;
}

Задача:

Включите фреймворк в свои собственные приложения SCSS таким образом, чтобы

  1. зависимости переменных в рамках сохраняются и уважаются;
  2. вы можете зависеть от значений по умолчанию, но все же сможете изменять результаты в зависимости от структуры.

Точнее:

Включите структуру в свои приложения SCSS таким образом, чтобы $brand-color всегда был обратным к $brand-warning, независимо от его значения в структуре.

Решение

Основной файл будет выглядеть так:

// application.scss

@import "variables";
@import "bootstrap/variables";
@import "bootstrap/main";

И ваш файл переменных будет выглядеть так:

// _variables.scss

%scope {
  @import "bootstrap/variables";

  $brand-primary: invert($brand-warning) !global;
}

Результаты:

> sassc main.scss

a {
  color: blue; }

Объяснение

Часть %scope не является чем-то магическим в SCSS, это просто скрытый класс с именем scope, доступный исключительно для более поздних расширений с @extend. Мы используем его только для создания области видимости переменной (отсюда и название).

Внутри области видимости мы @import переменные каркаса. Поскольку в этот момент для каждой переменной нет значения, каждой переменной создается и присваивается ее значение !default.

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

Фактически, когда мы хотим определить наши переменные, мы хотим, чтобы они были глобальными, и мы действительно используем ключевое слово !global, чтобы сигнализировать SCSS о сохранении их в глобальной области видимости.


Предостережения

Есть одно важное предостережение: вы не можете использовать свои собственные переменные, пока вы их определяете.

Это означает, что в этом файле

%scope {
  @import "bootstrap/variables";

  $brand-primary: black !global;

  @debug $brand-primary;
}

Оператор @debug будет печатать значение по умолчанию, определенное в bootstrap/_variables.scss, а не black.

Решение

Разделить переменные на две части:

%scope {
  @import "bootstrap/variables";

  $brand-primary: black !global;

  @debug $brand-primary;
}

@debug $brand-primary;

Второй @debug действительно правильно напечатает black.

Ответ 2

С Bootstrap 4 или bootstrap-sass все переменные, установленные в _variables.scss с флагом по умолчанию.

Поэтому, если вы установите переменную до того, как загрузочный файл _variables.scss включен, когда он будет включен, значение из _variables.scss будет проигнорировано.

Итак, мой файл записи sass может выглядеть так:

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

Ответ 3

В альфа-6 Bootstrap 4 все переменные в _variables.scss могут быть переопределены в _custom.scss, как описано в mryarbles. Однако переопределения не каскадируются на другие элементы, потому что порядок включения:

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

Когда я изменяю это на

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

работает как ожидалось.

Ответ 4

Файл _custom.scss в разделе BS4 dev удален. Попробуйте изменить порядок импорта в этом порядке:

Настройка

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

Обязательно скопируйте содержимое файла переменной boostrap _variables.scss в app-variables и client-variables. Оставьте значение по умолчанию рядом с каждой переменной, чтобы разрешить дальнейшее переопределение.

Объяснение

Все начальные переменные объявляются с помощью !default. От Sass reference:

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

  • Bootstrap будет уважать все переменные, уже определенные сверху, делая app_variables с более высоким приоритетом и client_variables с наивысшим приоритетом.
  • Вам нужно скопировать все объявления переменных из bootstrap _variables в app-variables и client-variables, чтобы вы могли иметь пользовательскую переменную по своему усмотрению. (Недостатком является то, что его сложнее поддерживать при каждом обновлении начальной загрузки)
  • Все переменные теперь доступны в ваших app-mixins и client-mixins