Cool, right? But I'm still only halfway there: my other-button class is having to pass an argument to a mixin, when that other button might make more sense as a child mixin with its own defaults, so it can be injected elsewhere into my styles.

@mixincancel-button@includebutton-base($color:blue)

Awesome. But here's the problem: @cancel-button doesn't know how to pass its own arguments to button-base unless you specify them directly ... so in an effort to expose properties consistently, you have to start writing some pretty silly stuff:

@mixincancel-button($color:blue)@includebutton-base($color:$color)

Once you're dealing with lots of arguments, having to explicitly define those arguments becomes painful and redundant.

So, that's the setup. Thanks for sticking with me so far. This is the problem I decided to try and solve: could I create a system in which Sass mixins inherited properties in a semi-classical way, instead of having to write all of that stuff out?

What I've written is a set of mixins that takes advantage of Sass' rendering order to store & retrieve parameters outside of mixins. Here's how it works:

you only ever pass one argument to a mixin: a Sass map, which defaults to ().

you call a mixin named override and pass it that single argument. This essentially tells Sass that this mixin is a child of another mixin, and stores the argument map in a global store. (If the mixin isn't the end of the inheritance chain, set $is_child to false.)

you set default attributes for this child mixin by calling a default mixin and passing it an attribute & value pair.

Anywhere along the way, instead of accessing variables directly, you can call fetch to retrieve them from the global storage.

There's a lot going on here, but this is the key takeaway: cancel-button no longer needs to know about the arguments used by button-base. This means that as you add arguments to button-base, you don't have to change anything about its children: cancel-button will automatically pass all arguments up the chain.

So, suppose I want to add support for a text-color argument, because I want to perform some color math on the text color and have it lighten on :hover. All I have to do is add two lines to button-base: a default value for text-color, and a style assignment.

Voila! cancel-button is "inheriting" all of the properties of button-base (and passes along arguments to it) without those properties having to be explicitly redefined. In a perfect world, this would be accomplished by some sort of actual extends command implemented natively, but until that day, this solution gives me something pretty special.

When you pass arguments with this technique, you're actually passing one single argument that is being stored in a global variable. override stores its $args map in this global store, overwriting whatever's there. If $is_child is true, it's the beginning of a new mixin inheritance chain, so it initializes the global store first.