styled-jsx
Version:
Full, scoped and component-friendly CSS support for JSX (SSR+browser)
216 lines (187 loc) • 8.18 kB
JavaScript
// based on Stylis (MIT)
// modified by Guillermo Rauch to add support for custom
// attributes instead of custom selector prefix
/*!
*
* __ ___
* _____/ /___ __/ (_)____
* / ___/ __/ / / / / / ___/
* (__ ) /_/ /_/ / / (__ )
* /____/\__/\__, /_/_/____/
* /____/
*
* stylis is a small css compiler
*
* @licence MIT
*/
(function (factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory(global);
} else if (typeof define === 'function' && define.amd) {
define(factory(window));
} else {
window.stylis = factory(window);
}
}(function (window) {
'use strict';
// regular expressions
var regPrefixKey = /@(keyframes +.*?}$)/g;
var regPrefix = /((?:transform|appearance):.*?;)/g;
var regSpaces = / +/g;
var regAnimation = /(,|:) +/g;
/**
* css compiler
*
* @example compiler('.class1', 'css...', false);
*
* @param {string} id to use for data attributes
* @param {string} styles
* @return {string}
*/
function stylis (id, styles) {
var suffix = '[data-jsx=' + id +']';
var output = '';
var line = '';
var len = styles.length;
var i = 0;
// parse + compile
while (i < len) {
var code = styles.charCodeAt(i);
// {, }, ; characters
if (code === 123 || code === 125 || code === 59) {
line += styles[i];
var first = line.charCodeAt(0);
// only trim when the first character is ` `
if (first === 32) {
first = (line = line.trim()).charCodeAt(0);
}
// / character, line comment
if (first === 47) {
line = code === 125 ? '}' : '';
}
// @ character, special block
else if (first === 64) {
var second = line.charCodeAt(1) || 0;
// @keyframe/@root, `k` or @root, `r` character
if (second === 107 || second === 114) {
i++;
if (second == 107) {
// @keyframes
line = line.substring(1, 11) + id + line.substring(11);
} else {
// @root
line = '';
}
var close = 0;
while (i < len) {
var char = styles[i++];
var _code = char.charCodeAt(0);
// not `\t`, `\r`, `\n` characters
if (_code !== 9 && _code !== 13 && _code !== 10) {
// } character
if (_code === 125) {
// previous block tag is close
if (close === 1) {
break;
}
// current block tag is close tag
else {
close = 1;
}
}
// { character
else if (_code === 123) {
// current block tag is open
close = 0;
}
line += char;
}
}
// vendor prefix transform properties within keyframes and @root blocks
line = line.replace(regSpaces, '').replace(regPrefix, '-webkit-$1-moz-$1-ms-$1$1');
if (second === 107) {
// vendor prefix keyframes blocks
line = '@-webkit-'+line+'}'+'@-moz-'+line+'}@'+line+'}';
} else {
// vendor prefix keyframes in @root block
line = line.replace(regPrefixKey, '@-webkit-$1}@-moz-$1}@$1}');
}
}
} else {
var second = line.charCodeAt(1) || 0;
var third = line.charCodeAt(2) || 0;
// animation: a, n, i characters
if (first === 97 && second === 110 && third === 105) {
// remove space after `,` and `:` then split line
var split = line.replace(regAnimation, '$1').split(':');
// build line
line = split[0] + ':' + id + (split[1].split(',')).join(','+id);
// vendor prefix
line = '-webkit-' + line + '-moz-' + line + line;
}
// appearance: a, p, p
// flex: f, l, e
// order: o, r, d
else if (
(first === 97 && second === 112 && third === 112) ||
(first === 102 && second === 108 && third === 101) ||
(first === 111 && second === 114 && third === 100)
) {
// vendor prefix
line = '-webkit-' + line + '-moz-' + line + line;
}
// transforms & transitions: t, r, a
// hyphens: h, y, p
// user-select: u, s, r, s
else if (
(first === 116 && second === 114 && third === 97) ||
(first === 104 && second === 121 && third === 112) ||
(first === 117 && second === 115 && third === 101 && line.charCodeAt(5) === 115)
) {
// vendor prefix
line = '-webkit-' + line + '-moz-' + line + '-ms-' + line + line;
}
// display: d, i, s
else if (first === 100 && second === 105 && third === 115) {
if (line.indexOf('flex') > -1) {
// vendor prefix
line = 'display:-webkit-flex; display:flex;'
}
}
else {
// selector declaration
if (code === 123) {
var split = line.split(',');
var _line = '';
// iterate through characters and prefix multiple selectors with namesapces
// i.e h1, h2, h3 --> [namespace] h1, [namespace] h1, ....
for (var j = 0, length = split.length; j < length; j++) {
var selector = split[j];
var _last = selector[selector.length - 1]
var affix = '';
if (_last === '{') {
_line += selector
.substring(0, selector.length - 1)
.trim() + suffix + _last;
} else {
_line += selector.trim() + suffix + ',';
}
}
line = _line;
}
}
}
output += line;
line = '';
}
// not `\t`, `\r`, `\n` characters
else if (code !== 9 && code !== 13 && code !== 10) {
line += styles[i];
}
// next character
i++;
}
return output;
}
return stylis;
}));