// @category utilities/styles // throw an error if archetype/util has not been registered $a-blackhole: require-archetype-modules(archetype/util); @function check-for-custom-styler($property, $value, $method: pre) { @return mixin-exists('custom-output-styler') and function-exits('has-custom-output-styler') and has-custom-output-styler($property, $value, $method); } // output a property or augment it to use a mixin // @mixin output-style // @param $property {String} the CSS property to output // @param $value {*} the CSS value to output @mixin output-style($property, $value: null) { @if type-of($property) == map { @include to-styles($property); } @else { $property: -archetype-normalize-property($property); @if( not is-null($property) and length(-archetype-list($value)) > 0 and not is-null(first-value-of(-archetype-list($value))) and not index($CONFIG_DISABLED_CSS, $property)) { // if the value is the special `-archetype-no-value`, pass along an empty value @if $value == -archetype-no-value { $value: (); } // for mixins, we need to do some custom work // check to see if we have a custom output styler (pre) @if check-for-custom-styler($property, $value, pre) { @include custom-output-styler($property, $value, $method: pre); } // make a dynamic function call @else if($property == function) { @if type-of($value) == string { $value: -archetype-list($value); } @if function-exists(nth($value, 1)) { $fn: nth($value, 1); $args: if(length($value) < 2, (), nth($value, 2)); @include to-styles( call($fn, $args...) ); } } // we're going to assume that any mixins that get invoked here have been properly imported // if not, they will simply throw a compilation error // check that we aren't already inside of these mixins before calling them @else if($property == font-family and not -archetype-within-mixin(font-family)) { @include font-family($value); } @else if($property == font-style and not -archetype-within-mixin(font-style)) { @include font-style($value); } // target-browser @else if($property == target-browser) { @include target-browser($value...); } // border-radius @else if($property == border-radius) { @include border-radius($value...); } // box-sizing @else if($property == box-sizing) { @include box-sizing($value...); } // box-shadow @else if($property == box-shadow) { @include box-shadow($value...); } // text-shadow @else if($property == text-shadow) { @include text-shadow($value); } // filter-gradient @else if($property == filter-gradient) { @include filter-gradient($value...); } // background @else if($property == background) { @include background($value...); } // background-image @else if($property == background-image) { @include background-image($value...); } // background-clip @else if($property == background-clip) { @include background-clip($value...); } // background-size @else if($property == background-size) { @include background-size($value...); } // background-origin @else if($property == background-origin) { @include background-origin($value...); } // IE filters @else if($property == ie-filter) { @include ie-filter($value...); } // hide-text @else if($property == hide-text) { @include hide-text($value...); } // inline-block @else if($property == inline-block) { @include inline-block($value...); } // opacity @else if($property == opacity) { @include opacity($value...); } // min-width @else if($property == min-width) { @include min-width($value...); } // min-height @else if($property == min-height) { @include min-height($value...); } // max-width @else if($property == max-width) { @include max-width($value...); } // max-height @else if($property == max-height) { @include max-height($value...); } // stretch @else if($property == stretch) { @include stretch($value...); } // appearance @else if($property == appearance) { @include appearance($value...); } // hide-element @else if($property == hide-element) { @include hide-element($value...); } // unhide-element @else if($property == unhide-element) { @include unhide-element($value...); } // unstyled-button @else if($property == unstyled-button) { @include unstyled-button($value...); } // ellipsis @else if($property == ellipsis) { @include ellipsis($value...); } // scale @else if($property == scale) { @include scale($value...); } // z-index @else if($property == z-index) { @include z-index($value...); } // glyphs @else if($property == glyph-icon) { @include glyph-icon($value...); } @else if($property == glyph-stroke) { @include glyph-stroke($value...); } // animations @else if($property == animation) { @include animation($value...); } // hasLayout @else if($property == has-layout) { @include has-layout($value...); } // IE :before/:after @else if($property == ie-pseudo) { @include ie-pseudo($value...); } @else if($property == ie-pseudo-before) { @include ie-pseudo-before($value...); } @else if($property == ie-pseudo-after) { @include ie-pseudo-after($value...); } // transparent-focusable @else if($property == transparent-focusable) { @include transparent-focusable($value); } // triangle @else if($property == triangle) { @include triangle($value...); } // styleguide-diff @else if($property == styleguide-diff) { @include styleguide-diff($value...); } // clearfix @else if($property == clearfix) { @if($value == legacy-pie-clearfix) { @include legacy-pie-clearfix; } @else if($value == pie-clearfix) { @include pie-clearfix; } @else { @include clearfix($value...); } } // special case for @extend @else if($property == extend or $property == '@extend') { @each $v in -archetype-list($value) { @extend #{$v}; } } // special case for @media @else if($property == media or $property == '@media') { @each $query, $styles in $value { @media #{$query} { @include to-styles($styles); } } } // special case for @keyframes @else if($property == keyframes or $property == '@keyframes') { @each $name, $def in $value { @each $frame, $styles in $def { @include keyframes($name) { @at-root #{$frame} { @include to-styles($styles); } } } } } // breakpoint @else if($property == breakpoint) { @each $key, $styles in $value { @include breakpoint($key) { @include to-styles($styles); } } } // high-resolution @else if($property == high-resolution) { @include high-resolution { @include to-styles($value); } } // switch-locale @else if($property == switch-locale) { @each $locale, $styles in $value { @include switch-locale($locale) { @include to-styles($styles); } } } // check to see if we have a custom output styler (post) @else if check-for-custom-styler($property, $value, post) { @include custom-output-styler($property, $value, $method: post); } // otherwise just use a key-value pair @else { // strip off the special `{raw}` keyword #{str-replace($property, '{raw}', '')}: $value; } } } } // outputs @content into a placeholder, and then extends it, // if the placeholder already exists, just extend it // @mixin archetype-smart-content // @param $placeholder {String} the placeholder // @param $extend {Boolean} if false, will only create the placeholder, but not extend it // @param $here {Boolean} if true, will force the content to be output in the current context, and not extended // @content @mixin archetype-smart-content($placeholder, $extend: true, $here: null) { @if $CONFIG_USE_SMART_CONTENT and not $here { // if we haven't already registered this placeholder... @if not index($archetype-placeholder-registry, $placeholder) { // create it... @at-root #{$placeholder} { @content; } // and register it... $archetype-placeholder-registry: append($archetype-placeholder-registry, $placeholder) !global; } @if $extend { @extend #{$placeholder} !optional; } } @elseif $extend { @content; } } // convert a list of key-value pairs into CSS rules // @mixin to-styles // @param $map {Map} a of styles // @param $states {Boolean|List} if true, output all states; if false, output no states; if a list, output only the states in the list // @param $exclude {List} a list of styles to exclude from output // @param $here {Boolean} if true, will force the content to be output in the current context, and not extended // @content @mixin to-styles($map: (), $states: true, $selectors: true, $exclude: (), $meta: (), $here: null) { @if $map { $exclude: -archetype-list($exclude); $placeholder: ( map: $map, states: $states, selectors: $selectors, exclude: $exclude ); $message: meta-message($map, map-merge($meta, (phase: begin, origin: $archetype-origin-function or styles))); @if $message { /* #{$message} */ } $placeholder: unquote("%#{tokenize($placeholder)}"); // register a placeholder if needed @include archetype-smart-content($placeholder, $here: $here) { @each $property, $value in $map { @include with-each-value($value) { // get the expected value given any runtime locale context $value: get-runtime-locale-value($archetype-current-value); @if not (index($exclude, $property) or $property == '-archetype-meta') { // deal with the states @if($property == states and $states != false and length($value) > 0) { @each $state, $val in $value { @if(index(-archetype-list($states), $state) or $states == true) { // get the state selector mappings $selector-map: _getStateSelectorMap($state); // output the state selectors if we're not using _only_ BEM style @if($CONFIG_STATE_BEM != only) { $selector: map-get($selector-map, selector); // if the selector is valid... @if($selector != null) { // output it #{$selector} { @include to-styles($val, true, true, $exclude); } } } // if BEM style states are enabled... @if($CONFIG_STATE_BEM) { // get the BEM states $bem-states: map-get($selector-map, states); // for each BEM state @each $bem-state in $bem-states { // output it (this looks scarier than it really is...) @include bem($modifier: $bem-state) { @include to-styles($val, true, true, $exclude); } } } } } } // deal with the nested selectors @else if($property == selectors and $selectors != false and length($value) > 0) { @each $selector, $val in $value { @if(index(-archetype-list($selectors), $selector) or $selectors == true) { $sel: _getSelectorSelector($selector); @if str-index($sel, '%bem ') == 1 { $sel: str-replace($sel, '%bem ', ''); @include bem($element: $sel) { @include to-styles($val, true, true, $exclude); } } @else { #{$sel} { @include to-styles($val, true, true, $exclude); } } } } } // otherwise, just output it @else if($selectors == true and $states == true ) { @include output-style($property, $value); } } } } } $message: meta-message($map, map-merge($meta, (phase: end, origin: $archetype-origin-function or styles))); @if $message { /* #{$message} */ } // any original block content passed in... // if we're using smart content... @if $CONFIG_USE_SMART_CONTENT and not $here { // intentionally break out of the context & { @content; } } // otherwise... @else { // just put it here @content; } } } // helper function to compute the selector for various states // @function _getStateSelectorMap // @private // @param $state {String} the state to select // @param $prefix {String} the prefix to append to the state (for class names only) @function _getStateSelectorMap($state, $prefix: $CONFIG_STATE_PREFIX) { $map: map-get($CONFIG_STATE_MAPPINGS, $state); @if $map == null or length($map) == 0 { $map: ( selector : '&.#{if(index(-archetype-list($CONFIG_STATE_NO_PREFIX), $state), $prefix, "")}#{$state}', states : ($state) ); } @return $map; } @function _getSelectorSelector($selector) { $selector: first-value-of(-archetype-list($selector)); @if $selector == $CORE_GLYPH_SELECTOR_VAR { $selector: $archetype-glyph-selector; } @elseif $selector == $CORE_GLYPH_SELECTOR_VAR { $selector: $archetype-pseudo-selector; } @return $selector; } // mixin for outputting filters in legacy IE // @mixin ie-filter // @param $value {String|List} a set of filters to output @mixin ie-filter($value, $use-prefix: false) { $out: false; $value: -archetype-list($value); $ms-filter-prefix: 'progid:DXImageTransform.Microsoft.'; @each $item in $value { @if($item != none and str-index($item, $ms-filter-prefix) != 1) { $item: #{$ms-filter-prefix}#{$item}; } $out: if($out, '#{$out},#{$item}', '#{$item}'); } @if($out) { @if $use-prefix != only { filter: unquote($out); } @if $use-prefix { -ms-filter: quote($out); } } } // mixin for generating a pseudo-element // @mixin pseudo-element // @param $width {String} width (default 0 for positioning in FF3) // @param $height {String} height // @param $content {String} content (this should be quoted) // @param $display {String} display [block|inline|etc] // @param $position {String} positioning [absolute|relative|etc] @mixin pseudo-element($width: 0, $height: auto, $content: $CONTENT_PLACEHOLDER, $display: block, $position: false) { @include if-set(content, quote($content)); @include if-set(display, $display); @include if-set(width, $width); @include if-set(height, $height); @include if-set(position, $position); } // abstract common issues and generate safe styles @mixin safe($property: null, $value: null) { @if not is-null($property) and not is-null($value) { // os friendly font stacks @if($property == font-family) { $stack: map-get($CONFIG_SAFE_FONTS, $value); @if($stack and length($stack) > 0) { // first, step through the list of os safe fonts @each $collection in $stack { @if(length($collection) > 1) { $default: null; $os: nth($collection, 1); $fonts: nth($collection, 2); $family: null; // next, step through the list of lang safe fonts @each $item in $fonts { @if not is-null($item) { $lang: nth($item, 1); $font: nth($item, 2); // check if the language matches or it's the default @if($lang == default or lang($lang)) { $family: $font; } // if it's the default, keep track of it @if($lang == default) { $default: $font; } // if the keyword `default` is in the font-stack, substitute it with the default font-stack $family: list-replace($family, index(-archetype-list($family), default), $default, comma); } } @include target-os($os, font-family, $family); } } $value: null; } } // text-transform: uppercase isn't friendly to turkish @else if($property == text-transform and $value == uppercase and lang(tr_TR)) { // TODO - more investigation, this may affect other locales as well // @link http://www.w3.org/International/tests/html-css/text-transform/results-text-transform // @link https://developer.mozilla.org/en/CSS/text-transform $value: null; } @else if($property == font-variant and $value == small-caps and lang(tr_TR)) { // TODO - more investigation, this may affect other locales as well // @link https://developer.mozilla.org/en/CSS/font-variant $value: null; } // italics aren't pretty in CJK @else if($property == font-style and $value == italic and lang(CJK)) { $value: null; } // if $value was set to `null`, this does nothing @include output-style($property, $value); } } // web-safe font stacks, if no safe fonts are found, use the raw `$family` as output // @mixin font-family // @param $family {String} the font stack to output @mixin font-family($family: null) { @include safe(font-family, $family); } // safely use text-transform:uppercase without localization issues // @mixin uppercase @mixin uppercase { @include safe(text-transform, uppercase); } // safely use font-variant:small-caps without localization issues // @mixin small-caps @mixin small-caps { @include safe(font-variant, small-caps); } // safely use font-style without localization issues // @mixin font-style // @param $style {String} the style @mixin font-style($style: null) { @include safe(font-style, $style); } // a method for maintaining z-index order // @mixin z-index // @param $value {Number} the z-index offset // @param $layer {String|Number} the initial z-index layer to start from @mixin z-index($value: 0, $layer: 0) { @if(type-of($layer) == string) { $layer: index($CONFIG_Z_LAYERS, $layer); $layer: if($layer, $layer - 1, 0) * $CONFIG_Z_LAYERS_OFFSET; } z-index: ($value + $layer); }