Utilisation de variables Sass avec des requêtes multimédias CSS3

121

J'essaie de combiner l'utilisation d'une variable Sass avec des requêtes @media comme suit:

$base_width:1160px;

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

$base_width est ensuite défini à divers points dans les mesures basées sur le pourcentage de largeur de la feuille de style pour produire des mises en page fluides.

Lorsque je fais cela, la variable semble être reconnue correctement, mais les conditions de la requête multimédia ne le sont pas. Par exemple, le code ci-dessus produit une mise en page 1160px quelle que soit la largeur de l'écran. Si je retourne les instructions @media comme ceci:

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

Il produit une mise en page de 960 pixels, là encore quelle que soit la largeur de l'écran. Notez également que si j'enlève la première ligne, $base_width: 1160px;elle renvoie une erreur pour une variable non définie. Des idées de ce qui me manque?

Jeremy S.
la source
Dupliquée stackoverflow.com/questions/40739695/...
Serkan KONAKCI

Réponses:

97

Ce n'est tout simplement pas possible. Puisque le déclencheur @media screen and (max-width: 1170px)se produit du côté client.

Atteindre le résultat attendu ne serait possible que si SASS saisissait toutes les règles et propriétés de votre feuille de style contenant votre $base_widthvariable et les copiait / changeait en conséquence.

Comme cela ne fonctionnera pas automatiquement, vous pouvez le faire à la main comme ceci:

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

Ce n'est pas vraiment SEC mais le mieux que vous puissiez faire.

Si les changements sont les mêmes à chaque fois, vous pouvez également préparer un mixin contenant toutes les valeurs changeantes, vous n'avez donc pas besoin de le répéter. De plus, vous pouvez essayer de combiner le mixin avec des changements spécifiques. Comme:

@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

Et le Mixin ressemblerait à ça

=base_width_changes($base_width)
    #wrapper
        width: $base_width
woerndl
la source
6
Est-ce toujours le cas que SASS ne fonctionnera pas avec les variables $ dans les requêtes @media?
HappyTorso
1
@HappyTorso ce sera toujours le cas, jusqu'à ce qu'il y ait un compilateur Sass côté client (par exemple en JavaScript) qui s'exécute lors du redimensionnement de la fenêtre. Les variables Sass sont compilées en valeurs statiques au moment de la construction; ils n'existent plus au moment de l'exécution. Les requêtes multimédias sont évaluées au moment de l'exécution, lorsque les attributs multimédias de la requête changent.
ericsoco
26
@ericsoco Je ne vois pas pourquoi c'est une raison. SASS pourrait facilement (?) Suivre toutes les utilisations de la variable et insérer des requêtes multimédias là où elles sont utilisées; $foo: 1rem; @media (min-width: 10rem) {$foo: 2rem} .class {font-size: $foo} .class2 {padding: $foo}aboutirait à.class {font-size: 1rem} .class2 {padding: 1rem} @media (min-width: 10rem) (.class {font-size: 2rem} .class2 {padding: 2rem}}
powerbuoy
1
yep sass est un préprocesseur, il n'y a aucune raison pour "ce n'est tout simplement pas possible" ... Il pourrait le faire, il n'est tout simplement pas implémenté.
Ini
54

Semblable à la réponse de Philipp Zedler, vous pouvez le faire avec un mixin. Cela vous permet de tout avoir dans un seul fichier si vous le souhaitez.

    @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
la source
1
tôt ou tard, vous rencontrerez ce problème, tho: sitepoint.com/cross-media-query-extend-sass
InsOp
Celui-ci mieux que ma réponse!
Philipp Zedler
+1 aime cette solution. Combinez cela avec une carte ( sass-lang.com/documentation/functions/map ) et vous avez un peu de pouvoir - je vais faire une solution distincte ici pour expliquer. Edit: terminé. stackoverflow.com/a/56894640/385273
Ben
9

Edit: Veuillez ne pas utiliser cette solution. La réponse de Ronen est bien meilleure.

En tant que solution DRY, vous pouvez utiliser l' @importinstruction dans une requête multimédia, par exemple comme ceci.

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

Vous définissez tous les éléments réactifs du fichier inclus à l'aide des variables définies dans la requête multimédia. Donc, tout ce que vous devez répéter est la déclaration d'importation.

Philipp Zedler
la source
9

Ce n'est pas possible avec SASS, mais c'est possible avec des variables CSS (ou des propriétés CSS personnalisées ). Le seul inconvénient est la prise en charge des navigateurs - mais il existe en fait un plugin PostCSS - postcss-css-variables - qui "aplatit" l'utilisation des variables CSS (ce qui vous permet également de prendre en charge les navigateurs plus anciens).

L'exemple suivant fonctionne très bien avec SASS (et avec postcss-css-variables, vous bénéficiez également d'une prise en charge pour les navigateurs plus anciens).

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

Cela donnerait le CSS suivant. Les requêtes multimédias répétitives augmenteront la taille du fichier, mais j'ai trouvé que l'augmentation est généralement négligeable une fois que le serveur Web s'applique gzip(ce qu'il fera généralement automatiquement).

.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
la source
8

Avec la bonne réponse de @ ronen et une carte, il y a une vraie puissance 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
        ));
    }

Il est maintenant possible d'avoir beaucoup plus de requêtes de médias DRY ciblant .myDivavec un tas de valeurs différentes.


Documentation de la carte: https://sass-lang.com/documentation/functions/map

Exemple d'utilisation de la carte: https://www.sitepoint.com/using-sass-maps/

Ben
la source
Il m'a fallu une seconde pour le voir, mais c'est la même technique que la réponse de @ ronen, en utilisant simplement une entrée de carte pour prendre en charge plusieurs variables à la fois. Je n'écarte en aucun cas sa valeur, mais c'était plus difficile à comprendre jusqu'à ce que je fasse ce lien
John Neuhaus
5

J'ai eu le même problème.

La $menu-widthvariable doit être de 240 pixels sur la vue mobile@media only screen and (max-width : 768px) et de 340 pixels sur la vue du bureau .

J'ai donc simplement créé deux variables:

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

Et voici comment je l'ai utilisé:

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

Deux recommandations

1 Rédigez vos instructions CSS «par défaut» pour les petits écrans et n'utilisez les requêtes multimédias que pour les écrans plus grands. Il n'y a généralement pas besoin d'une max-widthrequête multimédia.

Exemple (en supposant que l'élément a la classe "container")

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

.container {
  width: 960px;

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

2 Utilisez des variables CSS pour résoudre le problème, si vous le pouvez .

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

Remarque:

Comme il aura la largeur de 1160px lorsque la fenêtre a une largeur de 1170px, il peut être préférable d'utiliser une largeur de 100% et une largeur maximale de 1160px, et l'élément parent peut avoir un remplissage horizontal de 5px, tant que la propriété box-sizing est définie sur border-box. Il existe de nombreuses façons de résoudre le problème. Si le parent n'est pas un conteneur flex ou grid, vous pouvez utiliser .container { margin: auto }.

Stephen
la source