Переменные CSS с резервным вариантом для старых браузеров

TL; DR: Как вы можете использовать SCSS, чтобы иметь переменные CSS с резервным вариантом для старых браузеров.

Я пытаюсь разобраться в этой статье. На мой взгляд, вы должны быть уже продвинутым пользователем SASS, чтобы понять это, а я нет. Что еще хуже, это единственная статья, которую я нашел на эту тему.

Вот чего я пытаюсь достичь:

Мой scss должен быть примерно таким:

body {
  @include v(background-color, primary)
}

тогда обработанный CSS должен быть

body{
   background: yellow; /* Yellow being defined above as the primary color */
   background: var(--color-primary);
}

Немного поигравшись, я уже могу получить значение переменной CSS следующим образом:

$colors: (
  primary: yellow,
);

:root {
  @each $name, $color in $colors {
    --color-#{$name}: $color;
  }
}

@mixin background-color($color_) {
  background: var(--color-#{$color_});
}

Чтобы использовать это:

body{
  @include background-color(primary);
}

Что приведет к следующему:

body {
    background: var(--color-primary);
    /* But the fallback is missing :(, I tried  things with the map-get but it's really eluding me... */
}

person Ced    schedule 30.05.2017    source источник
comment
Извините за вопрос, но какова вообще цель использования переменных, если за каждым var следует его буквальное значение для совместимости с браузером?   -  person caiosm1005    schedule 22.11.2018
comment
@ caiosm1005 Переменная будет использоваться в новых браузерах, поэтому значение будет таким же, как обычное значение, когда вы используете переменные без отката. Когда вы разрабатываете, переменная также будет использоваться, и только во время компиляции будет вставлено буквальное значение. Кстати, за ним не следует его буквальное значение, ему предшествует буквальное значение, что и является точкой (иначе это действительно было бы бесполезно).   -  person Ced    schedule 22.11.2018
comment
Ага, имел в виду предыдущий. Виноват. Тем не менее, если есть буквальное значение, в этом случае нет необходимости в переменных. Я понимаю, что он автоматически генерируется препроцессором, но результат будет идентичным без операторов var, и таблица стилей станет чище.   -  person caiosm1005    schedule 22.11.2018
comment
@ caiosm1005, зачем тогда вообще использовать переменные? Разница здесь в том, что вы можете изменять стиль приложения в целом во время выполнения, и вы можете использовать css-переменные в своем коде. Для некоторых это полезные функции.   -  person Ced    schedule 23.11.2018


Ответы (3)


Если вы используете Sass, вы можете автоматизировать откаты с помощью миксина Sass. Создайте карту имен ваших переменных CSS и их значений, а затем вы можете найти эти значения в миксине, который выводит резервный стиль и предпочтительный.

$vars: (
  primary: yellow,
);

:root {
  --primary: map-get($vars, primary);
}

@mixin var($property, $varName) {
  #{$property}: map-get($vars, $varName);
  #{$property}: var(--#{$varName});
}

Приведенный выше миксин используется так:

body {
  @include var(background-color, primary);
}

и выводит следующий CSS:

:root {
  --primary: yellow;
}

body {
  background-color: yellow;
  background-color: var(--primary);
}

И вуаля :)

person rdhainaut    schedule 10.02.2018
comment
хорошо, кстати, некоторые плагины postCSS делают это автоматически (это называется настраиваемыми свойствами, если я правильно помню). В итоге я использовал это, так как это было удобнее - person Ced; 11.02.2018
comment
Au fait: Bonjour depuis la belgique :) - person Ced; 11.02.2018
comment
К вашему сведению, вам нужно интерполировать map-get, поэтому --primary: #{map-get($vars, primary)}; - person Jan; 29.08.2018
comment
это на самом деле намного лучше, чем мой ответ, я принимаю его через год: D - person Ced; 06.09.2018

Обновление: Пользовательские свойства Postcss могут выполнять откат, и это намного проще чем приведенный ниже код

шаг 1: объявите переменные scss

Итак, прежде всего мы хотим поместить некоторые переменные в $map, я выберу цветовые переменные:

$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

Шаг 2. Автоматизируйте создание переменных CSS 4

// ripped CSS4 vars out of color map
:root {
  // each item in color map
  @each $key, $value in $colors {
    --colors-#{$key}: $value;
  }
}

В корне происходит следующее: для каждого ключа и значения в цветах map мы печатаем следующее:

--colors-#{$key}: $value;

Что соответствует объявлениям переменных css. Я считаю, что странный бит с #{} вокруг ключа заключается в том, что вокруг значения нет пробелов. Таким образом, результат:

--colors-primary: #FFBB00,
--colors-secondary: #0969A2

Обратите внимание, что префикс (--colors-) - это то же имя, что и цветовая карта scss над ним. Почему станет ясно на последнем этапе.


шаг 3. Множество карт!

$props: (
  background-color: $colors
);

