Search code examples
csssassmixinsextend

SCSS - Using @mixins in an @extend based method (classy css)


I was taken with Una Kravits method of creating her SCSS. https://una.im/classy-css/

The issue I am running into is at the bottom of that page where she is using a @mixin to make building out easier. But it seems to me if I use a mixin in the manner shown, I will end up with duplicated code.

Some examples taken from her post:

Without the use of a mixin

$color--primary: #b29;
$color--secondary: #19d;

%btn--base {
  border: 1px solid currentColor;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;

  &:hover {
    color: white;
    background: black;
  }
}

%btn--primary {
  @extend %btn--base;
  color: $color--primary;
  font-size: 1.5em;
}

%btn--secondary {
  @extend %btn--base;
  color: $color--secondary;
  font-size: 1.1em;
}

.hero {
  &__btn {
    @extend %btn--primary;
  }
}

.sidebar {
  &__btn {
    @extend %btn--secondary;
  }
}

.global-nav {
    &__btn {
      @extend %btn--secondary;
      &--login {
        @extend .global-nav__btn;
        margin-right: 1em;
        // at this point, you're
        // styling .global-nav__btn--login
    }
  }
}

Results in css without repeats

.hero__btn, .sidebar__btn, .global-nav__btn, .global-nav__btn--login {
  border: 1px solid currentColor;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;
}
.hero__btn:hover, .sidebar__btn:hover, .global-nav__btn:hover, .global-nav__btn--login:hover {
  color: white;
  background: black;
}

.hero__btn {
  color: #b29;
  font-size: 1.5em;
}

.sidebar__btn, .global-nav__btn, .global-nav__btn--login {
  color: #19d;
  font-size: 1.1em;
}

.global-nav__btn--login {
  margin-right: 1em;
}

Okay, so far so good. But then she suggests using a @mixin. Now I really like this idea, I like being able to use the @if @else, and I really like being able to pass arguments in with the @mixin and the silent placeholder. Her example only builds out once css statement (well two with the hover). For example I will add a few more below the original example. Notice how the resulting css has repeats. Am I missing something here? I could go with something like the example from above, but again, I do really like the options of using the @mixin in conjunction for it's options.

Example

// the colon after the argument denotes a default value
// creating the constructor function (mixin)

@mixin btn-me($color: hotpink, $size: normal) {
  border: 1px solid $color;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;

  @if $size == 'small' {
    font-size: .8em;
  } @else {
    font-size: 1.2em;
  }

  &:hover {
    color: white;
    background: $color;
  }
}

// creating placeholder classes to extend from and reference

%btn--primary {
  @include btn-me; // no arguments means it takes defaults
}

%btn--secondary {
  @include btn-me(blue, small);
}

%additional--btn--to--show--repeats {
  @include btn-me(green, small);
}

// instantiating the code with semantic naming
// this is the only moment that we are writing
// any code to be compiled
.hero__btn {
  @extend %btn--secondary;
}
.additional__btn {
  @extend %btn--secondary;
}
.additional__btn__two {
  @extend %btn--primary;
}
.additional__btn__three {
  @extend %additional--btn--to--show--repeats;
}

Resulting repeating CSS

.additional__btn__two {
  border: 1px solid hotpink;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;
  font-size: 1.2em;
}
.additional__btn__two:hover {
  color: white;
  background: hotpink;
}

.hero__btn, .additional__btn {
  border: 1px solid blue;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;
  font-size: .8em;
}
.hero__btn:hover, .additional__btn:hover {
  color: white;
  background: blue;
}

.additional__btn__three {
  border: 1px solid green;
  border-radius: 1.5em;
  background: none;
  outline: none;
  transition-duration: .25s;
  cursor: pointer;
  margin: 30px;
  padding: .5em 1em;
  font-size: .8em;
}
.additional__btn__three:hover {
  color: white;
  background: green;
}

Solution

  • Unfortunately thats just how mixins work. If you wanted to reduce your file size, I would consider using the mixin only for the properties you would change, and using a regular extend for the unchanging css rules.

    @mixin btn-me($color: hotpink, $size: normal) {
      border: 1px solid $color;
    
      @if $size == 'small' {
        font-size: .8em;
      } @else {
        font-size: 1.2em;
      }
    
      &:hover {
            background: $color;
      }
    }
    
    %btn {
      border-radius: 1.5em;
      background: none;
      outline: none;
      transition-duration: .25s;
      cursor: pointer;
      margin: 30px;
      padding: .5em 1em;
    
      &:hover {
        color: white;
      }
    }
    
    %btn--primary {
      @include btn-me; // no arguments means it takes defaults
      @extend %btn;
    }
    
    %btn--secondary {
      @include btn-me(blue, small);
      @extend %btn;
    }