Usando variáveis ​​Sass com consultas de mídia CSS3

120

Estou tentando combinar o uso de uma variável Sass com consultas @media da seguinte maneira:

$base_width:1160px;

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

$base_width é então definido em vários pontos nas medidas baseadas em porcentagem da largura da folha de estilo para produzir layouts fluidos.

Quando faço isso, a variável parece ser reconhecida corretamente, mas as condições para a consulta de mídia não. Por exemplo, o código acima produz um layout de 1160px, independentemente da largura da tela. Se eu inverter as instruções @media assim:

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

Ele produz um layout de 960px, novamente independentemente da largura da tela. Observe também que, se eu remover a primeira linha, $base_width: 1160px;ele retornará um erro para uma variável indefinida. Alguma ideia do que estou perdendo?

Jeremy S.
fonte
Duplicado stackoverflow.com/questions/40739695/...
Serkan KONAKCI

Respostas:

96

Isso simplesmente não é possível. Já o gatilho @media screen and (max-width: 1170px)acontece no lado do cliente.

Alcançar o resultado esperado só seria possível se o SASS pegasse todas as regras e propriedades em sua folha de estilo contendo sua $base_widthvariável e as copiasse / alterasse de acordo.

Uma vez que não funcionará automaticamente, você pode fazê-lo manualmente desta forma:

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

Isso não é realmente SECO, mas o melhor que você pode fazer.

Se as alterações forem as mesmas todas as vezes, você também pode preparar um mixin contendo todos os valores alterados, portanto, não será necessário repeti-lo. Além disso, você pode tentar combinar o mixin com mudanças específicas. Gostar:

@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

E o Mixin ficaria assim

=base_width_changes($base_width)
    #wrapper
        width: $base_width
woerndl
fonte
6
Ainda é o caso de SASS não funcionar com $ variáveis ​​dentro de consultas @media?
HappyTorso
1
@HappyTorso sempre será o caso, até que exista um compilador Sass do lado do cliente (por exemplo, em JavaScript) que seja executado no redimensionamento da janela de visualização. As variáveis ​​Sass são compiladas para valores estáticos no momento da construção; eles não existem mais em tempo de execução. As consultas de mídia são avaliadas em tempo de execução, quando os atributos de mídia na consulta mudam.
ericsoco
26
@ericsoco Não vejo por que isso é uma razão. O SASS poderia facilmente (?) Apenas rastrear todos os usos da variável e inserir consultas de mídia onde eles são usados; $foo: 1rem; @media (min-width: 10rem) {$foo: 2rem} .class {font-size: $foo} .class2 {padding: $foo}resultaria em.class {font-size: 1rem} .class2 {padding: 1rem} @media (min-width: 10rem) (.class {font-size: 2rem} .class2 {padding: 2rem}}
powerbuoy
1
sim sass é um pré-processador, não há razão para "Isso simplesmente não é possível" ... Ele poderia fazer isso, simplesmente não foi implementado.
Ini
54

Semelhante à resposta de Philipp Zedler, você pode fazer isso com um mixin. Isso permite que você tenha tudo em um único arquivo, se desejar.

    @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
fonte
1
mais cedo ou mais tarde, você encontrará esse problema, por: sitepoint.com/cross-media-query-extend-sass
InsOp
Esta é melhor do que a minha resposta!
Philipp Zedler
1 amo esta solução. Combine isso com um mapa ( sass-lang.com/documentation/functions/map ) e você terá algum poder - farei uma solução separada aqui para explicar. Editar: pronto. stackoverflow.com/a/56894640/385273
Ben
9

Edit: Por favor, não use esta solução. A resposta por ronen é muito melhor.

Como uma solução DRY, você pode usar a @importinstrução dentro de uma consulta de mídia, por exemplo, assim.

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

Você define todos os elementos responsivos no arquivo incluído usando as variáveis ​​definidas na consulta de mídia. Portanto, tudo o que você precisa repetir é a instrução import.

Philipp Zedler
fonte
9

Isso não é possível com SASS, mas é possível com variáveis ​​CSS (ou propriedades personalizadas CSS ). A única desvantagem é o suporte do navegador - mas na verdade existe um plugin PostCSS - postcss-css-variables - que "nivela" o uso de variáveis ​​CSS (que também oferece suporte para navegadores mais antigos).

O exemplo a seguir funciona muito bem com SASS (e com postcss-css-variables você obtém suporte para navegadores mais antigos também).

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

Isso resultaria no seguinte CSS. As consultas de mídia repetitivas aumentam o tamanho do arquivo, mas descobri que o aumento geralmente é insignificante depois que o servidor da web é aplicado gzip(o que geralmente acontece automaticamente).

.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
fonte
8

Com a ótima resposta de @ronen e um mapa, existe algum poder real disponível:

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

Agora é possível ter muito mais consultas de mídia DRY direcionadas .myDivcom vários valores diferentes.


Documentos do mapa: https://sass-lang.com/documentation/functions/map

Exemplo de uso de mapa: https://www.sitepoint.com/using-sass-maps/

Ben
fonte
Levei um segundo para ver, mas esta é a mesma técnica da resposta de @ronen, apenas usando uma entrada de mapa para suportar várias variáveis ​​ao mesmo tempo. Não estou descartando seu valor de forma alguma, mas era mais difícil de entender até que fiz essa conexão
John Neuhaus
5

Eu tive o mesmo problema.

A $menu-widthvariável deve ser 240px na visualização móvel@media only screen and (max-width : 768px) e 340px na visualização desktop .

Então, simplesmente criei duas variáveis:

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

E aqui está como eu usei:

.menu {
    width: $menu-width;
    @media only screen and (max-width : 768px) {
      width: $menu-mobile-width;
    }
}
Dawid Dyrcz
fonte
1

Duas recomendações

1 Escreva suas instruções CSS "padrão" para telas pequenas e use apenas consultas de mídia para telas maiores. Normalmente, não há necessidade de uma max-widthconsulta à mídia.

Exemplo (supondo que o elemento tenha a classe "contêiner")

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

.container {
  width: 960px;

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

2 Use variáveis ​​CSS para resolver o problema, se puder .

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

Nota:

Uma vez que terá a largura de 1160 px quando a janela tem uma largura de 1170 px, pode ser melhor usar uma largura de 100% e largura máxima de 1160 px, e o elemento pai pode ter um preenchimento horizontal de 5 px, contanto que a propriedade box-sizing é definida como border-box. Existem várias maneiras de resolver o problema. Se o pai não for um contêiner flexível ou de grade, você poderá usar .container { margin: auto }.

Stephen
fonte