Uso de variables de Sass con consultas de medios CSS3

120

Estoy tratando de combinar el uso de una variable Sass con consultas @media de la siguiente manera:

$base_width:1160px;

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

$base_width luego se define en varios puntos en las medidas basadas en porcentajes de ancho de la hoja de estilo para producir diseños fluidos.

Cuando hago esto, la variable parece reconocerse correctamente, pero no las condiciones para la consulta de medios. Por ejemplo, el código anterior produce un diseño de 1160 px independientemente del ancho de la pantalla. Si cambio las declaraciones de @media así:

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

Produce un diseño de 960 px, nuevamente independientemente del ancho de la pantalla. También tenga en cuenta que si elimino la primera línea $base_width: 1160px;, devuelve un error para una variable indefinida. ¿Alguna idea de lo que me estoy perdiendo?

Jeremy S.
fuente
Duplicada stackoverflow.com/questions/40739695/...
Serkan KONAKCI

Respuestas:

96

Esto simplemente no es posible. Dado que el desencadenante @media screen and (max-width: 1170px)ocurre en el lado del cliente.

Lograr el resultado esperado solo sería posible si SASS tomara todas las reglas y propiedades en su hoja de estilo que contiene su $base_widthvariable y las copiara / cambiara en consecuencia.

Como no funcionará automáticamente, puede hacerlo a mano de esta manera:

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

Esto no es realmente SECO, pero es lo mejor que puede hacer.

Si los cambios son los mismos cada vez, también puede preparar un mixin que contenga todos los valores cambiantes, por lo que no tendrá que repetirlo. Además, puede intentar combinar el mixin con cambios específicos. Me gusta:

@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

Y el Mixin se vería así

=base_width_changes($base_width)
    #wrapper
        width: $base_width
woerndl
fuente
6
¿Sigue siendo cierto que SASS no funcionará con $ variables dentro de las consultas @media?
HappyTorso
1
@HappyTorso siempre será el caso, hasta que exista un compilador Sass del lado del cliente (por ejemplo, en JavaScript) que se ejecute en el cambio de tamaño de la ventana gráfica. Las variables de Sass se compilan en valores estáticos en el momento de la compilación; ya no existen en tiempo de ejecución. Las consultas de medios se evalúan en tiempo de ejecución, cuando cambian los atributos de medios en la consulta.
ericsoco
26
@ericsoco No veo por qué esa es una razón. SASS podría fácilmente (?) Rastrear todos los usos de la variable e insertar consultas de medios donde se usan; $foo: 1rem; @media (min-width: 10rem) {$foo: 2rem} .class {font-size: $foo} .class2 {padding: $foo}daría como resultado.class {font-size: 1rem} .class2 {padding: 1rem} @media (min-width: 10rem) (.class {font-size: 2rem} .class2 {padding: 2rem}}
powerbuoy
1
sí, sass es un preprocesador, no hay razón para "Esto simplemente no es posible" ... Podría hacerlo, simplemente no está implementado.
Ini
54

Similar a la respuesta de Philipp Zedler, puede hacerlo con un mixin. Eso le permite tener todo en un solo archivo si lo desea.

    @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
fuente
1
tarde o temprano, encontrará este problema, aunque: sitepoint.com/cross-media-query-extend-sass
InsOp
¡Este mejor que mi respuesta!
Philipp Zedler
+1 me encanta esta solución. Combine esto con un mapa ( sass-lang.com/documentation/functions/map ) y tendrá algo de poder; aquí crearé una solución separada para explicar. Editar: hecho. stackoverflow.com/a/56894640/385273
Ben
9

Editar: No utilice esta solución. La respuesta de ronen es mucho mejor.

Como solución DRY, puede utilizar la @importdeclaración dentro de una consulta de medios, por ejemplo, como esta.

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

Defina todos los elementos de respuesta en el archivo incluido utilizando las variables definidas en la consulta de medios. Entonces, todo lo que necesita repetir es la declaración de importación.

Philipp Zedler
fuente
9

Esto no es posible con SASS, pero es posible con variables CSS (o propiedades personalizadas de CSS ). El único inconveniente es la compatibilidad con el navegador, pero en realidad hay un complemento PostCSS, postcss-css-variables , que "aplana" el uso de variables CSS (lo que también le brinda soporte para navegadores antiguos).

El siguiente ejemplo funciona muy bien con SASS (y con postcss-css-variables también obtiene soporte para navegadores más antiguos).

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

Esto daría como resultado el siguiente CSS. Las consultas de medios repetitivas aumentarán el tamaño del archivo, pero he descubierto que el aumento suele ser insignificante una vez gzipque se aplica el servidor web (lo que suele suceder automáticamente).

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

Con la gran respuesta de @ ronen y un mapa, hay algo de poder real disponible:

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

Ahora es posible tener muchas más consultas de medios DRY dirigidas .myDivcon un montón de valores diferentes.


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

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

Ben
fuente
Me tomó un segundo verlo, pero esta es la misma técnica que la respuesta de @ ronen, solo usando una entrada de mapa para admitir múltiples variables a la vez. No estoy descontando su valor de ninguna manera, pero fue más difícil de entender hasta que hice esa conexión
John Neuhaus
5

Yo tuve el mismo problema.

La $menu-widthvariable debe ser de 240 px en la vista móvil@media only screen and (max-width : 768px) y 340 px en la vista de escritorio .

Así que simplemente he creado dos variables:

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

Y así es como lo he usado:

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

Dos recomendaciones

1 Escriba sus declaraciones CSS "predeterminadas" para que sean para pantallas pequeñas y use solo consultas de medios para pantallas más grandes. Por lo general, no es necesario realizar una max-widthconsulta de medios.

Ejemplo (asumiendo que el elemento tiene la clase "contenedor")

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

.container {
  width: 960px;

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

2 Utilice variables CSS para resolver el problema, si puede .

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

Dado que tendrá un ancho de 1160px cuando la ventana tenga un ancho de 1170px, puede ser mejor usar un ancho de 100% y un ancho máximo de 1160px, y el elemento principal puede tener un relleno horizontal de 5px, siempre que la propiedad box-sizing se establece en border-box. Hay muchas formas de resolver el problema. Si el padre no es un contenedor flexible o de cuadrícula, puede usar .container { margin: auto }.

Stephen
fuente