Использование переменных Sass с медиа-запросами CSS3

121

Я пытаюсь совместить использование переменной Sass с запросами @media следующим образом:

$base_width:1160px;

@media screen and (max-width: 1170px) {$base_width: 960px;}
@media screen and (min-width: 1171px) {$base_width: 1160px;}

$base_width затем определяется в различных точках измерения ширины таблицы стилей на основе процентов для создания гибких макетов.

Когда я это делаю, кажется, что переменная распознается правильно, а условия для медиа-запроса - нет. Например, приведенный выше код создает макет размером 1160 пикселей независимо от ширины экрана. Если я переверну утверждения @media следующим образом:

@media screen and (min-width: 1171px) {$base_width: 1160px;}
@media screen and (max-width: 1170px) {$base_width: 960px;}

Он создает макет 960 пикселей, опять же, независимо от ширины экрана. Также обратите внимание, что если я удалю первую строку, $base_width: 1160px;она вернет ошибку для неопределенной переменной. Есть идеи, что мне не хватает?

Джереми С.
источник
Дублированный stackoverflow.com/questions/40739695/…
Серкан КОНАКЧИ

Ответы:

97

Это просто невозможно. Поскольку триггер@media screen and (max-width: 1170px) происходит на стороне клиента.

Достижение ожидаемого результата возможно только в том случае, если SASS захватит все правила и свойства из вашей таблицы стилей, содержащей ваш $base_width переменную, и скопирует / изменит их соответственно.

Поскольку это не будет работать автоматически, вы можете сделать это вручную следующим образом:

@media screen and (max-width: 1170px)
      $base_width: 960px // you need to indent it to (re)set it just within this media-query
      // now you copy all the css rules/properties that contain or are relative to $base_width e.g.
      #wrapper
          width: $base_width
          ...

@media screen and (min-width: 1171px)
    $base_width: 1160px
      #wrapper
          width: $base_width
          ...

Это не совсем СУХОЕ, но лучшее, что вы можете сделать.

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

@media screen and (min-width: 1171px)
    +base_width_changes(1160px)
    #width-1171-specific-element // additional specific changes, that aren't in the mixin
        display: block

И миксин будет выглядеть так

=base_width_changes($base_width)
    #wrapper
        width: $base_width
woerndl
источник
6
По-прежнему ли SASS не работает с переменными $ внутри запросов @media?
HappyTorso
1
@HappyTorso так будет всегда, пока не появится такая вещь, как компилятор Sass на стороне клиента (например, в JavaScript), который запускается при изменении размера области просмотра. Переменные Sass компилируются до статических значений во время сборки; они больше не существуют во время выполнения. Медиа-запросы оцениваются во время выполнения, когда медиа-атрибуты в запросе меняются.
ericsoco
26
@ericsoco Я не понимаю, почему это причина. SASS может легко (?) Просто отслеживать все случаи использования переменной и вставлять медиа-запросы там, где они используются; $foo: 1rem; @media (min-width: 10rem) {$foo: 2rem} .class {font-size: $foo} .class2 {padding: $foo}приведет к.class {font-size: 1rem} .class2 {padding: 1rem} @media (min-width: 10rem) (.class {font-size: 2rem} .class2 {padding: 2rem}}
powerbuoy
1
да, sass - это препроцессор, нет причин для выражения «Это просто невозможно» ... Он мог бы это сделать, это просто не реализовано.
Ini
54

Подобно ответу Филиппа Зедлера, вы можете сделать это с помощью миксина. Это позволяет вам хранить все в одном файле, если хотите.

    @mixin styling($base-width) {
        // your SCSS here, e.g.
        #Contents {
            width: $base-width;
        }
    }

    @media screen and (max-width: 1170px) {
        @include styling($base-width: 960px);
    }
    @media screen and (min-width: 1171px) {
        @include styling($base-width: 1160px);
    }
Ронен
источник
1
рано или поздно вы столкнетесь с этой проблемой, хотя: sitepoint.com/cross-media-query-extend-sass
InsOp
Этот лучше, чем мой ответ!
Филипп
+1 люблю это решение. Объедините это с картой ( sass-lang.com/documentation/functions/map ), и вы получите некоторую мощность - я сделаю здесь отдельное решение, чтобы объяснить. Изменить: готово. stackoverflow.com/a/56894640/385273
Бен
9

Изменить: пожалуйста, не используйте это решение. Ответ Ронена намного лучше.

В качестве СУХОГО решения вы можете использовать @importоператор внутри медиа-запроса, например, вот так.

@media screen and (max-width: 1170px) {
    $base_width: 960px;
    @import "responsive_elements";
}
@media screen and (min-width: 1171px) {
    $base_width: 1160px;
    @import "responsive_elements";
}

