Search code examples
cssfontssassgoogle-webfonts

@each with an @include mixin


I have a dropdown of 30 fonts where I'd like the font of the dropdown to also display the Google Font (for visual representation, of course!) example text.

I have the HTML with the ID's, for example: font-oswald-light, font-oswald-reg, font-oswald-bold.

Using the @each directive, I wanted to do something like this:

@each $font-name, $font-mixin in (lato, Lato-Light),
                                  (open_sans, Open_Sans-Light),
                                  (oswald, Oswald-Light),
                                  (raleway, Raleway-Light),
                                  (roboto, Roboto-Light),
                                  (source_sans_pro, Source_Sans_Pro-Light),
                                  (ubuntu, Ubuntu-Light) {
  #font-#{$font-name}-light {
    @include #{$font-mixin};
  }
}

create the font-family:

@import url('https://fonts.googleapis.com/css?family=......)

@mixin Lato-Light {
  font-family: 'Lato', sans-serif;
  font-weight: 300;
  font-style: normal;
}

@mixin Lato-Reg {
  font-family: 'Lato', sans-serif;
  font-weight: 400;
  font-style: normal;
}

@mixin Lato-Bold {
  font-family: 'Lato', sans-serif;
  font-weight: 700;
  font-style: normal;
}

However, the @each does not like the @include inside to display the font-family. I'm not using any libraries (bourbon, compass, etc) to create the font-face().

My question is: what would be a way to dynamically create the @each font-ID list so it doesn't error out when trying to @include the families?


Solution

  • #font-#{$font-name}-light {
      @include #{$font-mixin};
    }
    

    First of all, this code can't work. Interpolation does not work this way in Sass. Sass expects an identifier after the @include keyword and when it comes across this code, it evaluates $font-mixin to the value represented by the variable and that's all it is, a value. Sass will not interpret that value as an identifier

    Secondly you don't need to create a mixin for every single font. That approach is not flexible and even less maintainable.

    I suggest one using one mixin that will loop through a fonts map to dynamically generate the css you want.

    $fonts-list:(
      lato-light: ("Lato", sans-serif) 300 normal,
      lato-reg: ("Lato", sans-serif) 400 normal,
      lato-bold: ("Lato", sans-serif) 700 normal,
      oswald-light: ("Oswald", sans-serif) 200 normal,
      oswald-reg: ("Oswald", sans-serif) 400 normal
    );
    
    
    @mixin fonts($family, $weight, $style) {
      font-family: $family;
      font-weight: $weight;
      font-style: $style;
    }
    
    @each $font, $attributes in $fonts-list {
      #font-#{$font} {
        @include fonts(nth($attributes, 1), nth($attributes, 2), nth($attributes, 3));
      }
    }
    

    The compiled CSS looks like this

    #font-lato-light {
      font-family: "Lato", sans-serif;
      font-weight: 300;
      font-style: normal;
    }
    
    #font-lato-reg {
      font-family: "Lato", sans-serif;
      font-weight: 400;
      font-style: normal;
    }
    
    #font-lato-bold {
      font-family: "Lato", sans-serif;
      font-weight: 700;
      font-style: normal;
    }
    
    #font-oswald-light {
      font-family: "Oswald", sans-serif;
      font-weight: 200;
      font-style: normal;
    }
    
    #font-oswald-reg {
      font-family: "Oswald", sans-serif;
      font-weight: 400;
      font-style: normal;
    }
    

    You can decide to have multiple font maps to store fonts based on variations i.e $fonts-light, which stores all the light variations of the fonts, $fonts-reg, which stores all the regular variations of the fonts... And then you could loop through each of them the same way. It all depends on the structure you prefer. I hope this helps