$map-maps: (
  background-color: colors
);

Здесь мы добавляем карту $props, которая отображает свойство css на карту, содержащую значения. background-color будет содержать цвет, поэтому правильная карта - $colors.

map-maps - это копия реквизита, где вместо карты у нас есть название указанной карты. (это относительно примечания в шаге 2).

Шаг 4: давайте заставим это работать!

@mixin v($prop, $var) {
  // get the map from map name
  $map: map-get($props, $prop);
  // fallback value, grab the variable's value from the map
  $var-fall: map-get($map, $var);
  // our css4 variable output
  $var-output: var(--#{$map}-#{$var});    
  #{$prop}: $var-fall;
  // css4 variable output
  #{$prop}: $var-output;
}

body{
  @include v(background-color, primary);
}

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

Во всяком случае, вот что происходит.

Сначала мы вызываем миксин с помощью:

  @include v(background-color, primary);

Затем при входе

 $map: map-get($props, $prop); // map-get($props, background-color)

у нас есть переменная с именем $map, которой мы присваиваем значение внутри карты $props ключу background-color, который оказывается картой $colors. Это немного похоже на лабиринт, но не так уж и сложно, как только вы его решите.

Затем для запасного варианта:

 $var-fall: map-get($map, $var);

Это просто получает значение только что полученной карты ($colors) по ключу $var (который оказывается первичным). Таким образом, результат #FFBB00.

Для css var

  $map-name: map-get($map-maps, $prop);
  $var-output: var(--#{$map-name}-#{$var});

мы воссоздаем то, что мы сделали, чтобы сгенерировать переменную в цикле @each


Весь код будет:

$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

// ripped CSS4 vars out of color map
:root {
  // each item in color map
  @each $name, $color in $colors {
    --colors-#{$name}: $color;
  }
}



$props: (
  background-color: $colors,
  color:            $colors
);

$map-maps: (
  background-color: colors
);



@mixin v($prop, $var) {
  // get the map from map name
  $map: map-get($props, $prop);
  // fallback value, grab the variable's value from the map
  $var-fall: map-get($map, $var);
  // our css4 variable output

  $map-name: map-get($map-maps, $prop);
  $var-output: var(--#{$map-name}-#{$var});

  #{$prop}: $var-fall;
  // css4 variable output
  #{$prop}: $var-output;
}

body{
  @include v(background-color, primary);
}

Теперь это упрощение того, что сделано в статье. Вы должны проверить это, чтобы получить более надежный код.

person Ced    schedule 30.05.2017
comment
Этот ответ был бы намного лучше с демонстрацией - person Zach Saucier; 31.05.2017
comment
Этот ответ также излишне сложен как по коду, так и по объему объяснения. - person Zach Saucier; 31.05.2017
comment
Просто вау, не используйте это в продакшене. Умно, но продуманно и ненужно. Всегда предпочитайте удобочитаемость магии. Вы меня поблагодарите через несколько лет. - person Kugel; 16.02.2018

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

Текущий блок миксина имеет только одно свойство фона, что заставляет компилятор sass генерировать только одно свойство. Я не думаю, что sass может определить, поддерживается ли var в браузере или нет. Итак, мы должны явно указать, нужен ли нам запасной вариант.

Поскольку у вас уже есть карта, все, что вам нужно, это получить значение, указав ключ 'primary'

 @mixin background-color($color_) {
      background: var(--color-#{$color_});  
      background: map-get($colors, primary);
    }

Это всегда добавит background: yellow к классу тела. В качестве альтернативы, если вы хотите контролировать добавление резервной копии на основе условия. Вы можете сделать это

@mixin background-color($color_, $showFall) {
  background: var(--color-#{$color_});  
  @if $showFall {
    background: map-get($colors, primary);
  }
}

и звони так

body{
  @include background-color(primary, true);
}

Кодовое перо для того же https://codepen.io/srajagop/pen/xdovON

Примечание: я пишу ответ, исходя из предположения, что вы хотите, чтобы работал только цвет фона, а не все другие свойства, упомянутые в этом сообщении. Для этого вам необходимо создать правильную структуру данных.

person karthick    schedule 30.05.2017
comment
Спасибо. На самом деле я понял, почему этого не произошло, однако ваш ответ позволил мне понять всю статью. Поскольку мой вопрос был о @include v(background-color, primary), и я чувствую, что он может принести пользу будущим читателям, я отвечу на него, объяснив, как он это делает. Однако, если вы тоже хотите это сделать, я приму ваш ответ, если нет, я приму свой. Просто голова вверх. - person Ced; 31.05.2017
comment
мое время на сегодня истекло. Пожалуйста, ответьте на вопрос - person karthick; 31.05.2017
comment
Хорошее объяснение. Мой плюс один - person karthick; 31.05.2017
comment
Обязательно поместите резервную копию перед переменной, иначе всегда будет использоваться резервная копия! - person Zach Saucier; 31.05.2017