Вы определяете все отзывчивые элементы в файле, включенном с помощью переменных, определенных в медиа-запросе. Итак, все, что вам нужно повторить, - это оператор импорта.

Филипп Зедлер
источник
9

Это невозможно с SASS, но возможно с переменными CSS (или настраиваемыми свойствами CSS ). Единственный недостаток - это поддержка браузера, но на самом деле есть плагин PostCSS - postcss-css-variables - который «сглаживает» использование переменных CSS (что дает вам поддержку и для старых браузеров).

Следующий пример отлично работает с SASS (а с переменными postcss-css вы также получаете поддержку старых браузеров).

$mq-laptop: 1440px;
$mq-desktop: 1680px;

:root {
    --font-size-regular: 14px;
    --gutter: 1rem;
}

// The fact that we have to use a `max-width` media query here, so as to not
// overlap with the next media query, is a quirk of postcss-css-variables
@media (min-width: $mq-laptop) and (max-width: $mq-desktop - 1px) {
    :root {
        --font-size-regular: 16px;
        --gutter: 1.5rem;
    }
}

@media (min-width: $mq-desktop) {
    :root {
        --font-size-regular: 18px;
        --gutter: 1.75rem;
    }
}

.my-element {
    font-size: var(--font-size-regular);
    padding: 0 calc(var(--gutter) / 2);
}

Это приведет к следующему CSS. Повторяющиеся медиа-запросы увеличивают размер файла, но я обнаружил, что это увеличение обычно незначительно после применения веб-сервера gzip(что обычно происходит автоматически).

.my-element {
  font-size: 14px;
  padding: 0 calc(1rem / 2);
}
@media (min-width: 1680px) {
  .my-element {
  padding: 0 calc(1.75rem / 2);
  }
}
@media (min-width: 1440px) and (max-width: 1679px) {
  .my-element {
  padding: 0 calc(1.5rem / 2);
  }
}
@media (min-width: 1680px) {
  .my-element {
  font-size: 18px;
  }
}
@media (min-width: 1440px) and (max-width: 1679px) {
  .my-element {
  font-size: 16px;
  }
}
fredrikekelund
источник
8

С отличным ответом @ ronen и картой есть реальная сила:

    @mixin styling($map) {
        .myDiv {
            background: map-get($map, 'foo');
            font-size: map-get($map, 'bar');
        }
    }

    @media (min-height: 500px) {
        @include styling((
            foo: green,
            bar: 50px
        ));
    }

    @media (min-height: 1000px) {
        @include styling((
            foo: red,
            bar: 100px
        ));
    }

Теперь возможно иметь гораздо больше DRY медиа-запросов, нацеленных .myDivна множество разных значений.


Документы карты: https://sass-lang.com/documentation/functions/map

Пример использования карты: https://www.sitepoint.com/using-sass-maps/

Бен
источник
Мне потребовалась секунда, чтобы увидеть это, но это тот же метод, что и ответ @ronen, просто используя ввод карты для одновременной поддержки нескольких переменных. Я ни в коем случае не сбрасываю со счетов его ценность, но это было труднее понять, пока я не установил эту связь
Джон Нойхаус
5

У меня такая же проблема.

$menu-widthПеременная должна быть 240px на мобильный зрения @media only screen and (max-width : 768px)и 340px на рабочем столе зрения.

Итак, я просто создал две переменные:

$menu-width: 340px;
$menu-mobile-width: 240px;

И вот как я это использовал:

.menu {
    width: $menu-width;
    @media only screen and (max-width : 768px) {
      width: $menu-mobile-width;
    }
}
Давид Дирч
источник
1

Две рекомендации

1 Напишите операторы CSS по умолчанию для маленьких экранов и используйте медиа-запросы только для больших экранов. Обычно в max-widthмедиа-запросе нет необходимости .

Пример (предполагается, что элемент имеет класс "контейнер")

@mixin min-width($width) {
  @media screen and (max-width: $width) {
    @content;
  }
}

.container {
  width: 960px;

  @include min-width(1170px) {
    width: 1160px;
  }
}

2 С помощью переменных CSS , чтобы решить эту проблему, если вы можете .

@mixin min-width($width) {
  @media screen and (max-width: $width) {
    @content;
  }
}

:root {
  --container-width: 960px;
  @include min-width(1170px) {
    --container-width: 1160px;
  }
}

.container {
  width: var(--container-width);
}

Примечание:

Поскольку его ширина будет 1160 пикселей, когда ширина окна 1170 пикселей, может быть лучше использовать ширину 100% и максимальную ширину 1160 пикселей, а родительский элемент может иметь горизонтальное заполнение 5 пикселей, если для свойства box-sizing установлено значение border-box. Есть много способов решить проблему. Если родительский элемент не является гибким или сеточным контейнером, вы можете использовать его .container { margin: auto }.

Стивен
источник