A library for creating object-oriented sass components.
Proudly sponsored by Marketdial
npm install --save sass-deployables
// for example, if using webpack node-sass// ...
Deployables allow you to define a component with internal state properties, which you can then reference from the content of the component. Then you can define different versions and transformations of the component, with different values of those state properties, and the changes you make will cascade to all the references.
Here's a basic example.
// activates this selector as a deployable. only valid on a single simple selector// creates a new internal property, and sets the default to red// normal css attributes are of course fine// uses the color internal property, outputting its value to border-color// creates a new version, with blue as its state// creates a new transform,// saying that every version should have a lightened hover state// builds the deployable
This will output something equivalent to this:
See how the Deployable remembered the reference (
dy-output), and output an adjusted version of it for
.primary? And notice how the hover transformation was only defined once but was output for both versions?
This gets much more powerful when you have more references, versions, and transforms.
Compiles to something like:
Using Deployables will help you cut down on a huge amount of redundancy.
There are six mixins to create Deployables, and one to build them.
@mixin dy-deployable($parent-selector: null)
@mixin dy-define-state($name, $value)
@mixin dy-define-version($name, $value)
@mixin dy-define-transform($name, $func, $func-args...)
@mixin dy-output($css-prop, $var-name: null)
@mixin dy-output-function($css-prop, $func, $func-args...)
Thorough explanations are given below.
Deployables are made of a few core concepts:
- States. Internal variables the component has.
- Versions. Distinct types of the component. Each version should have at least one state variable that is different than all the others.
- Transforms. Changes that should be applied to the state variables no matter what they are.
- References, or the Content. The uses of the state variables, made in the content of the component.
Defining a new state variable is simple. Just call
dy-define-state in the base of the component, in the "default" version.
// initialize// everything at the base of the component is the "default" version
Now that variable can be referred to in the content, given different values in different versions, and changed in transforms.
Note: you can't call
dy-define-state in versions, transforms, or nested blocks.
// -> ERROR, invalid location to set deployable state// -> ERROR, can't call dy-define-state in version// use dy-define-version instead
In order to output the state values in a way that will be remembered and used for every Version, you can use the
dy-output-function functions. You can even call these functions in nested blocks, and they'll still work exactly as expected.
// has two forms// one that outputs to the css property with the same name// -> color: the value of color// and one that outputs to a named css property// -> border-color: the value of color// supply the css property to be output to,// the function name,// and the list of arguments,// using the format 'this.statevar' to refer to state properties// every version will have a .nested-block with the appropriate background-color// ... versions and transforms ...
In different Versions and Transforms, you can override the content outputs, or add new ones.
// this will override the normal "border-color: color" output// this will only output for this version
Compiles to something like:
A Version is any different kind of the component that is distinct from all the others, and doesn't overlap with them.
A common example is different kinds of buttons:
// the default value
Each of those types (
.danger) can't coexist with the others. A button can't be both
.danger. That's what makes them Versions, their separateness.
Even though Versions shouldn't overlap, you can have different kinds of Versions changing different qualities. As long as they don't conflict with each other everything will work. A simple example is a button with different Versions for colors, and another set of Versions for sizes.
// states, outputs, and versions for coloring// states, outputs, and versions for sizing
Now when you make buttons in your templates, you can use Versions for colors and for sizes together, like
.button.primary.big. As long as these interleaved Versions don't operate on the same States, they'll work great.
Version Selector Limitations
Right now, the rules for versions are pretty specific. A valid version is any selector that starts with
&. Here are some examples of valid versions.
- Compounding selectors:
- Attributes or psueudo-classes:
You can define Transforms on the component. These work very similarly to Versions, but instead of setting the state directly, you define a change to be made to it.
Transforms will be applied to all versions, so you only have to declare them once.
All transforms you define will be compounded on top of each other, in every combination. This means that when something has multiple transforms applied to it at the same time, all the transform functions will be run in order of their definition.
Here's an example.
// first both of them by themselves// then both together// first the fade-out is applied,// since :hover was specified first,// and then the adjust-hue
This compounding also occurs for content references. When two transforms both specify an output for an attribute, the one that was defined last will win.
// for all of these, the transform function(s) happen before the output function// here 20% desaturate happens, because open was defined last
If you like, you can provide your own compound Transforms. These will override all transform functions or content references you defined in the constituent Transforms, and only use the things you explicitly define.
// notice how this doesn't use anything from :hover and .open?// neither the fade-out from :hover, nor the desaturate from either// this transform stands on it's own
Obviously, the more Transforms you have the crazier the number of combinations can get (
(2 ^ n) - 1 to be exact). For the next version of this functionality, there will be an ability to control which Transforms compound and which don't, but for now just don't go too crazy.
Mingling Versions and Transforms
It isn't allowed to mix versions and transforms in the same block.
// -> ERROR, can't mingle versions and transforms// -> ERROR, can't mingle versions and transforms
You can create secondary Deployables that inherit the states, versions, and transforms of existing Deployables. Just make the component object available to other scopes, and pass it to
@extend is called for you when you do this.
// here's a normal css attribute// which will be extended when you inherit// calls @extend for you under the hood
This is just as useful with extend-only selectors. Just skip the
%// ... etc ...
Because they can be inherited, Deployables can be shared in libraries.
// in a package called 'cool-buttons'// 'main.sass'%// in user code// overriding an old Version// creating a new Version// creating a new Transform// ... etc ...
- transform compounding
$cobject saving, so the calls aren't so cluttered
- getting rid of
- control over compounding
- ability to strip versions and transforms out of extend deployables
- more flexible version definitions, allowing the
&to be in different places
- "related" versions, ones that are derived from a base value
- robust testing with sassaby
- thorough errors and warnings
If you'd like to contribute, just fork the project and then make a pull request!
dev directory, there is a
_dev.sass file that is set up for you to play around with the library. Running
npm run dev in the terminal will run that file, and compile to