Skip to content

Instantly share code, notes, and snippets.

@sgregson
Last active August 29, 2015 14:11
Show Gist options
  • Save sgregson/aca4fe6b2adff9b0fcdc to your computer and use it in GitHub Desktop.
Save sgregson/aca4fe6b2adff9b0fcdc to your computer and use it in GitHub Desktop.
LibSass Configuable Objects (syntax 1)
// ----
// libsass (v3.1.0-beta)
// ----
// LibSass Configurable Objects Syntax 1
// uses selector() to include content
//
// @author Spencer Gregson
// @credit http://hugogiraudel.com/2014/05/05/bringing-configuration-objects-to-sass/ - a modular way to build partials with signatures
// @credit map-merge-deep() - Pedr Browne (https://gist.github.com/Undistraction/681818d751b6dd06c0cc), edited for compatibility with libsass 3.0.2, no dependencies
//////////////////////REQUIRED UTILITIES////////////////////////////////////////
// map-merge-deep()
//
// Merges the contents of two maps, depth-first
// ```map-merge-deep($base, $ext)```
//
// Styleguide functions.map-merge-deep
@function map-merge-deep($map-old, $map-new, $overwrite: true) {
// Iterate through each value of the new map
@each $key, $new-value in $map-new {
@if map-has-key($map-old, $key) {
// There is an existing key
$old-value: map-get($map-old, $key);
@if type-of($new-value) == map and type-of($old-value) == map {
// If both are maps, recurse regardless of $overwrite
$merged-value: map-merge-deep($old-value, $new-value);
$map-old: map-merge($map-old, ($key: $merged-value));
} @else {
// Otherwise check $overwrite
@if $overwrite{
$map-old: map-merge($map-old, ($key: $new-value));
}
}
} @else {
// There is no existing key so add
$map-old: map-merge($map-old, ($key: $new-value));
}
}
@return $map-old;
}
// init()
//
// Alias to initialize a mixin configuration
// deeply merges two maps - where the keys exist in both, the second takes priority
// ```
// $conf: init((
// '.button': ( color: red ),
// '.button:hover': ( text-decoration: underline )
// ), $conf);
// ```
//
// Styleguide functions.init
@function init($base, $ext) {
@return map-merge-deep($base, $ext);
}
// selector()
//
// Renders a selector using the given configuration object
// Each key-value pair in $conf will output as a CSS property-value
// + When the value is 'null', the property won't render
// + When the selector is 'null', the selector won't render
//
// Styleguide mixins.selector
@mixin selector($sel, $conf) {
// remove everything if the selector instance is nulled-out
@if map-get($conf, $sel) != null {
#{$sel} {
// debug: map-values(map-get($conf, $sel));
// content from the selector initialization
@content;
// CSS property-value pairs from the selector instance
@each $key, $value in map-get($conf, $sel) {
// Permit duplicate keys using an underscore prefix (use case: rgba() degradation)
@if str-index($key, '_') == 1 {
$key: str-slice($key, 2);
}
@if str-index($key, '@extend') != 1 {
#{$key}: #{$value};
} @else {
@extend #{$value};
}
}
}
}
}
//////////////////////GLOBALS///////////////////////////////////////////////////
/*//////////////////////
//// SIMPLE EXAMPLE ////
//////////////////////*/
/*SHARED*/
$primary: #0072BC;
@mixin box_sizing($type) {
-webkit-box-sizing: $type;
-moz-box-sizing: $type;
box-sizing: $type;
}
%full_width { width: 100%; }
%full_height { height: 100%; }
// 1) INITIALIZE the component:
// required properties/settings go into the content of the @include selector() directive
// any properties which you expect to vary live in the init() call
@mixin buttons($conf: ()) {
$conf: init((
'.button': (
color: red
),
'.button:hover': (
text-decoration: underline
)
), $conf);
@include selector('.button', $conf) {
@extend %full_width; // every instance of .button will extend %full_width
// @include box_sizing($type:border-box);
}
@include selector('.button:hover', $conf);
}
// 2) INSTANTIATE the element
// keys set in the instance will either:
// * override the default value
// * append new property
/*Page Instance 1 (default values) */
@include buttons();
/*Page Instance 2 (override+append) */
.parent {
@include buttons((
'.button': (
color: blue,
background: white,
'@extend': '%full_height'
),
'.button:hover': (
text-decoration: null,
color: yellow
)
));
}
/*////////////////////////
//// COMPLETE EXAMPLE ////
////////////////////////*/
//////////////////////PARTIALS FILE/////////////////////////////////////////////
@mixin topnav-mainsearch($conf:()) {
$conf: init((
'input.search': (
padding: 30px 0 10px,
width: null,
margin-top: 2px,
outline: none,
),
'.search .btn_search': (
position: absolute,
top: 0,
right: 0
)), $conf);
@include selector('input.search', $conf) {
// THIS IS WHERE YOU PLACE CONTENT THAT MUST NOT VARY FOR THIS SELECTOR
@include box_sizing($type: 'border-box');
@content; // to allow the instance to have content
}
@include selector('.search .btn_search', $conf);
}
//////////////////////PAGE FILE VARIATIONS//////////////////////////////////////
/**********************************
VARIATION 1: PARAMETERLESS
*/
@include topnav-mainsearch();
/**********************************
VARIATION 2: KILLING A SELECTOR
'null' removes .search .btn_search {...}
*/
@include topnav-mainsearch((
'.search .btn_search': null
));
/**********************************
VARIATION 3: PARTIAL OVERWRITE with parent selector
.search .btn_search {
margin: 20px 6px 0 0,
right: null
}
*/
a.parent {
@include topnav-mainsearch((
'.search .btn_search': (
margin: 20px 6px 0 0,
right: null
)
));
}
/**********************************
VARIATION 4: DEEP EXTENSION
* both are extended
* duplicate selector enabled with underscore prefix
* star-hack possible with quoted value
*/
@include topnav-mainsearch((
'input.search': (
padding: 6px 0 6px 10px,
width: 1px,
_width: '3px\9 /*ie-hack*/',
'*width': 2px,
border-color: $primary
),
'.search .btn_search': (
margin: 8px 6px 0 0
)
));
/*//////////////////////
//// SIMPLE EXAMPLE ////
//////////////////////*/
/*SHARED*/
.button, .parent .button {
width: 100%;
}
.parent .button {
height: 100%;
}
/*Page Instance 1 (default values) */
.button {
color: red;
}
.button:hover {
text-decoration: underline;
}
/*Page Instance 2 (override+append) */
.parent .button {
color: blue;
background: white;
}
.parent .button:hover {
color: yellow;
}
/*////////////////////////
//// COMPLETE EXAMPLE ////
////////////////////////*/
/**********************************
VARIATION 1: PARAMETERLESS
*/
input.search {
-webkit-box-sizing: "border-box";
-moz-box-sizing: "border-box";
box-sizing: "border-box";
padding: 30px 0 10px;
margin-top: 2px;
outline: none;
}
.search .btn_search {
position: absolute;
top: 0;
right: 0;
}
/**********************************
VARIATION 2: KILLING A SELECTOR
'null' removes .search .btn_search {...}
*/
input.search {
-webkit-box-sizing: "border-box";
-moz-box-sizing: "border-box";
box-sizing: "border-box";
padding: 30px 0 10px;
margin-top: 2px;
outline: none;
}
/**********************************
VARIATION 3: PARTIAL OVERWRITE with parent selector
.search .btn_search {
margin: 20px 6px 0 0,
right: null
}
*/
a.parent input.search {
-webkit-box-sizing: "border-box";
-moz-box-sizing: "border-box";
box-sizing: "border-box";
padding: 30px 0 10px;
margin-top: 2px;
outline: none;
}
a.parent .search .btn_search {
position: absolute;
top: 0;
margin: 20px 6px 0 0;
}
/**********************************
VARIATION 4: DEEP EXTENSION
* both are extended
* duplicate selector enabled with underscore prefix
* star-hack possible with quoted value
*/
input.search {
-webkit-box-sizing: "border-box";
-moz-box-sizing: "border-box";
box-sizing: "border-box";
padding: 6px 0 6px 10px;
width: 1px;
margin-top: 2px;
outline: none;
width: 3px /*ie-hack*/;
*width: 2px;
border-color: #0072BC;
}
.search .btn_search {
position: absolute;
top: 0;
right: 0;
margin: 8px 6px 0 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment