Created
September 25, 2016 10:03
-
-
Save JawsomeJason/6bef728cf60367321504ec4d65f786ce to your computer and use it in GitHub Desktop.
Create scoped themes using Sass and CSS Custom Properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// options | |
// identifier for theme CSS custom properties (e.g. --theme__prop--theme-name) | |
$__theme-custom-property-prefix: theme !default; | |
// enable/disable CSS4 var(--custom-property) declarations | |
$__theme-enable-css-variables: true !default; | |
// enable/disable compatibility for legacy browsers. | |
// Warning: If enabled, cascade will not function, so you must inherit down | |
// the chain or re-declare themes where needed | |
$__theme-enable-decorator-fallbacks: true !default; | |
// color variable map in Sass | |
//stiletto, alpine, blue-bayoux | |
$__themes: ( | |
// theme-name. "default" is required. | |
default: ( | |
// properties should match up in each theme for theme-switching and cascading to work properly | |
background: #fff, | |
text: #333, | |
accent: blue | |
) | |
) !default; | |
/// Retrieves property/value from given theme | |
/// @param {string} $property-name Name of theme property | |
/// @param {boolean} $sass-value [false] Use hard value instead | |
/// of referencing CSS Custom Property | |
/// @param {string} $theme [local] Name of theme (default to local/current scope) | |
/// @return {string} the CSS Custom Property or value of theme property. | |
@function theme($property-name, $sass-value:false, $theme:local) { | |
@if $sass-value { | |
// local values don't exist in sass, so return to default if so | |
$theme: if($theme != local, $theme, default); | |
@return map-get(map-get($__themes,$theme),$property-name); | |
// if we're only returning the CSS4 variable | |
} @else if $__theme-enable-css-variables { | |
// return the requested theme property | |
@return var(--theme($property-name, $theme)); | |
} | |
@return null; | |
} | |
/// Gets a theme property as a CSS custom property | |
/// @param {string} $property Name of theme property | |
/// @param {string} $theme-name Name of theme | |
@function --theme($property, $theme-name) { | |
@return --#{$__theme-custom-property-prefix}__#{$property}--#{$theme-name}; | |
} | |
/// Default CSS to apply when including a theme | |
/// @access private | |
/// @param {string} $theme-name name of theme being applied | |
/// @param {boolean} $sass-value If enabled, include the actual | |
/// value instead of the CSS custom property | |
@mixin __theme-decorator($theme-name, $sass-value) { | |
@if ($sass-value and $__theme-enable-decorator-fallbacks) | |
or (not $sass-value and $__theme-enable-css-variables) { | |
background-color: theme(background, $sass-value, $theme-name); | |
color: theme(text, $sass-value, $theme-name); | |
::selection { | |
background: theme(accent, $sass-value, $theme-name); | |
color: theme(background, $sass-value, $theme-name); | |
} | |
::-moz-selection { | |
background: theme(accent, $sass-value, $theme-name); | |
color: theme(background, $sass-value, $theme-name); | |
} | |
} | |
} | |
/// Establishes a theme in the current Sass scope | |
/// @param {string} $theme-name The name of the theme being applied | |
/// @param {map} $config Configuration options | |
/// @property {boolean} $options.decorate [true] Include theme-decorator | |
/// @property {boolean} $options.local [true] Include as a local/scoped reference | |
/// @property {boolean} $options.include-css-variables [$__theme-enable-css-variables] Include scoped CSS Variables syntax | |
/// @property {boolean} $options.include-decorator-fallbacks [$__theme-enable-decorator-fallbacks] Include plain values (CSS fallback) | |
@mixin theme($theme-name, $config: ()) { | |
$defaults: ( | |
decorate: true, | |
local: true, | |
include-css-variables: $__theme-enable-css-variables, | |
include-decorator-fallbacks: $__theme-enable-decorator-fallbacks | |
); | |
$settings: $defaults; | |
@each $property, $value in $config { | |
$settings: map-merge($settings, ($property: $value)); | |
} | |
// spread settings to variables | |
$decorate: map-get($settings, decorate); | |
$local: map-get($settings, local); | |
$include-css-variables: map-get($settings, include-css-variables); | |
$include-decorator-fallbacks: map-get($settings, include-decorator-fallbacks); | |
// get theme name for scope | |
$prop-theme: if($local == true, local, $theme-name); | |
@each $property, $value in map-get($__themes, $theme-name) { | |
//set current theme | |
@if $include-css-variables { | |
#{--theme($property, $prop-theme)}: $value; | |
} | |
} | |
@if $decorate { | |
@if $include-decorator-fallbacks { | |
@include __theme-decorator($theme-name, true); | |
} | |
@if $include-css-variables { | |
@include __theme-decorator($prop-theme, false); | |
} | |
} | |
} | |
// if first request, initialize document root with theme variables | |
$__theme-initialized: false !default; | |
@if not $__theme-initialized { | |
:root { | |
/* establish theme variables in the root for reference */ | |
@each $theme, $value in $__themes { | |
@include theme($theme, (local: false, decorate: false)); | |
}; | |
// establish default theme as top-level local theme | |
@include theme(default); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment