Używanie zmiennych Sass z zapytaniami o media CSS3

121

Próbuję połączyć użycie zmiennej Sass z zapytaniami @media w następujący sposób:

$base_width:1160px;

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

$base_width jest następnie definiowana w różnych punktach w pomiarach procentowych szerokości arkusza stylów w celu uzyskania płynnych układów.

Kiedy to robię, zmienna wydaje się być poprawnie rozpoznawana, ale warunki zapytania o media nie. Na przykład powyższy kod tworzy układ 1160px niezależnie od szerokości ekranu. Jeśli przerzucę oświadczenia @media w następujący sposób:

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

Tworzy układ 960px, niezależnie od szerokości ekranu. Zauważ również, że jeśli usunę pierwszą linię $base_width: 1160px;, zwróci błąd dla niezdefiniowanej zmiennej. Jakieś pomysły, czego mi brakuje?

Jeremy S.
źródło
Zduplikowany stackoverflow.com/questions/40739695/…
Serkan KONAKCI

Odpowiedzi:

97

To po prostu niemożliwe. Ponieważ wyzwalacz @media screen and (max-width: 1170px)występuje po stronie klienta.

Osiągnięcie oczekiwanego wyniku byłoby możliwe tylko wtedy, gdyby SASS przechwycił wszystkie reguły i właściwości w arkuszu stylów zawierającym $base_widthzmienną i odpowiednio je skopiował / zmienił.

Ponieważ nie zadziała automatycznie, możesz to zrobić ręcznie w następujący sposób:

@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
          ...

To nie jest naprawdę SUCHE, ale najlepsze, co możesz zrobić.

Jeśli zmiany są takie same za każdym razem, możesz również przygotować mixin zawierający wszystkie zmieniające się wartości, więc nie musisz tego powtarzać. Dodatkowo możesz spróbować połączyć mixin z określonymi zmianami. Lubić:

@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

A Mixin wyglądałby tak

=base_width_changes($base_width)
    #wrapper
        width: $base_width
woerndl
źródło
6
Czy nadal jest tak, że SASS nie będzie działał ze zmiennymi $ w zapytaniach @media?
HappyTorso,
1
@HappyTorso zawsze tak będzie, dopóki nie pojawi się coś takiego jak kompilator Sass po stronie klienta (np. W JavaScript), który działa przy zmianie rozmiaru widoku. Zmienne Sass są kompilowane do wartości statycznych w czasie kompilacji; nie istnieją już w czasie wykonywania. Zapytania o media są oceniane w czasie wykonywania, kiedy zmieniają się atrybuty mediów w zapytaniu.
ericsoco
26
@ericsoco Nie rozumiem, dlaczego to jest powód. SASS może łatwo (?) Po prostu śledzić wszystkie zastosowania zmiennej i wstawiać zapytania o media tam, gdzie są używane; $foo: 1rem; @media (min-width: 10rem) {$foo: 2rem} .class {font-size: $foo} .class2 {padding: $foo}spowodowałoby.class {font-size: 1rem} .class2 {padding: 1rem} @media (min-width: 10rem) (.class {font-size: 2rem} .class2 {padding: 2rem}}
powerbuoy
1
tak, sass jest preprocesorem, nie ma powodu, aby „To po prostu niemożliwe”… Mogłoby to zrobić, po prostu nie jest zaimplementowane.
Ini
54

Podobnie jak w przypadku odpowiedzi Philippa Zedlera, możesz to zrobić za pomocą miksera. Dzięki temu możesz mieć wszystko w jednym pliku, jeśli chcesz.

    @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);
    }
ronen
źródło
1
prędzej czy później napotkasz ten problem, chociaż: sitepoint.com/cross-media-query-extend-sass
InsOp
Ten lepszy niż moja odpowiedź!
Philipp Zedler
+1 uwielbiam to rozwiązanie. Połącz to z mapą ( sass-lang.com/documentation/functions/map ) i masz trochę mocy - zrobię tutaj osobne rozwiązanie, aby wyjaśnić. Edycja: gotowe. stackoverflow.com/a/56894640/385273
Ben,
9

Edycja: nie używaj tego rozwiązania. Odpowiedź Ronena jest znacznie lepsza.

Jako rozwiązanie DRY możesz użyć @importinstrukcji wewnątrz zapytania o media, np. W ten sposób.

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

Definiujesz wszystkie responsywne elementy zawarte w pliku za pomocą zmiennych zdefiniowanych w zapytaniu o media. Zatem wszystko, co musisz powtórzyć, to instrukcja importu.

Philipp Zedler
źródło
9

Nie jest to możliwe w przypadku SASS, ale jest to możliwe w przypadku zmiennych CSS (lub niestandardowych właściwości CSS ). Jedyną wadą jest obsługa przeglądarek - ale w rzeczywistości istnieje wtyczka PostCSS - postcss-css-variable - która „spłaszcza” użycie zmiennych CSS (co zapewnia obsługę również starszych przeglądarek).

Poniższy przykład działa świetnie z SASS (a ze zmiennymi postcss-css-otrzymujesz również wsparcie dla starszych przeglądarek).

$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);
}

Spowodowałoby to następujący CSS. Powtarzające się zapytania o media zwiększą rozmiar pliku, ale odkryłem, że wzrost jest zwykle pomijalny po zastosowaniu serwera WWW gzip(co zwykle robi się automatycznie).

.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
źródło
8

Dzięki świetnej odpowiedzi @ ronen i mapie dostępna jest prawdziwa moc:

    @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
        ));
    }

Teraz możliwe jest kierowanie .myDivo wiele więcej zapytań o media typu DRY przy użyciu kilku różnych wartości.


Mapa dokumentów: https://sass-lang.com/documentation/functions/map

Przykładowe użycie mapy: https://www.sitepoint.com/using-sass-maps/

Ben
źródło
Zajęło mi to sekundę, aby to zobaczyć, ale jest to ta sama technika, co odpowiedź @ ronen, po prostu użycie danych wejściowych mapy do obsługi wielu zmiennych jednocześnie. W żaden sposób nie dyskontuję jego wartości, ale trudniej było to zrozumieć, dopóki nie nawiązałem tego połączenia
John Neuhaus
5

Miałem ten sam problem.

$menu-widthZmienna powinna być 240 piks na ruchomej widzenia @media only screen and (max-width : 768px)i 340px na pulpicie widzenia.

Więc po prostu utworzyłem dwie zmienne:

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

A oto jak go użyłem:

.menu {
    width: $menu-width;
    @media only screen and (max-width : 768px) {
      width: $menu-mobile-width;
    }
}
Dawid Dyrcz
źródło
1

Dwie rekomendacje

1 Napisz swoje „domyślne” instrukcje CSS na małe ekrany i używaj zapytań o media tylko dla większych ekranów. Zwykle nie ma potrzeby max-widthzapytania o media.

Przykład (zakładając, że element ma klasę „kontener”)

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

.container {
  width: 960px;

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

2 Użycie zmiennych CSS, aby rozwiązać problem, jeśli to możliwe .

@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);
}

Uwaga:

Ponieważ będzie miał szerokość 1160 pikseli, gdy okno ma szerokość 1170 pikseli, lepiej będzie użyć szerokości 100% i maksymalnej szerokości 1160 pikseli, a element nadrzędny może mieć poziome dopełnienie 5 pikseli, o ile właściwość box-sizing jest ustawiona na border-box. Istnieje wiele sposobów rozwiązania tego problemu. Jeśli rodzic nie jest kontenerem flex lub grid, możesz użyć .container { margin: auto }.

Stephen
źródło