Skip to content

Instantly share code, notes, and snippets.

@kgcreative
Last active April 23, 2017 20:20
Show Gist options
  • Save kgcreative/820db39fc32bbe4997ec426067712113 to your computer and use it in GitHub Desktop.
Save kgcreative/820db39fc32bbe4997ec426067712113 to your computer and use it in GitHub Desktop.
Element System in SCSS
@charset 'UTF-8';
////
/// Smart merge
/// @author Kevin Garcia
////
/// Returns a merged map, a single map, or a null value, depending on the results of the function's operations
///
/// @param {map} default-map - The default map the mixin consumes
/// @param {map} map [null] - The optional override map to merge in
///
/// @example scss - SCSS
/// $map--default: (
/// property1: value1,
/// property2: value2,
/// property3: value3,
/// // etc...
/// )!default;
///
/// $my-new-map: (
/// property1: my-value1,
/// property3: null, // By explicitly declaring a
/// // null value in the merged map,
/// // we can suppress this property
/// my-new-property: my-new-value,
/// // etc...
/// );
///
/// $merged-map: smart-merge($map--default, $my-new-map);
///
///
/// @example scss - Merged SCSS Map
/// $merged-map: (
/// property1: my-value1,
/// property2: value2,
/// my-new-property: my-new-value,
/// // etc...
/// );
///
@function smart-merge($default-map, $map) {
@if $map {
@if $default-map {
@return map-merge($default-map, $map);
} @else {
@return $map;
}
} @else {
@if $default-map {
@return $default-map;
} @else {
@warn 'Could not return map.';
@return false;
}
}
}
@charset "UTF-8";
////
/// Nested properties
/// @author Kevin Garcia
////
///
/// Returns a list of recursively nested `$property`: `$value` pairs.
/// The mixin will continue to iterate as long as any map value is a nested map.
///
/// @param {css property} $_property - The CSS Property
/// @param {css value | map} $_value - The CSS Value the property contains, or, if a map, the properties of the nested selector.
///
/// @access private
@mixin _nested-properties($_property, $_value) {
@if type-of($_value) == map {
#{$_property} {
@each $_nested-property, $_nested-value in $_value {
@if type-of($_nested-value) == map {
@include _nested-properties($_nested-property, $_nested-value);
} @else {
#{$_nested-property}: #{$_nested-value};
}
}
}
} @else {
#{$_property}: #{$_value};
}
}
@charset 'UTF-8';
////
/// Properties
/// @author Kevin Garcia
////
/// Returns a list of `$property`: `$value` pairs
///
/// @param {map} default-settings - The default map the mixin consumes
/// @param {map} settings [null] - The optional override map to merge in
///
/// @example scss - source SCSS
/// $default-map: (
/// color: blue,
/// margin: 0 auto,
/// );
///
/// $map-override: (
/// color: red,
/// overflow: hidden,
/// );
///
/// .default-element {
/// @include properties($default-map);
/// }
///
/// .override-element {
/// @include properties($map-override);
/// }
///
/// .merged-element {
/// @include properties($default-map, $map-override);
/// }
///
/// @example css - output CSS
///
/// .default-element {
/// color: blue;
/// margin: 0 auto;
/// }
///
/// .override-element: {
/// color: red;
/// overflow: hidden;
/// };
///
/// .merged-element {
/// color: red;
/// margin: 0 auto;
/// overflow: hidden;
/// }
///
/// @example scss - When to use default + merged elements (scss)
///
/// // The main use case is when one wants to output variations of an element.
///
/// $default-button: (
/// background-color: #333333,
/// color: white,
/// margin: 0 auto,
/// padding: 1em,
/// );
///
/// $blue-button: (
/// background-color: blue,
/// );
///
/// // You can pass it a default map
/// .button {
/// @include element-properties($default-button);
/// }
///
/// // You can pass it a named map
/// .button--blue {
/// @include element-properties($default-button, $blue-button);
/// }
///
/// // You may also pass it an inline map
/// .button--green {
/// @include element-properties($default-button, (font-size: 1.125em, background-color: green, color: inherit, margin: null, ));
/// }
///
/// @example css - When to use default + merged elements (output)
///
/// .button: {
/// background-color: #333333;
/// color: white;
/// margin: 0 auto;
/// padding: 1em;
/// };
///
/// .button--blue: (
/// background-color: blue;
/// color: white;
/// margin: 0 auto;
/// padding: 1em;
/// );
///
/// /* Notice that explicitly null values in overrides are removed from the output */
/// .button--green: (
/// background-color: green; /* <- overriden value */
/// color: inherit; /* <- overriden value */
/// padding: 1em; /* <- Notice that the margin property is gone. */
/// font-size: 1.125em /* <- values not present in the default map are amended to the end */
/// );
///
///
/// @requires [function] smart-merge
/// @requires [mixin] _nested-properties
///
@mixin properties($default-settings, $settings: null) {
$_settings: smart-merge($default-settings, $settings);
@each $_property, $_value in $_settings {
@include _nested-properties($_property, $_value);
}
}
@charset "UTF-8";
////
/// Element
/// @author Kevin Garcia
////
///
/// Returns an element with property:value pairs based on a map. If no maps are available, it outputs it's own @content
/// @content
///
/// @param {selector} $element - The CSS selector that the property map applies to
/// @param {map} default-settings [null] - The default map the mixin consumes
/// @param {map} settings [null] - The optional override map to merge in
///
///
/// @example scss - source SCSS
/// $default-map: (
/// color: blue,
/// margin: 0 auto,
/// );
///
/// $map-override: (
/// color: red,
/// overflow: hidden,
/// '&:after': (
/// content: '"pseudo element"',
/// display: block,
/// )
/// );
///
///
/// @include ('.element', $default-map, $map-override);
///
/// @example css - output CSS
/// .element {
/// color: red;
/// margin: 0 auto;
/// overflow: hidden;
/// }
///
/// .element:after {
/// content: "pseudo element";
/// display: block;
/// }
///
/// @requires {mixin} element-properties
@mixin element($element, $default-settings: null, $settings: null) {
$_settings: smart-merge($default-settings, $settings);
#{$element} {
@if ($_settings == null) {
@warn '$_settings map returned a null value. Attempting to use @content instead';
@content;
} @else {
@include properties($default-settings, $settings);
}
}
}
@charset "UTF-8";
////
/// Element group
/// @author Kevin Garcia
////
///
/// Returns a group of configurable elements based on a map.
///
/// @param {map} $element-group - The element map the mixin consumes
///
/// @example scss - SCSS
/// $default-element: (
/// background-color: purple,
/// margin-left: auto,
/// margin-right: auto,
/// margin-bottom: 2em,
/// display: inline,
/// );
///
/// $element-a: (
/// background-color: green,
/// );
///
/// $element-b: (
/// display: block,
/// margin-left: null,
/// margin-right: null,
/// );
///
/// $element-c: (
/// display: null,
/// overflow: hidden,
/// );
///
/// $group-1: (
/// '#test-a': (default-settings: $default-element, settings: $element-a),
/// '#test-b': (default-settings: $default-element, settings: $element-b),
/// '#test-c': (default-settings: $element-c),
/// );
///
/// @include element-group($group-1);
///
/// @example css - CSS output
///
/// #test-a {
/// background-color: green;
/// margin-left: auto;
/// margin-right: auto;
/// margin-bottom: 2em;
/// display: inline;
/// }
///
/// #test-b {
/// background-color: purple;
/// margin-bottom: 2em;
/// display: block;
/// }
///
/// #test-c {
/// overflow: hidden;
/// }
///
/// @requires {mixin} element
@mixin elements($elements) {
@each $element, $map in $elements {
$_default-settings: map-get($map, default-settings);
$_settings: map-get($map, settings);
@include element($element, $_default-settings, $_settings);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment