diff --git a/.npmignore b/.npmignore
index 35d450777..b62fff18b 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,14 +1,3 @@
-.DS_Store
-.prettierrc
-.nyc_output
-.travis.yml
-coverage
-coverage.lcov
-bench
-docs
-src
-examples
-babel.config.js
-test
-CONTRIBUTING.md
-CODE_OF_CONDUCT.md
+/*
+!/dist/**/*.js
+!/props.js
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
index b98087bf5..ad4605be9 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -1,29 +1,16 @@
-module.exports = {
- env: {
- test: {
- presets: [
- [ '@babel/env', { loose: true } ],
- '@babel/react'
- ],
- plugins: [
- '@babel/transform-runtime'
- ]
- },
- cjs: {
- presets: [
- [ '@babel/env', { loose: true } ]
- ],
- plugins: [
- '@babel/transform-runtime'
- ]
- },
- esm: {
- presets: [
- [ '@babel/env', { loose: true, modules: false } ]
- ],
- plugins: [
- ['@babel/transform-runtime', { useESModules: true }]
- ]
- },
+module.exports = function getConfig(api) {
+ const isRollup = api.caller(
+ caller => caller && caller.name === 'rollup-plugin-babel'
+ )
+ if (!isRollup)
+ return {
+ presets: [['@babel/preset-env', { loose: true }], '@babel/react'],
+ }
+ return {
+ presets: [['@babel/preset-env', { loose: true, modules: false }]],
+ plugins: [
+ 'babel-plugin-annotate-pure-calls',
+ ['@babel/transform-runtime', { useESModules: true }],
+ ],
}
}
diff --git a/benchmarks/index.js b/benchmarks/index.js
new file mode 100644
index 000000000..f46ec7875
--- /dev/null
+++ b/benchmarks/index.js
@@ -0,0 +1,40 @@
+/* eslint-disable no-console, import/no-unresolved */
+const Benchmark = require('benchmark')
+
+const libs = [
+ {
+ name: 'actual',
+ module: require('../dist/styled-system.cjs'),
+ },
+ {
+ name: 'v4',
+ module: require('./v4.1.0'),
+ },
+].map(({ name, module: { compose, fontSize, space } }) => {
+ const system = compose(
+ fontSize,
+ space
+ )
+ const run = () => system({ p: [10, 20], mt: 10, m: '20px', fontSize: 10 })
+ return { name, run }
+})
+
+const suite = new Benchmark.Suite()
+
+console.log('Initial run...')
+
+libs.forEach(lib => {
+ console.log(lib.name, lib.run())
+ suite.add(lib.name, lib.run)
+})
+
+console.log('Run suite...')
+
+suite
+ .on('cycle', event => {
+ console.log(String(event.target))
+ })
+ .on('complete', function onComplete() {
+ console.log(`Fastest is ${this.filter('fastest').map('name')}`)
+ })
+ .run({ async: true })
diff --git a/benchmarks/v4.1.0.js b/benchmarks/v4.1.0.js
new file mode 100644
index 000000000..114f91cea
--- /dev/null
+++ b/benchmarks/v4.1.0.js
@@ -0,0 +1,688 @@
+"use strict";
+
+var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
+
+exports.__esModule = true;
+exports.colorStyle = exports.textStyle = exports.buttonStyle = exports.left = exports.bottom = exports.right = exports.top = exports.zIndex = exports.position = exports.backgroundRepeat = exports.backgroundPosition = exports.backgroundSize = exports.backgroundImage = exports.background = exports.overflow = exports.opacity = exports.boxShadow = exports.borders = exports.borderRadius = exports.borderLeft = exports.borderBottom = exports.borderRight = exports.borderTop = exports.borderColor = exports.borderStyle = exports.borderWidth = exports.border = exports.gridArea = exports.gridTemplateAreas = exports.gridTemplateRows = exports.gridTemplateColumns = exports.gridAutoRows = exports.gridAutoColumns = exports.gridAutoFlow = exports.gridRow = exports.gridColumn = exports.gridRowGap = exports.gridColumnGap = exports.gridGap = exports.order = exports.alignSelf = exports.justifySelf = exports.flex = exports.flexDirection = exports.flexBasis = exports.flexWrap = exports.justifyContent = exports.justifyItems = exports.alignContent = exports.alignItems = exports.verticalAlign = exports.size = exports.minHeight = exports.maxHeight = exports.height = exports.minWidth = exports.maxWidth = exports.display = exports.letterSpacing = exports.fontStyle = exports.textAlign = exports.lineHeight = exports.fontWeight = exports.fontFamily = exports.fontSize = exports.getPx = exports.width = exports.getWidth = exports.color = exports.backgroundColor = exports.textColor = exports.space = exports.paddingRight = exports.paddingLeft = exports.paddingBottom = exports.paddingTop = exports.padding = exports.marginRight = exports.marginLeft = exports.marginBottom = exports.marginTop = exports.margin = exports.variant = exports.mapProps = exports.compose = exports.style = exports.createMediaQuery = exports.px = exports.num = exports.isObject = exports.is = exports.themeGet = exports.get = exports.cloneFunction = exports.propType = exports.defaultBreakpoints = void 0;
+
+var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
+
+var _propTypes = _interopRequireDefault(require("prop-types"));
+
+var defaultBreakpoints = [40, 52, 64].map(function (n) {
+ return n + 'em';
+});
+exports.defaultBreakpoints = defaultBreakpoints;
+
+var propType = _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string, _propTypes.default.array, _propTypes.default.object]);
+
+exports.propType = propType;
+
+var cloneFunction = function cloneFunction(fn) {
+ return function () {
+ return fn.apply(void 0, arguments);
+ };
+};
+
+exports.cloneFunction = cloneFunction;
+
+var get = function get(obj) {
+ for (var _len = arguments.length, paths = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ paths[_key - 1] = arguments[_key];
+ }
+
+ var value = paths.reduce(function (a, path) {
+ if (is(a)) return a;
+ var keys = typeof path === 'string' ? path.split('.') : [path];
+ return keys.reduce(function (a, key) {
+ return a && is(a[key]) ? a[key] : null;
+ }, obj);
+ }, null);
+ return is(value) ? value : paths[paths.length - 1];
+};
+
+exports.get = get;
+
+var themeGet = function themeGet(path, fallback) {
+ if (fallback === void 0) {
+ fallback = null;
+ }
+
+ return function (props) {
+ return get(props.theme, path, fallback);
+ };
+};
+
+exports.themeGet = themeGet;
+
+var is = function is(n) {
+ return n !== undefined && n !== null;
+};
+
+exports.is = is;
+
+var isObject = function isObject(n) {
+ return typeof n === 'object' && n !== null;
+};
+
+exports.isObject = isObject;
+
+var num = function num(n) {
+ return typeof n === 'number' && !isNaN(n);
+};
+
+exports.num = num;
+
+var px = function px(n) {
+ return num(n) && n !== 0 ? n + 'px' : n;
+};
+
+exports.px = px;
+
+var createMediaQuery = function createMediaQuery(n) {
+ return "@media screen and (min-width: " + px(n) + ")";
+};
+
+exports.createMediaQuery = createMediaQuery;
+
+var getValue = function getValue(n, scale) {
+ return get(scale, n);
+};
+
+var style = function style(_ref) {
+ var _func$propTypes;
+
+ var prop = _ref.prop,
+ cssProperty = _ref.cssProperty,
+ alias = _ref.alias,
+ key = _ref.key,
+ _ref$transformValue = _ref.transformValue,
+ transformValue = _ref$transformValue === void 0 ? getValue : _ref$transformValue,
+ _ref$scale = _ref.scale,
+ defaultScale = _ref$scale === void 0 ? {} : _ref$scale;
+ var property = cssProperty || prop;
+
+ var func = function func(props) {
+ var value = get(props, prop, alias, null);
+ if (!is(value)) return null;
+ var scale = get(props.theme, key, defaultScale);
+
+ var createStyle = function createStyle(n) {
+ var _ref2;
+
+ return is(n) ? (_ref2 = {}, _ref2[property] = transformValue(n, scale), _ref2) : null;
+ };
+
+ if (!isObject(value)) return createStyle(value);
+ var breakpoints = get(props.theme, 'breakpoints', defaultBreakpoints);
+ var styles = [];
+
+ if (Array.isArray(value)) {
+ styles.push(createStyle(value[0]));
+
+ for (var i = 1; i < value.slice(0, breakpoints.length + 1).length; i++) {
+ var rule = createStyle(value[i]);
+
+ if (rule) {
+ var _styles$push;
+
+ var media = createMediaQuery(breakpoints[i - 1]);
+ styles.push((_styles$push = {}, _styles$push[media] = rule, _styles$push));
+ }
+ }
+ } else {
+ for (var _key2 in value) {
+ var breakpoint = breakpoints[_key2];
+
+ var _media = createMediaQuery(breakpoint);
+
+ var _rule = createStyle(value[_key2]);
+
+ if (!breakpoint) {
+ styles.unshift(_rule);
+ } else {
+ var _styles$push2;
+
+ styles.push((_styles$push2 = {}, _styles$push2[_media] = _rule, _styles$push2));
+ }
+ }
+
+ styles.sort();
+ }
+
+ return styles;
+ };
+
+ func.propTypes = (_func$propTypes = {}, _func$propTypes[prop] = cloneFunction(propType), _func$propTypes);
+ func.propTypes[prop].meta = {
+ prop: prop,
+ themeKey: key
+ };
+
+ if (alias) {
+ func.propTypes[alias] = cloneFunction(propType);
+ func.propTypes[alias].meta = {
+ prop: alias,
+ themeKey: key
+ };
+ }
+
+ return func;
+};
+
+exports.style = style;
+
+var compose = function compose() {
+ for (var _len2 = arguments.length, funcs = new Array(_len2), _key3 = 0; _key3 < _len2; _key3++) {
+ funcs[_key3] = arguments[_key3];
+ }
+
+ var func = function func(props) {
+ var n = funcs.map(function (fn) {
+ return fn(props);
+ }).filter(Boolean);
+ return n;
+ };
+
+ func.propTypes = {};
+ funcs.forEach(function (fn) {
+ func.propTypes = (0, _extends2.default)({}, func.propTypes, fn.propTypes);
+ });
+ return func;
+};
+
+exports.compose = compose;
+
+var mapProps = function mapProps(mapper) {
+ return function (func) {
+ var next = function next(props) {
+ return func(mapper(props));
+ };
+
+ for (var key in func) {
+ next[key] = func[key];
+ }
+
+ return next;
+ };
+};
+
+exports.mapProps = mapProps;
+
+var variant = function variant(_ref3) {
+ var _fn$propTypes;
+
+ var key = _ref3.key,
+ _ref3$prop = _ref3.prop,
+ prop = _ref3$prop === void 0 ? 'variant' : _ref3$prop;
+
+ var fn = function fn(props) {
+ return get(props.theme, [key, props[prop]].join('.'), null);
+ };
+
+ fn.propTypes = (_fn$propTypes = {}, _fn$propTypes[prop] = _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), _fn$propTypes);
+ return fn;
+}; // space
+
+
+exports.variant = variant;
+var spaceScale = [0, 4, 8, 16, 32, 64, 128, 256, 512];
+
+var getSpace = function getSpace(n, scale) {
+ if (!num(n)) {
+ return px(get(scale, n, n));
+ }
+
+ var isNegative = n < 0;
+ var absolute = Math.abs(n);
+ var value = get(scale, absolute);
+
+ if (!num(value)) {
+ return isNegative ? '-' + value : value;
+ }
+
+ return px(value * (isNegative ? -1 : 1));
+};
+
+var margin = style({
+ prop: 'margin',
+ alias: 'm',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.margin = margin;
+var marginTop = style({
+ prop: 'marginTop',
+ alias: 'mt',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.marginTop = marginTop;
+var marginBottom = style({
+ prop: 'marginBottom',
+ alias: 'mb',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.marginBottom = marginBottom;
+var marginLeft = style({
+ prop: 'marginLeft',
+ alias: 'ml',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.marginLeft = marginLeft;
+var marginRight = style({
+ prop: 'marginRight',
+ alias: 'mr',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.marginRight = marginRight;
+var padding = style({
+ prop: 'padding',
+ alias: 'p',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.padding = padding;
+var paddingTop = style({
+ prop: 'paddingTop',
+ alias: 'pt',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.paddingTop = paddingTop;
+var paddingBottom = style({
+ prop: 'paddingBottom',
+ alias: 'pb',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.paddingBottom = paddingBottom;
+var paddingLeft = style({
+ prop: 'paddingLeft',
+ alias: 'pl',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.paddingLeft = paddingLeft;
+var paddingRight = style({
+ prop: 'paddingRight',
+ alias: 'pr',
+ key: 'space',
+ transformValue: getSpace,
+ scale: spaceScale
+});
+exports.paddingRight = paddingRight;
+var space = mapProps(function (props) {
+ return (0, _extends2.default)({}, props, {
+ mt: is(props.my) ? props.my : props.mt,
+ mb: is(props.my) ? props.my : props.mb,
+ ml: is(props.mx) ? props.mx : props.ml,
+ mr: is(props.mx) ? props.mx : props.mr,
+ pt: is(props.py) ? props.py : props.pt,
+ pb: is(props.py) ? props.py : props.pb,
+ pl: is(props.px) ? props.px : props.pl,
+ pr: is(props.px) ? props.px : props.pr
+ });
+})(compose(margin, marginTop, marginBottom, marginLeft, marginRight, padding, paddingTop, paddingBottom, paddingLeft, paddingRight)); // color
+
+exports.space = space;
+var textColor = style({
+ prop: 'color',
+ key: 'colors'
+});
+exports.textColor = textColor;
+var backgroundColor = style({
+ prop: 'backgroundColor',
+ alias: 'bg',
+ key: 'colors'
+});
+exports.backgroundColor = backgroundColor;
+var color = compose(textColor, backgroundColor); // width
+
+exports.color = color;
+
+var getWidth = function getWidth(n, scale) {
+ return !num(n) || n > 1 ? px(n) : n * 100 + '%';
+};
+
+exports.getWidth = getWidth;
+var width = style({
+ prop: 'width',
+ key: 'widths',
+ transformValue: getWidth
+}); // typography
+
+exports.width = width;
+
+var getPx = function getPx(n, scale) {
+ return px(get(scale, n));
+};
+
+exports.getPx = getPx;
+var fontSize = style({
+ prop: 'fontSize',
+ key: 'fontSizes',
+ transformValue: getPx,
+ scale: [12, 14, 16, 20, 24, 32, 48, 64, 72]
+});
+exports.fontSize = fontSize;
+var fontFamily = style({
+ prop: 'fontFamily',
+ key: 'fonts'
+});
+exports.fontFamily = fontFamily;
+var fontWeight = style({
+ prop: 'fontWeight',
+ key: 'fontWeights'
+});
+exports.fontWeight = fontWeight;
+var lineHeight = style({
+ prop: 'lineHeight',
+ key: 'lineHeights'
+});
+exports.lineHeight = lineHeight;
+var textAlign = style({
+ prop: 'textAlign'
+});
+exports.textAlign = textAlign;
+var fontStyle = style({
+ prop: 'fontStyle'
+});
+exports.fontStyle = fontStyle;
+var letterSpacing = style({
+ prop: 'letterSpacing',
+ key: 'letterSpacings',
+ transformValue: getPx
+}); // layout
+
+exports.letterSpacing = letterSpacing;
+var display = style({
+ prop: 'display'
+});
+exports.display = display;
+var maxWidth = style({
+ prop: 'maxWidth',
+ key: 'maxWidths',
+ transformValue: getPx
+});
+exports.maxWidth = maxWidth;
+var minWidth = style({
+ prop: 'minWidth',
+ key: 'minWidths',
+ transformValue: getPx
+});
+exports.minWidth = minWidth;
+var height = style({
+ prop: 'height',
+ key: 'heights',
+ transformValue: getPx
+});
+exports.height = height;
+var maxHeight = style({
+ prop: 'maxHeight',
+ key: 'maxHeights',
+ transformValue: getPx
+});
+exports.maxHeight = maxHeight;
+var minHeight = style({
+ prop: 'minHeight',
+ key: 'minHeights',
+ transformValue: getPx
+});
+exports.minHeight = minHeight;
+var size = mapProps(function (props) {
+ return (0, _extends2.default)({}, props, {
+ width: props.size,
+ height: props.size
+ });
+})(compose(width, height));
+exports.size = size;
+var verticalAlign = style({
+ prop: 'verticalAlign'
+}); // flexbox
+
+exports.verticalAlign = verticalAlign;
+var alignItems = style({
+ prop: 'alignItems'
+});
+exports.alignItems = alignItems;
+var alignContent = style({
+ prop: 'alignContent'
+});
+exports.alignContent = alignContent;
+var justifyItems = style({
+ prop: 'justifyItems'
+});
+exports.justifyItems = justifyItems;
+var justifyContent = style({
+ prop: 'justifyContent'
+});
+exports.justifyContent = justifyContent;
+var flexWrap = style({
+ prop: 'flexWrap'
+});
+exports.flexWrap = flexWrap;
+var flexBasis = style({
+ prop: 'flexBasis',
+ transformValue: getWidth
+});
+exports.flexBasis = flexBasis;
+var flexDirection = style({
+ prop: 'flexDirection'
+});
+exports.flexDirection = flexDirection;
+var flex = style({
+ prop: 'flex'
+});
+exports.flex = flex;
+var justifySelf = style({
+ prop: 'justifySelf'
+});
+exports.justifySelf = justifySelf;
+var alignSelf = style({
+ prop: 'alignSelf'
+});
+exports.alignSelf = alignSelf;
+var order = style({
+ prop: 'order'
+}); // grid
+
+exports.order = order;
+var gridGap = style({
+ prop: 'gridGap',
+ key: 'space',
+ transformValue: getPx,
+ scale: spaceScale
+});
+exports.gridGap = gridGap;
+var gridColumnGap = style({
+ prop: 'gridColumnGap',
+ key: 'space',
+ transformValue: getPx,
+ scale: spaceScale
+});
+exports.gridColumnGap = gridColumnGap;
+var gridRowGap = style({
+ prop: 'gridRowGap',
+ key: 'space',
+ transformValue: getPx,
+ scale: spaceScale
+});
+exports.gridRowGap = gridRowGap;
+var gridColumn = style({
+ prop: 'gridColumn'
+});
+exports.gridColumn = gridColumn;
+var gridRow = style({
+ prop: 'gridRow'
+});
+exports.gridRow = gridRow;
+var gridAutoFlow = style({
+ prop: 'gridAutoFlow'
+});
+exports.gridAutoFlow = gridAutoFlow;
+var gridAutoColumns = style({
+ prop: 'gridAutoColumns'
+});
+exports.gridAutoColumns = gridAutoColumns;
+var gridAutoRows = style({
+ prop: 'gridAutoRows'
+});
+exports.gridAutoRows = gridAutoRows;
+var gridTemplateColumns = style({
+ prop: 'gridTemplateColumns'
+});
+exports.gridTemplateColumns = gridTemplateColumns;
+var gridTemplateRows = style({
+ prop: 'gridTemplateRows'
+});
+exports.gridTemplateRows = gridTemplateRows;
+var gridTemplateAreas = style({
+ prop: 'gridTemplateAreas'
+});
+exports.gridTemplateAreas = gridTemplateAreas;
+var gridArea = style({
+ prop: 'gridArea'
+}); // borders
+
+exports.gridArea = gridArea;
+var border = style({
+ prop: 'border',
+ key: 'borders'
+});
+exports.border = border;
+var borderWidth = style({
+ prop: 'borderWidth',
+ key: 'borderWidths',
+ transformValue: getPx
+});
+exports.borderWidth = borderWidth;
+var borderStyle = style({
+ prop: 'borderStyle',
+ key: 'borderStyles'
+});
+exports.borderStyle = borderStyle;
+var borderColor = style({
+ prop: 'borderColor',
+ key: 'colors'
+});
+exports.borderColor = borderColor;
+var borderTop = style({
+ prop: 'borderTop',
+ key: 'borders'
+});
+exports.borderTop = borderTop;
+var borderRight = style({
+ prop: 'borderRight',
+ key: 'borders'
+});
+exports.borderRight = borderRight;
+var borderBottom = style({
+ prop: 'borderBottom',
+ key: 'borders'
+});
+exports.borderBottom = borderBottom;
+var borderLeft = style({
+ prop: 'borderLeft',
+ key: 'borders'
+});
+exports.borderLeft = borderLeft;
+var borderRadius = style({
+ prop: 'borderRadius',
+ key: 'radii',
+ transformValue: getPx
+});
+exports.borderRadius = borderRadius;
+var borders = compose(border, borderTop, borderRight, borderBottom, borderLeft, borderWidth, borderStyle, borderColor, borderRadius);
+exports.borders = borders;
+var boxShadow = style({
+ prop: 'boxShadow',
+ key: 'shadows'
+});
+exports.boxShadow = boxShadow;
+var opacity = style({
+ prop: 'opacity'
+});
+exports.opacity = opacity;
+var overflow = style({
+ prop: 'overflow'
+}); // backgrounds
+
+exports.overflow = overflow;
+var background = style({
+ prop: 'background'
+});
+exports.background = background;
+var backgroundImage = style({
+ prop: 'backgroundImage'
+});
+exports.backgroundImage = backgroundImage;
+var backgroundSize = style({
+ prop: 'backgroundSize'
+});
+exports.backgroundSize = backgroundSize;
+var backgroundPosition = style({
+ prop: 'backgroundPosition'
+});
+exports.backgroundPosition = backgroundPosition;
+var backgroundRepeat = style({
+ prop: 'backgroundRepeat'
+}); // position
+
+exports.backgroundRepeat = backgroundRepeat;
+var position = style({
+ prop: 'position'
+});
+exports.position = position;
+var zIndex = style({
+ prop: 'zIndex',
+ key: 'zIndices'
+});
+exports.zIndex = zIndex;
+var top = style({
+ prop: 'top',
+ transformValue: getPx
+});
+exports.top = top;
+var right = style({
+ prop: 'right',
+ transformValue: getPx
+});
+exports.right = right;
+var bottom = style({
+ prop: 'bottom',
+ transformValue: getPx
+});
+exports.bottom = bottom;
+var left = style({
+ prop: 'left',
+ transformValue: getPx
+}); // variants
+
+exports.left = left;
+var buttonStyle = variant({
+ key: 'buttons'
+});
+exports.buttonStyle = buttonStyle;
+var textStyle = variant({
+ key: 'textStyles',
+ prop: 'textStyle'
+});
+exports.textStyle = textStyle;
+var colorStyle = variant({
+ key: 'colorStyles',
+ prop: 'colors'
+});
+exports.colorStyle = colorStyle;
diff --git a/package-lock.json b/package-lock.json
index 997d964ea..33c71718f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1565,6 +1565,33 @@
"any-observable": "^0.3.0"
}
},
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "11.13.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.8.tgz",
+ "integrity": "sha512-szA3x/3miL90ZJxUCzx9haNbK5/zmPieGraZEe4WI+3srN0eGLiT22NXeMHmyhNEopn+IrxqMc7wdVwvPl8meg==",
+ "dev": true
+ },
+ "@types/resolve": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
+ "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "acorn": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
+ "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
+ "dev": true
+ },
"ansi-align": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
@@ -1977,6 +2004,12 @@
}
}
},
+ "babel-plugin-annotate-pure-calls": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-annotate-pure-calls/-/babel-plugin-annotate-pure-calls-0.4.0.tgz",
+ "integrity": "sha512-oi4M/PWUJOU9ZyRGoPTfPMqdyMp06jbJAomd3RcyYuzUtBOddv98BqLm96Lucpi2QFoQHkdGQt0ACvw7VzVEQA==",
+ "dev": true
+ },
"babel-plugin-emotion": {
"version": "10.0.9",
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.9.tgz",
@@ -2099,6 +2132,16 @@
}
}
},
+ "benchmark": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
+ "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.4",
+ "platform": "^1.3.3"
+ }
+ },
"binary-extensions": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz",
@@ -2715,6 +2758,11 @@
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true
},
+ "deepmerge": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz",
+ "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow=="
+ },
"defaults": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
@@ -2935,6 +2983,12 @@
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"dev": true
},
+ "estree-walker": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.0.tgz",
+ "integrity": "sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==",
+ "dev": true
+ },
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
@@ -3224,8 +3278,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"aproba": {
"version": "1.2.0",
@@ -3246,14 +3299,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3268,20 +3319,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -3398,8 +3446,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"ini": {
"version": "1.3.5",
@@ -3411,7 +3458,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -3426,7 +3472,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -3434,14 +3479,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -3460,7 +3503,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -3541,8 +3583,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -3554,7 +3595,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -3640,8 +3680,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -3677,7 +3716,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3697,7 +3735,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -3741,14 +3778,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -4383,6 +4418,12 @@
"is-path-inside": "^1.0.0"
}
},
+ "is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
+ "dev": true
+ },
"is-npm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
@@ -4550,6 +4591,27 @@
"semver": "^5.5.0"
}
},
+ "jest-worker": {
+ "version": "24.6.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz",
+ "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==",
+ "dev": true,
+ "requires": {
+ "merge-stream": "^1.0.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"js-levenshtein": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
@@ -5048,6 +5110,15 @@
"yallist": "^2.1.2"
}
},
+ "magic-string": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz",
+ "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
"make-dir": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
@@ -5125,6 +5196,15 @@
"yargs-parser": "^10.0.0"
}
},
+ "merge-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
@@ -6808,6 +6888,12 @@
}
}
},
+ "platform": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
+ "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==",
+ "dev": true
+ },
"please-upgrade-node": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz",
@@ -7213,6 +7299,91 @@
"glob": "^7.1.3"
}
},
+ "rollup": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.10.1.tgz",
+ "integrity": "sha512-pW353tmBE7QP622ITkGxtqF0d5gSRCVPD9xqM+fcPjudeZfoXMFW2sCzsTe2TU/zU1xamIjiS9xuFCPVT9fESw==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "@types/node": "^11.13.5",
+ "acorn": "^6.1.1"
+ }
+ },
+ "rollup-plugin-babel": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.3.2.tgz",
+ "integrity": "sha512-KfnizE258L/4enADKX61ozfwGHoqYauvoofghFJBhFnpH9Sb9dNPpWg8QHOaAfVASUYV8w0mCx430i9z0LJoJg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "rollup-pluginutils": "^2.3.0"
+ }
+ },
+ "rollup-plugin-commonjs": {
+ "version": "9.3.4",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.3.4.tgz",
+ "integrity": "sha512-DTZOvRoiVIHHLFBCL4pFxOaJt8pagxsVldEXBOn6wl3/V21wVaj17HFfyzTsQUuou3sZL3lEJZVWKPFblJfI6w==",
+ "dev": true,
+ "requires": {
+ "estree-walker": "^0.6.0",
+ "magic-string": "^0.25.2",
+ "resolve": "^1.10.0",
+ "rollup-pluginutils": "^2.6.0"
+ }
+ },
+ "rollup-plugin-node-resolve": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.2.3.tgz",
+ "integrity": "sha512-r+WaesPzdGEynpLZLALFEDugA4ACa5zn7bc/+LVX4vAXQQ8IgDHv0xfsSvJ8tDXUtprfBtrDtRFg27ifKjcJTg==",
+ "dev": true,
+ "requires": {
+ "@types/resolve": "0.0.8",
+ "builtin-modules": "^3.1.0",
+ "is-module": "^1.0.0",
+ "resolve": "^1.10.0"
+ },
+ "dependencies": {
+ "builtin-modules": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
+ "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
+ "dev": true
+ }
+ }
+ },
+ "rollup-plugin-replace": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz",
+ "integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==",
+ "dev": true,
+ "requires": {
+ "magic-string": "^0.25.2",
+ "rollup-pluginutils": "^2.6.0"
+ }
+ },
+ "rollup-plugin-uglify": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.2.tgz",
+ "integrity": "sha512-qwz2Tryspn5QGtPUowq5oumKSxANKdrnfz7C0jm4lKxvRDsNe/hSGsB9FntUul7UeC4TsZEWKErVgE1qWSO0gw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "jest-worker": "^24.0.0",
+ "serialize-javascript": "^1.6.1",
+ "uglify-js": "^3.4.9"
+ }
+ },
+ "rollup-pluginutils": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
+ "integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
+ "dev": true,
+ "requires": {
+ "estree-walker": "^0.6.0",
+ "micromatch": "^3.1.10"
+ }
+ },
"run-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz",
@@ -7280,6 +7451,12 @@
"integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=",
"dev": true
},
+ "serialize-javascript": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz",
+ "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==",
+ "dev": true
+ },
"set-value": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
@@ -7521,6 +7698,12 @@
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
"dev": true
},
+ "sourcemap-codec": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz",
+ "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==",
+ "dev": true
+ },
"spdx-correct": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
@@ -7843,6 +8026,30 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
},
+ "uglify-js": {
+ "version": "3.5.9",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.9.tgz",
+ "integrity": "sha512-WpT0RqsDtAWPNJK955DEnb6xjymR8Fn0OlK4TT4pS0ASYsVPqr5ELhgwOwLCP5J5vHeJ4xmMmz3DEgdqC10JeQ==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.20.0",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
"uid2": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
diff --git a/package.json b/package.json
index df79a6e74..cb2c855b4 100644
--- a/package.json
+++ b/package.json
@@ -2,22 +2,22 @@
"name": "styled-system",
"version": "4.1.1",
"description": "Responsive, theme-based style props for building design systems with React",
- "main": "dist/index.cjs.js",
- "module": "dist/index.esm.js",
+ "main": "dist/styled-system.cjs.js",
+ "module": "dist/styled-system.es.js",
+ "jsnext:main": "dist/styled-system.es.js",
"sideEffects": false,
"scripts": {
- "prepare": "mkdirp dist && npm run build:cjs && npm run build:esm",
+ "prepare": "rollup -c",
"prettier": "prettier \"{src,test}/**/*.js\" --write",
"clean": "rm -rf dist",
- "build:cjs": "NODE_ENV=cjs babel src -o dist/index.cjs.js",
- "build:esm": "NODE_ENV=esm babel src -o dist/index.esm.js",
"logo": "npx repng docs/src/logo/index.js -d docs/static -f logo.png -w 512 -h 512",
"logo-svg": "npx scrs docs/src/logo/index.js --svg > docs/static/logo.svg",
"logo-white": "npx scrs docs/src/logo/white.js --svg > docs/static/logo-white.svg",
"size": "npx bundlesize",
"cover": "nyc report --reporter=html --reporter=lcov > coverage.lcov",
"codecov": "nyc report --reporter=html --reporter=lcov > coverage.lcov && npx codecov",
- "test": "nyc ava"
+ "test": "nyc ava",
+ "bench": "node benchmarks"
},
"keywords": [
"react",
@@ -37,12 +37,20 @@
"@emotion/core": "^10.0.9",
"@emotion/styled": "^10.0.9",
"ava": "^1.3.1",
+ "babel-plugin-annotate-pure-calls": "^0.4.0",
+ "benchmark": "^2.1.4",
"husky": "^1.3.1",
"lint-staged": "^8.1.5",
"nyc": "^13.3.0",
"prettier": "^1.16.4",
"react": "^16.8.5",
"react-test-renderer": "^16.8.5",
+ "rollup": "^1.10.1",
+ "rollup-plugin-babel": "^4.3.2",
+ "rollup-plugin-commonjs": "^9.3.4",
+ "rollup-plugin-node-resolve": "^4.2.3",
+ "rollup-plugin-replace": "^2.2.0",
+ "rollup-plugin-uglify": "^6.0.2",
"styled-components": "^4.2.0"
},
"ava": {
@@ -71,6 +79,7 @@
},
"dependencies": {
"@babel/runtime": "^7.4.2",
+ "deepmerge": "^3.2.0",
"prop-types": "^15.7.2"
},
"husky": {
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 000000000..29dbe11ec
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,90 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import path from 'path'
+import resolve from 'rollup-plugin-node-resolve'
+import babel from 'rollup-plugin-babel'
+import replace from 'rollup-plugin-replace'
+import commonjs from 'rollup-plugin-commonjs'
+import { uglify } from 'rollup-plugin-uglify'
+import pkg from './package.json'
+
+function getConfig() {
+ const name = 'styledSystem'
+ const buildName = 'styled-system'
+
+ const SOURCE_DIR = path.resolve(__dirname, 'src')
+ const DIST_DIR = path.resolve(__dirname, 'dist')
+
+ const baseConfig = {
+ input: `${SOURCE_DIR}/index.js`,
+ plugins: [
+ babel({
+ runtimeHelpers: true,
+ exclude: 'node_modules/**',
+ configFile: path.join(__dirname, 'babel.config.js'),
+ }),
+ ],
+ }
+
+ const esConfig = Object.assign({}, baseConfig, {
+ output: {
+ file: `${DIST_DIR}/${buildName}.es.js`,
+ format: 'es',
+ },
+ external: [
+ ...Object.keys(pkg.peerDependencies || {}),
+ ...Object.keys(pkg.dependencies || {}),
+ ],
+ plugins: [...baseConfig.plugins, resolve()],
+ })
+
+ const cjsConfig = Object.assign({}, esConfig, {
+ output: {
+ file: `${DIST_DIR}/${buildName}.cjs.js`,
+ format: 'cjs',
+ },
+ })
+
+ const globals = {
+ deepmerge: 'deepmerge',
+ }
+
+ const umdConfig = Object.assign({}, baseConfig, {
+ output: {
+ name,
+ file: `${DIST_DIR}/${buildName}.js`,
+ format: 'umd',
+ globals,
+ exports: 'named',
+ sourcemap: false,
+ },
+ external: Object.keys(globals),
+ plugins: [...baseConfig.plugins, resolve({ browser: true }), commonjs()],
+ })
+
+ const minConfig = Object.assign({}, umdConfig, {
+ output: {
+ ...umdConfig.output,
+ file: `${DIST_DIR}/${buildName}.min.js`,
+ },
+ plugins: [
+ ...umdConfig.plugins,
+ replace({ 'process.env.NODE_ENV': JSON.stringify('production') }),
+ uglify({
+ compress: {
+ pure_getters: true,
+ unsafe: true,
+ unsafe_comps: true,
+ warnings: false,
+ },
+ }),
+ ],
+ })
+
+ if (process.env.WATCH_MODE) {
+ return [esConfig, cjsConfig]
+ }
+
+ return [esConfig, cjsConfig, umdConfig, minConfig]
+}
+
+export default getConfig()
diff --git a/src/index.js b/src/index.js
index c5435234f..876d7b920 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,508 +1,5 @@
-import PropTypes from 'prop-types'
+export * from './style'
+export * from './styles/index'
-export const defaultBreakpoints = [40, 52, 64].map(n => n + 'em')
-
-export const propType = PropTypes.oneOfType([
- PropTypes.number,
- PropTypes.string,
- PropTypes.array,
- PropTypes.object,
-])
-
-export const cloneFunction = fn => (...args) => fn(...args)
-
-export const get = (obj, ...paths) => {
- const value = paths.reduce((a, path) => {
- if (is(a)) return a
- const keys = typeof path === 'string' ? path.split('.') : [path]
- return keys.reduce((a, key) => (a && is(a[key]) ? a[key] : null), obj)
- }, null)
- return is(value) ? value : paths[paths.length - 1]
-}
-
-export const themeGet = (path, fallback = null) => props =>
- get(props.theme, path, fallback)
-
-export const is = n => n !== undefined && n !== null
-
-export const isObject = n => typeof n === 'object' && n !== null
-
-export const num = n => typeof n === 'number' && !isNaN(n)
-
-export const px = n => (num(n) && n !== 0 ? n + 'px' : n)
-
-export const createMediaQuery = n => `@media screen and (min-width: ${px(n)})`
-
-const getValue = (n, scale) => get(scale, n)
-
-export const style = ({
- prop,
- cssProperty,
- alias,
- key,
- transformValue = getValue,
- scale: defaultScale = {},
-}) => {
- const property = cssProperty || prop
- const func = props => {
- const value = get(props, prop, alias, null)
- if (!is(value)) return null
- const scale = get(props.theme, key, defaultScale)
- const createStyle = n =>
- is(n)
- ? {
- [property]: transformValue(n, scale),
- }
- : null
-
- if (!isObject(value)) return createStyle(value)
-
- const breakpoints = get(props.theme, 'breakpoints', defaultBreakpoints)
-
- const styles = []
- if (Array.isArray(value)) {
- styles.push(createStyle(value[0]))
- for (let i = 1; i < value.slice(0, breakpoints.length + 1).length; i++) {
- const rule = createStyle(value[i])
- if (rule) {
- const media = createMediaQuery(breakpoints[i - 1])
- styles.push({ [media]: rule })
- }
- }
- } else {
- for (let key in value) {
- const breakpoint = breakpoints[key]
- const media = createMediaQuery(breakpoint)
- const rule = createStyle(value[key])
- if (!breakpoint) {
- styles.unshift(rule)
- } else {
- styles.push({ [media]: rule })
- }
- }
- styles.sort()
- }
-
- return styles
- }
-
- func.propTypes = {
- [prop]: cloneFunction(propType),
- }
- func.propTypes[prop].meta = {
- prop,
- themeKey: key,
- }
-
- if (alias) {
- func.propTypes[alias] = cloneFunction(propType)
- func.propTypes[alias].meta = {
- prop: alias,
- themeKey: key,
- }
- }
-
- return func
-}
-
-export const compose = (...funcs) => {
- const func = props => {
- const n = funcs.map(fn => fn(props)).filter(Boolean)
- return n
- }
-
- func.propTypes = {}
- funcs.forEach(fn => {
- func.propTypes = {
- ...func.propTypes,
- ...fn.propTypes,
- }
- })
-
- return func
-}
-
-export const mapProps = mapper => func => {
- const next = props => func(mapper(props))
- for (const key in func) {
- next[key] = func[key]
- }
- return next
-}
-
-export const variant = ({ key, prop = 'variant' }) => {
- const fn = props => get(props.theme, [key, props[prop]].join('.'), null)
- fn.propTypes = {
- [prop]: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
- }
- return fn
-}
-
-// space
-const spaceScale = [0, 4, 8, 16, 32, 64, 128, 256, 512]
-
-const getSpace = (n, scale) => {
- if (!num(n)) {
- return px(get(scale, n, n))
- }
-
- const isNegative = n < 0
- const absolute = Math.abs(n)
- const value = get(scale, absolute)
- if (!num(value)) {
- return isNegative ? '-' + value : value
- }
- return px(value * (isNegative ? -1 : 1))
-}
-
-export const margin = style({
- prop: 'margin',
- alias: 'm',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const marginTop = style({
- prop: 'marginTop',
- alias: 'mt',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const marginBottom = style({
- prop: 'marginBottom',
- alias: 'mb',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const marginLeft = style({
- prop: 'marginLeft',
- alias: 'ml',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const marginRight = style({
- prop: 'marginRight',
- alias: 'mr',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const padding = style({
- prop: 'padding',
- alias: 'p',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const paddingTop = style({
- prop: 'paddingTop',
- alias: 'pt',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const paddingBottom = style({
- prop: 'paddingBottom',
- alias: 'pb',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const paddingLeft = style({
- prop: 'paddingLeft',
- alias: 'pl',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const paddingRight = style({
- prop: 'paddingRight',
- alias: 'pr',
- key: 'space',
- transformValue: getSpace,
- scale: spaceScale,
-})
-
-export const space = mapProps(props => ({
- ...props,
- mt: is(props.my) ? props.my : props.mt,
- mb: is(props.my) ? props.my : props.mb,
- ml: is(props.mx) ? props.mx : props.ml,
- mr: is(props.mx) ? props.mx : props.mr,
- pt: is(props.py) ? props.py : props.pt,
- pb: is(props.py) ? props.py : props.pb,
- pl: is(props.px) ? props.px : props.pl,
- pr: is(props.px) ? props.px : props.pr,
-}))(
- compose(
- margin,
- marginTop,
- marginBottom,
- marginLeft,
- marginRight,
- padding,
- paddingTop,
- paddingBottom,
- paddingLeft,
- paddingRight
- )
-)
-
-// color
-export const textColor = style({
- prop: 'color',
- key: 'colors',
-})
-
-export const backgroundColor = style({
- prop: 'backgroundColor',
- alias: 'bg',
- key: 'colors',
-})
-
-export const color = compose(
- textColor,
- backgroundColor
-)
-
-// width
-export const getWidth = (n, scale) => (!num(n) || n > 1 ? px(n) : n * 100 + '%')
-
-export const width = style({
- prop: 'width',
- key: 'widths',
- transformValue: getWidth,
-})
-
-// typography
-
-export const getPx = (n, scale) => px(get(scale, n))
-
-export const fontSize = style({
- prop: 'fontSize',
- key: 'fontSizes',
- transformValue: getPx,
- scale: [12, 14, 16, 20, 24, 32, 48, 64, 72],
-})
-
-export const fontFamily = style({
- prop: 'fontFamily',
- key: 'fonts',
-})
-
-export const fontWeight = style({
- prop: 'fontWeight',
- key: 'fontWeights',
-})
-
-export const lineHeight = style({
- prop: 'lineHeight',
- key: 'lineHeights',
-})
-
-export const textAlign = style({
- prop: 'textAlign',
-})
-
-export const fontStyle = style({
- prop: 'fontStyle',
-})
-
-export const letterSpacing = style({
- prop: 'letterSpacing',
- key: 'letterSpacings',
- transformValue: getPx,
-})
-
-// layout
-export const display = style({
- prop: 'display',
-})
-
-export const maxWidth = style({
- prop: 'maxWidth',
- key: 'maxWidths',
- transformValue: getPx,
-})
-
-export const minWidth = style({
- prop: 'minWidth',
- key: 'minWidths',
- transformValue: getPx,
-})
-
-export const height = style({
- prop: 'height',
- key: 'heights',
- transformValue: getPx,
-})
-
-export const maxHeight = style({
- prop: 'maxHeight',
- key: 'maxHeights',
- transformValue: getPx,
-})
-
-export const minHeight = style({
- prop: 'minHeight',
- key: 'minHeights',
- transformValue: getPx,
-})
-
-export const size = mapProps(props => ({
- ...props,
- width: props.size,
- height: props.size,
-}))(
- compose(
- width,
- height
- )
-)
-
-export const verticalAlign = style({ prop: 'verticalAlign' })
-
-// flexbox
-export const alignItems = style({ prop: 'alignItems' })
-export const alignContent = style({ prop: 'alignContent' })
-export const justifyItems = style({ prop: 'justifyItems' })
-export const justifyContent = style({ prop: 'justifyContent' })
-export const flexWrap = style({ prop: 'flexWrap' })
-export const flexBasis = style({ prop: 'flexBasis', transformValue: getWidth })
-export const flexDirection = style({ prop: 'flexDirection' })
-export const flex = style({ prop: 'flex' })
-export const justifySelf = style({ prop: 'justifySelf' })
-export const alignSelf = style({ prop: 'alignSelf' })
-export const order = style({ prop: 'order' })
-
-// grid
-export const gridGap = style({
- prop: 'gridGap',
- key: 'space',
- transformValue: getPx,
- scale: spaceScale,
-})
-
-export const gridColumnGap = style({
- prop: 'gridColumnGap',
- key: 'space',
- transformValue: getPx,
- scale: spaceScale,
-})
-
-export const gridRowGap = style({
- prop: 'gridRowGap',
- key: 'space',
- transformValue: getPx,
- scale: spaceScale,
-})
-
-export const gridColumn = style({ prop: 'gridColumn' })
-export const gridRow = style({ prop: 'gridRow' })
-export const gridAutoFlow = style({ prop: 'gridAutoFlow' })
-export const gridAutoColumns = style({ prop: 'gridAutoColumns' })
-export const gridAutoRows = style({ prop: 'gridAutoRows' })
-export const gridTemplateColumns = style({ prop: 'gridTemplateColumns' })
-export const gridTemplateRows = style({ prop: 'gridTemplateRows' })
-export const gridTemplateAreas = style({ prop: 'gridTemplateAreas' })
-export const gridArea = style({ prop: 'gridArea' })
-
-// borders
-export const border = style({
- prop: 'border',
- key: 'borders',
-})
-
-export const borderWidth = style({
- prop: 'borderWidth',
- key: 'borderWidths',
- transformValue: getPx,
-})
-
-export const borderStyle = style({
- prop: 'borderStyle',
- key: 'borderStyles',
-})
-
-export const borderColor = style({
- prop: 'borderColor',
- key: 'colors',
-})
-
-export const borderTop = style({
- prop: 'borderTop',
- key: 'borders',
-})
-
-export const borderRight = style({
- prop: 'borderRight',
- key: 'borders',
-})
-
-export const borderBottom = style({
- prop: 'borderBottom',
- key: 'borders',
-})
-
-export const borderLeft = style({
- prop: 'borderLeft',
- key: 'borders',
-})
-
-export const borderRadius = style({
- prop: 'borderRadius',
- key: 'radii',
- transformValue: getPx,
-})
-
-export const borders = compose(
- border,
- borderTop,
- borderRight,
- borderBottom,
- borderLeft,
- borderWidth,
- borderStyle,
- borderColor,
- borderRadius
-)
-
-export const boxShadow = style({
- prop: 'boxShadow',
- key: 'shadows',
-})
-
-export const opacity = style({ prop: 'opacity' })
-export const overflow = style({ prop: 'overflow' })
-
-// backgrounds
-export const background = style({ prop: 'background' })
-export const backgroundImage = style({ prop: 'backgroundImage' })
-export const backgroundSize = style({ prop: 'backgroundSize' })
-export const backgroundPosition = style({ prop: 'backgroundPosition' })
-export const backgroundRepeat = style({ prop: 'backgroundRepeat' })
-
-// position
-export const position = style({ prop: 'position' })
-export const zIndex = style({ prop: 'zIndex', key: 'zIndices' })
-export const top = style({ prop: 'top', transformValue: getPx })
-export const right = style({ prop: 'right', transformValue: getPx })
-export const bottom = style({ prop: 'bottom', transformValue: getPx })
-export const left = style({ prop: 'left', transformValue: getPx })
-
-// variants
-export const buttonStyle = variant({ key: 'buttons' })
-export const textStyle = variant({ key: 'textStyles', prop: 'textStyle' })
-export const colorStyle = variant({ key: 'colorStyles', prop: 'colors' })
+export * from './variant'
+export * from './variants'
diff --git a/src/media.js b/src/media.js
new file mode 100644
index 000000000..13bec68b2
--- /dev/null
+++ b/src/media.js
@@ -0,0 +1,8 @@
+export const minBreakpoint = breakpoint =>
+ breakpoint !== 0 ? breakpoint : null
+
+export const minWidth = value => `@media screen and (min-width: ${value})`
+
+export const DEFAULT_BREAKPOINTS = [0, 40, 52, 64].map(n =>
+ n > 0 ? n + 'em' : n
+)
diff --git a/src/style.js b/src/style.js
new file mode 100644
index 000000000..d5bbd6ce6
--- /dev/null
+++ b/src/style.js
@@ -0,0 +1,195 @@
+import { minBreakpoint, minWidth, DEFAULT_BREAKPOINTS } from './media'
+import { is, num, string, obj, merge, get, func } from './util'
+
+function callOrReturn(fn, arg) {
+ if (!func(fn)) return fn
+ const next = fn(arg)
+ return callOrReturn(next, arg)
+}
+
+function getThemeValue(theme, path, initial) {
+ if (!theme) return undefined
+ return callOrReturn(get(initial || theme, path), { theme })
+}
+
+function getValue(value, variants, theme) {
+ if (is(variants)) {
+ const valueFromVariants = getThemeValue(theme, value, variants)
+ if (is(valueFromVariants)) {
+ return valueFromVariants
+ }
+ }
+ return value
+}
+
+function styleFromValue(cssProperties, value, theme, key, transformValue) {
+ const variants = getThemeValue(theme, key)
+ const computedValue = getValue(value, variants, theme)
+ if (string(computedValue) || num(computedValue)) {
+ const style = {}
+ for (let i = 0; i < cssProperties.length; i++) {
+ style[cssProperties[i]] = transformValue
+ ? transformValue(computedValue, {
+ rawValue: value,
+ variants,
+ })
+ : computedValue
+ }
+ return style
+ }
+ return null
+}
+
+function getBreakpoints(theme) {
+ const themeBreakpoints = getThemeValue(theme, 'breakpoints')
+ if (is(themeBreakpoints)) {
+ return themeBreakpoints
+ }
+ return DEFAULT_BREAKPOINTS
+}
+
+function createStyleGenerator(getStyle, props, generators) {
+ const getStyles = props => {
+ const theme = props.theme || null
+ return getStyle(props, theme)
+ }
+
+ getStyles.meta = {
+ props,
+ getStyle,
+ generators,
+ }
+
+ return getStyles
+}
+
+function styleFromBreakPoint(cssProperties, value, theme, key, transformValue) {
+ const breakpoints = getBreakpoints(theme)
+ const keys = Object.keys(value)
+ let allStyle = {}
+ for (let i = 0; i < keys.length; i++) {
+ const breakpoint = keys[i]
+ const style = styleFromValue(
+ cssProperties,
+ value[breakpoint],
+ theme,
+ key,
+ transformValue
+ )
+
+ if (style !== null) {
+ const breakpointValue = minBreakpoint(breakpoints[breakpoint])
+
+ if (breakpointValue === null) {
+ allStyle = merge(allStyle, style)
+ } else if (breakpointValue) {
+ allStyle = merge(allStyle, {
+ [minWidth(breakpointValue)]: style,
+ })
+ }
+ }
+ }
+ return allStyle
+}
+
+function getStyleFactory(prop, cssProperties, key, transformValue) {
+ return function getStyle(attrs, theme) {
+ const value = attrs[prop]
+ if (!is(value)) return null
+
+ const style = styleFromValue(
+ cssProperties,
+ value,
+ theme,
+ key,
+ transformValue
+ )
+
+ if (style !== null) {
+ return style
+ }
+
+ if (obj(value)) {
+ return styleFromBreakPoint(
+ cssProperties,
+ value,
+ theme,
+ key,
+ transformValue
+ )
+ }
+
+ return null
+ }
+}
+
+export function style({
+ prop,
+ cssProperties,
+ cssProperty,
+ alias,
+ key = null,
+ transformValue = null,
+}) {
+ cssProperties = cssProperties || (cssProperty ? [cssProperty] : [prop])
+ const getStyle = getStyleFactory(prop, cssProperties, key, transformValue)
+ const generator = createStyleGenerator(getStyle, [prop])
+
+ if (!alias) {
+ return generator
+ }
+
+ return compose(
+ generator,
+ style({ prop: alias, cssProperties, key, transformValue })
+ )
+}
+
+function indexGeneratorsByProp(styles) {
+ const index = {}
+ for (let i = 0; i < styles.length; i++) {
+ const style = styles[i]
+ if (style && style.meta) {
+ const propsKeys = Object.keys(style.meta.props)
+ for (let j = 0; j < propsKeys.length; j++) {
+ const prop = style.meta.props[propsKeys[j]]
+ index[prop] = style
+ }
+ }
+ }
+ return index
+}
+
+export function compose(...generators) {
+ let flatGenerators = []
+ generators.forEach(gen => {
+ if (gen.meta.generators) {
+ flatGenerators = [...flatGenerators, ...gen.meta.generators]
+ } else {
+ flatGenerators.push(gen)
+ }
+ })
+
+ const generatorsByProp = indexGeneratorsByProp(flatGenerators)
+
+ function getStyle(attrs, theme) {
+ const propKeys = Object.keys(attrs)
+ const propCount = propKeys.length
+ let allStyle = {}
+ for (let i = 0; i < propCount; i++) {
+ const propKey = propKeys[i]
+ const generator = generatorsByProp[propKey]
+ if (generator) {
+ allStyle = merge(allStyle, generator.meta.getStyle(attrs, theme))
+ }
+ }
+ return allStyle
+ }
+
+ const props = flatGenerators.reduce(
+ (keys, generator) => [...keys, ...generator.meta.props],
+ []
+ )
+
+ return createStyleGenerator(getStyle, props, generators)
+}
diff --git a/src/styles/backgrounds.js b/src/styles/backgrounds.js
new file mode 100644
index 000000000..940b5a541
--- /dev/null
+++ b/src/styles/backgrounds.js
@@ -0,0 +1,36 @@
+import { style, compose } from '../style'
+
+export const background = style({
+ prop: 'background',
+})
+
+export const backgroundColor = style({
+ prop: 'backgroundColor',
+ alias: 'bg',
+ key: 'colors',
+})
+
+export const backgroundImage = style({
+ prop: 'backgroundImage',
+})
+
+export const backgroundSize = style({
+ prop: 'backgroundSize',
+})
+
+export const backgroundPosition = style({
+ prop: 'backgroundPosition',
+})
+
+export const backgroundRepeat = style({
+ prop: 'backgroundRepeat',
+})
+
+export const backgrounds = compose(
+ background,
+ backgroundColor,
+ backgroundImage,
+ backgroundSize,
+ backgroundPosition,
+ backgroundRepeat
+)
diff --git a/src/styles/basics.js b/src/styles/basics.js
new file mode 100644
index 000000000..07d2f125c
--- /dev/null
+++ b/src/styles/basics.js
@@ -0,0 +1,14 @@
+import { style, compose } from '../style'
+
+export const opacity = style({
+ prop: 'opacity',
+})
+
+export const overflow = style({
+ prop: 'overflow',
+})
+
+export const basics = compose(
+ opacity,
+ overflow,
+)
diff --git a/src/styles/borders.js b/src/styles/borders.js
new file mode 100644
index 000000000..660aced02
--- /dev/null
+++ b/src/styles/borders.js
@@ -0,0 +1,99 @@
+import { style, compose } from '../style'
+import { num } from '../util'
+import { px } from '../unit'
+
+const getBorder = n => (num(n) && n > 0 ? `${n}px solid` : n)
+
+export const border = style({
+ prop: 'border',
+ key: 'borders',
+ transformValue: getBorder,
+})
+
+export const borderWidth = style({
+ prop: 'borderWidth',
+ key: 'borderWidths',
+ transformValue: px,
+})
+
+export const borderStyle = style({
+ prop: 'borderStyle',
+ key: 'borderStyles',
+})
+
+export const borderColor = style({
+ prop: 'borderColor',
+ key: 'colors',
+})
+
+export const borderRadius = style({
+ prop: 'borderRadius',
+ key: 'radii',
+ transformValue: px,
+})
+
+export const borderTop = style({
+ prop: 'borderTop',
+ key: 'borders',
+ transformValue: getBorder,
+})
+
+export const borderTopColor = style({
+ prop: 'borderTopColor',
+ key: 'colors',
+})
+
+export const borderRight = style({
+ prop: 'borderRight',
+ key: 'borders',
+ transformValue: getBorder,
+})
+
+export const borderRightColor = style({
+ prop: 'borderRightColor',
+ key: 'colors',
+})
+
+export const borderBottom = style({
+ prop: 'borderBottom',
+ key: 'borders',
+ transformValue: getBorder,
+})
+
+export const borderBottomColor = style({
+ prop: 'borderBottomColor',
+ key: 'colors',
+})
+
+export const borderLeft = style({
+ prop: 'borderLeft',
+ key: 'borders',
+ transformValue: getBorder,
+})
+
+export const borderLeftColor = style({
+ prop: 'borderLeftColor',
+ key: 'colors',
+})
+
+export const boxShadow = style({
+ prop: 'boxShadow',
+ key: 'shadows',
+})
+
+export const borders = compose(
+ border,
+ borderWidth,
+ borderStyle,
+ borderColor,
+ borderTop,
+ borderTopColor,
+ borderRight,
+ borderRightColor,
+ borderBottom,
+ borderBottomColor,
+ borderLeft,
+ borderLeftColor,
+ borderRadius,
+ boxShadow
+)
diff --git a/src/styles/colors.js b/src/styles/colors.js
new file mode 100644
index 000000000..72ada39bd
--- /dev/null
+++ b/src/styles/colors.js
@@ -0,0 +1,9 @@
+import { compose } from '../style'
+
+import { backgroundColor } from './backgrounds'
+import { textColor } from './typography'
+
+export const color = compose(
+ backgroundColor,
+ textColor
+)
diff --git a/src/styles/dimensions.js b/src/styles/dimensions.js
new file mode 100644
index 000000000..0ee39d7be
--- /dev/null
+++ b/src/styles/dimensions.js
@@ -0,0 +1,54 @@
+import { style, compose } from '../style'
+import { percent } from '../unit'
+
+export const width = style({
+ prop: 'width',
+ transformValue: percent,
+ key: 'widths',
+})
+
+export const height = style({
+ prop: 'height',
+ transformValue: percent,
+ key: 'heights',
+})
+
+export const size = style({
+ prop: 'size',
+ cssProperties: ['width', 'height'],
+ transformValue: percent,
+ key: 'sizes',
+})
+
+export const maxWidth = style({
+ prop: 'maxWidth',
+ transformValue: percent,
+ key: 'widths',
+})
+
+export const maxHeight = style({
+ prop: 'maxHeight',
+ transformValue: percent,
+ key: 'heights',
+})
+
+export const minWidth = style({
+ prop: 'minWidth',
+ transformValue: percent,
+ key: 'widths',
+})
+
+export const minHeight = style({
+ prop: 'minHeight',
+ transformValue: percent,
+ key: 'heights',
+})
+
+export const dimensions = compose(
+ width,
+ height,
+ maxWidth,
+ maxHeight,
+ minWidth,
+ minHeight
+)
diff --git a/src/styles/flexboxes.js b/src/styles/flexboxes.js
new file mode 100644
index 000000000..16f0f622b
--- /dev/null
+++ b/src/styles/flexboxes.js
@@ -0,0 +1,65 @@
+import { style, compose } from '../style'
+import { percent } from '../unit'
+
+export const display = style({
+ prop: 'display',
+})
+
+export const alignItems = style({
+ prop: 'alignItems',
+})
+
+export const alignContent = style({
+ prop: 'alignContent',
+})
+
+export const justifyItems = style({
+ prop: 'justifyItems',
+})
+
+export const justifyContent = style({
+ prop: 'justifyContent',
+})
+
+export const flexWrap = style({
+ prop: 'flexWrap',
+})
+
+export const flexBasis = style({
+ prop: 'flexBasis',
+ transformValue: percent,
+})
+
+export const flexDirection = style({
+ prop: 'flexDirection',
+})
+
+export const flex = style({
+ prop: 'flex',
+})
+
+export const justifySelf = style({
+ prop: 'justifySelf',
+})
+
+export const alignSelf = style({
+ prop: 'alignSelf',
+})
+
+export const order = style({
+ prop: 'order',
+})
+
+export const flexboxes = compose(
+ display,
+ alignItems,
+ alignContent,
+ justifyContent,
+ flexWrap,
+ flexBasis,
+ flexDirection,
+ flex,
+ justifySelf,
+ alignSelf,
+ order
+)
diff --git a/src/styles/grids.js b/src/styles/grids.js
new file mode 100644
index 000000000..ffbc13522
--- /dev/null
+++ b/src/styles/grids.js
@@ -0,0 +1,45 @@
+import { style, compose } from '../style'
+import { scale } from '../unit'
+
+export const gridGap = style({
+ prop: 'gridGap',
+ key: 'space',
+ transformValue: scale(),
+})
+
+export const gridColumnGap = style({
+ prop: 'gridColumnGap',
+ key: 'space',
+ transformValue: scale(),
+})
+
+export const gridRowGap = style({
+ prop: 'gridRowGap',
+ key: 'space',
+ transformValue: scale(),
+})
+
+export const gridColumn = style({ prop: 'gridColumn' })
+export const gridRow = style({ prop: 'gridRow' })
+export const gridAutoFlow = style({ prop: 'gridAutoFlow' })
+export const gridAutoColumns = style({ prop: 'gridAutoColumns' })
+export const gridAutoRows = style({ prop: 'gridAutoRows' })
+export const gridTemplateColumns = style({ prop: 'gridTemplateColumns' })
+export const gridTemplateRows = style({ prop: 'gridTemplateRows' })
+export const gridTemplateAreas = style({ prop: 'gridTemplateAreas' })
+export const gridArea = style({ prop: 'gridArea' })
+
+export const grids = compose(
+ gridGap,
+ gridColumnGap,
+ gridRowGap,
+ gridColumn,
+ gridRow,
+ gridAutoFlow,
+ gridAutoColumns,
+ gridAutoRows,
+ gridTemplateColumns,
+ gridTemplateRows,
+ gridTemplateAreas,
+ gridArea
+)
diff --git a/src/styles/index.js b/src/styles/index.js
new file mode 100644
index 000000000..08e64fd66
--- /dev/null
+++ b/src/styles/index.js
@@ -0,0 +1,33 @@
+import { compose } from '../style'
+import { backgrounds } from './backgrounds'
+import { basics } from './basics'
+import { borders } from './borders'
+import { dimensions } from './dimensions'
+import { flexboxes } from './flexboxes'
+import { grids } from './grids'
+import { positions } from './positions'
+import { space } from './space'
+import { typography } from './typography'
+
+export * from './backgrounds'
+export * from './basics'
+export * from './borders'
+export * from './colors'
+export * from './dimensions'
+export * from './flexboxes'
+export * from './grids'
+export * from './positions'
+export * from './space'
+export * from './typography'
+
+export const system = compose(
+ backgrounds,
+ basics,
+ borders,
+ dimensions,
+ flexboxes,
+ grids,
+ positions,
+ space,
+ typography
+)
diff --git a/src/styles/index.test.js-nop b/src/styles/index.test.js-nop
new file mode 100644
index 000000000..7f3bbad66
--- /dev/null
+++ b/src/styles/index.test.js-nop
@@ -0,0 +1,432 @@
+import React from 'react'
+import { mount } from 'enzyme'
+import styled from 'styled-components'
+import * as styles from './index'
+
+describe('styles', () => {
+ describe.each([
+ [
+ 'fontFamily',
+ {
+ styleRule: 'font-family',
+ theme: {
+ fonts: {
+ primary: 'serif',
+ secondary: 'sans-serif',
+ },
+ },
+ expectations: [['arial', 'arial'], ['primary', 'serif']],
+ },
+ ],
+ [
+ 'fontSize',
+ {
+ styleRule: 'font-size',
+ theme: {
+ fontSizes: [10, 15, 40],
+ },
+ expectations: [[0, '10px'], [1, '15px'], [20, '20px'], ['3em', '3em']],
+ },
+ ],
+ [
+ 'lineHeight',
+ {
+ styleRule: 'line-height',
+ theme: {
+ lineHeights: [1.2, 1.5, 2],
+ },
+ expectations: [[0, 1.2], [1, 1.5], [3, 3], ['3em', '3em']],
+ },
+ ],
+ [
+ 'fontWeight',
+ {
+ styleRule: 'font-weight',
+ theme: {
+ fontWeights: [400, 500, 800],
+ },
+ expectations: [[0, 400], [1, 500], [800, 800], ['medium', 'medium']],
+ },
+ ],
+ [
+ 'textAlign',
+ {
+ styleRule: 'text-align',
+ expectations: [['center', 'center'], ['justify', 'justify']],
+ },
+ ],
+ [
+ 'letterSpacing',
+ {
+ styleRule: 'letter-spacing',
+ theme: {
+ letterSpacings: [1.2, 2],
+ },
+ expectations: [
+ [0, '1.2px'],
+ [1, '2px'],
+ [1.1, '1.1px'],
+ ['2rem', '2rem'],
+ ],
+ },
+ ],
+ [
+ 'color',
+ {
+ styleRule: 'color',
+ theme: {
+ colors: {
+ primary: 'red',
+ },
+ },
+ expectations: [
+ ['primary', 'red'],
+ ['#fff', '#fff'],
+ ['rgba(0,0,0,0)', 'rgba(0,0,0,0)'],
+ ],
+ },
+ ],
+ [
+ 'width',
+ {
+ styleRule: 'width',
+ theme: {
+ widths: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'height',
+ {
+ styleRule: 'height',
+ theme: {
+ heights: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'maxWidth',
+ {
+ styleRule: 'max-width',
+ theme: {
+ widths: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'maxHeight',
+ {
+ styleRule: 'max-height',
+ theme: {
+ heights: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'minWidth',
+ {
+ styleRule: 'min-width',
+ theme: {
+ widths: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'minHeight',
+ {
+ styleRule: 'min-height',
+ theme: {
+ heights: {
+ large: 400,
+ },
+ },
+ expectations: [[0.5, '50%'], ['large', '400px'], [50, '50px']],
+ },
+ ],
+ [
+ 'display',
+ {
+ styleRule: 'display',
+ expectations: [['flex', 'flex'], ['block', 'block']],
+ },
+ ],
+ [
+ 'alignItems',
+ {
+ styleRule: 'align-items',
+ expectations: [['flex-start', 'flex-start'], ['center', 'center']],
+ },
+ ],
+ [
+ 'alignContent',
+ {
+ styleRule: 'align-content',
+ expectations: [['flex-start', 'flex-start'], ['center', 'center']],
+ },
+ ],
+ [
+ 'justifyContent',
+ {
+ styleRule: 'justify-content',
+ expectations: [['flex-start', 'flex-start'], ['center', 'center']],
+ },
+ ],
+ [
+ 'flexWrap',
+ {
+ styleRule: 'flex-wrap',
+ expectations: [['wrap', 'wrap'], ['nowrap', 'nowrap']],
+ },
+ ],
+ [
+ 'flexBasis',
+ {
+ styleRule: 'flex-basis',
+ expectations: [[0.5, '50%'], [50, '50px']],
+ },
+ ],
+ [
+ 'flexDirection',
+ {
+ styleRule: 'flex-direction',
+ expectations: [['row', 'row'], ['column', 'column']],
+ },
+ ],
+ [
+ 'flex',
+ {
+ styleRule: 'flex',
+ expectations: [[1, '1'], ['1 0 auto', '1 0 auto']],
+ },
+ ],
+ [
+ 'justifySelf',
+ {
+ styleRule: 'justify-self',
+ expectations: [['flex-start', 'flex-start'], ['center', 'center']],
+ },
+ ],
+ [
+ 'alignSelf',
+ {
+ styleRule: 'align-self',
+ expectations: [['flex-start', 'flex-start'], ['center', 'center']],
+ },
+ ],
+ [
+ 'order',
+ {
+ styleRule: 'order',
+ expectations: [[1, '1'], [10, '10']],
+ },
+ ],
+ [
+ 'background',
+ {
+ styleRule: 'background',
+ expectations: [['red', 'red'], ['blue', 'blue']],
+ },
+ ],
+ [
+ 'backgroundColor',
+ {
+ theme: {
+ colors: {
+ primary: 'red',
+ },
+ },
+ styleRule: 'background-color',
+ expectations: [['primary', 'red'], ['blue', 'blue']],
+ },
+ ],
+ [
+ 'backgroundImage',
+ {
+ styleRule: 'background-image',
+ expectations: [['url(x.gif)', 'url(x.gif)']],
+ },
+ ],
+ [
+ 'backgroundSize',
+ {
+ styleRule: 'background-size',
+ expectations: [['cover', 'cover'], ['50%', '50%']],
+ },
+ ],
+ [
+ 'backgroundRepeat',
+ {
+ styleRule: 'background-repeat',
+ expectations: [['no-repeat', 'no-repeat'], ['repeat-y', 'repeat-y']],
+ },
+ ],
+ [
+ 'position',
+ {
+ styleRule: 'position',
+ expectations: [['absolute', 'absolute'], ['relative', 'relative']],
+ },
+ ],
+ [
+ 'zIndex',
+ {
+ theme: {
+ zIndexes: {
+ alert: 100,
+ },
+ },
+ styleRule: 'z-index',
+ expectations: [['alert', '100'], [20, '20']],
+ },
+ ],
+ [
+ 'top',
+ {
+ styleRule: 'top',
+ expectations: [[10, '10px'], ['10px', '10px'], ['4%', '4%']],
+ },
+ ],
+ [
+ 'right',
+ {
+ styleRule: 'right',
+ expectations: [[10, '10px'], ['10px', '10px'], ['4%', '4%']],
+ },
+ ],
+ [
+ 'bottom',
+ {
+ styleRule: 'bottom',
+ expectations: [[10, '10px'], ['10px', '10px'], ['4%', '4%']],
+ },
+ ],
+ [
+ 'left',
+ {
+ styleRule: 'left',
+ expectations: [[10, '10px'], ['10px', '10px'], ['4%', '4%']],
+ },
+ ],
+ [
+ 'border',
+ {
+ styleRule: 'border',
+ expectations: [[1, '1px solid'], ['1px solid red', '1px solid red']],
+ },
+ ],
+ [
+ 'borderTop',
+ {
+ styleRule: 'border-top',
+ expectations: [[1, '1px solid'], ['1px solid red', '1px solid red']],
+ },
+ ],
+ [
+ 'borderRight',
+ {
+ styleRule: 'border-right',
+ expectations: [[1, '1px solid'], ['1px solid red', '1px solid red']],
+ },
+ ],
+ [
+ 'borderBottom',
+ {
+ styleRule: 'border-bottom',
+ expectations: [[1, '1px solid'], ['1px solid red', '1px solid red']],
+ },
+ ],
+ [
+ 'borderLeft',
+ {
+ styleRule: 'border-left',
+ expectations: [[1, '1px solid'], ['1px solid red', '1px solid red']],
+ },
+ ],
+ [
+ 'borderColor',
+ {
+ theme: {
+ colors: {
+ primary: 'red',
+ },
+ },
+ styleRule: 'border-color',
+ expectations: [['primary', 'red'], ['blue', 'blue']],
+ },
+ ],
+ [
+ 'borderRadius',
+ {
+ theme: {
+ radii: {
+ round: '50%',
+ },
+ },
+ styleRule: 'border-radius',
+ expectations: [['round', '50%'], [10, '10px']],
+ },
+ ],
+ [
+ 'boxShadow',
+ {
+ theme: {
+ shadows: {
+ red: '10px 5px 5px red',
+ },
+ },
+ styleRule: 'box-shadow',
+ expectations: [
+ ['red', '10px 5px 5px red'],
+ [
+ '12px 12px 2px 1px rgba(0, 0, 255, .2)',
+ '12px 12px 2px 1px rgba(0,0,255,.2)',
+ ],
+ ],
+ },
+ ],
+ [
+ 'opacity',
+ {
+ styleRule: 'opacity',
+ expectations: [[1, '1'], [0.2, '0.2']],
+ },
+ ],
+ ])('#%s', (name, config) => {
+ const Dummy = styled.div`
+ ${styles[name]};
+ `
+
+ it('should support basic value', () => {
+ config.expectations.forEach(([value, expected]) => {
+ const props = { [name]: value }
+ const wrapper = mount()
+ expect(wrapper).toHaveStyleRule(config.styleRule, String(expected))
+ })
+ })
+
+ it('should support breakpoints value', () => {
+ config.expectations.forEach(([value, expected]) => {
+ const props = { [name]: { md: value } }
+ const wrapper = mount()
+ expect(wrapper).toHaveStyleRule(config.styleRule, String(expected), {
+ media: '(min-width:768px)',
+ })
+ })
+ })
+ })
+})
diff --git a/src/styles/positions.js b/src/styles/positions.js
new file mode 100644
index 000000000..7806fa01a
--- /dev/null
+++ b/src/styles/positions.js
@@ -0,0 +1,40 @@
+import { style, compose } from '../style'
+import { px } from '../unit'
+
+export const position = style({
+ prop: 'position',
+})
+
+export const zIndex = style({
+ prop: 'zIndex',
+ key: 'zIndices',
+})
+
+export const top = style({
+ prop: 'top',
+ transformValue: px,
+})
+
+export const right = style({
+ prop: 'right',
+ transformValue: px,
+})
+
+export const bottom = style({
+ prop: 'bottom',
+ transformValue: px,
+})
+
+export const left = style({
+ prop: 'left',
+ transformValue: px,
+})
+
+export const positions = compose(
+ position,
+ zIndex,
+ top,
+ right,
+ bottom,
+ left
+)
diff --git a/src/styles/space.js b/src/styles/space.js
new file mode 100644
index 000000000..4654cf3ad
--- /dev/null
+++ b/src/styles/space.js
@@ -0,0 +1,129 @@
+import { style, compose } from '../style'
+import { scale } from '../unit'
+
+const key = 'space'
+
+export const margin = style({
+ prop: 'margin',
+ alias: 'm',
+ cssProperties: ['margin'],
+ key,
+ transformValue: scale(),
+})
+
+export const marginTop = style({
+ prop: 'marginTop',
+ alias: 'mt',
+ cssProperties: ['marginTop'],
+ key,
+ transformValue: scale(),
+})
+
+export const marginRight = style({
+ prop: 'marginRight',
+ alias: 'mr',
+ cssProperties: ['marginRight'],
+ key,
+ transformValue: scale(),
+})
+
+export const marginBottom = style({
+ prop: 'marginBottom',
+ alias: 'mb',
+ cssProperties: ['marginBottom'],
+ key,
+ transformValue: scale(),
+})
+
+export const marginLeft = style({
+ prop: 'marginLeft',
+ alias: 'ml',
+ cssProperties: ['marginLeft'],
+ key,
+ transformValue: scale(),
+})
+
+export const mx = style({
+ prop: 'mx',
+ cssProperties: ['marginRight', 'marginLeft'],
+ key,
+ transformValue: scale(),
+})
+
+export const my = style({
+ prop: 'my',
+ cssProperties: ['marginTop', 'marginBottom'],
+ key,
+ transformValue: scale(),
+})
+
+export const padding = style({
+ prop: 'padding',
+ alias: 'p',
+ cssProperties: ['padding'],
+ key,
+ transformValue: scale(),
+})
+
+export const paddingTop = style({
+ prop: 'paddingTop',
+ alias: 'pt',
+ cssProperties: ['paddingTop'],
+ key,
+ transformValue: scale(),
+})
+
+export const paddingRight = style({
+ prop: 'paddingRight',
+ alias: 'pr',
+ cssProperties: ['paddingRight'],
+ key,
+ transformValue: scale(),
+})
+
+export const paddingBottom = style({
+ prop: 'paddingBottom',
+ alias: 'pb',
+ cssProperties: ['paddingBottom'],
+ key,
+ transformValue: scale(),
+})
+
+export const paddingLeft = style({
+ prop: 'paddingLeft',
+ alias: 'pl',
+ cssProperties: ['paddingLeft'],
+ key,
+ transformValue: scale(),
+})
+
+export const px = style({
+ prop: 'px',
+ cssProperties: ['paddingRight', 'paddingLeft'],
+ key,
+ transformValue: scale(),
+})
+
+export const py = style({
+ prop: 'py',
+ cssProperties: ['paddingTop', 'paddingBottom'],
+ key,
+ transformValue: scale(),
+})
+
+export const space = compose(
+ margin,
+ marginTop,
+ marginRight,
+ marginBottom,
+ marginLeft,
+ mx,
+ my,
+ padding,
+ paddingTop,
+ paddingRight,
+ paddingBottom,
+ paddingLeft,
+ px,
+ py
+)
diff --git a/src/styles/space.test.js-nop b/src/styles/space.test.js-nop
new file mode 100644
index 000000000..2f79ab728
--- /dev/null
+++ b/src/styles/space.test.js-nop
@@ -0,0 +1,108 @@
+import { space } from './space'
+
+describe('space', () => {
+ it('should support m', () => {
+ expect(space({ m: 1 })).toEqual({ margin: 8 })
+ expect(space({ m: 2 })).toEqual({ margin: 16 })
+ expect(space({ m: -2 })).toEqual({ margin: -16 })
+ expect(space({ m: 10 })).toEqual({ margin: 10 })
+ expect(space({ m: -10 })).toEqual({ margin: -10 })
+ expect(space({ m: '50%' })).toEqual({ margin: '50%' })
+ expect(space({ m: { md: '50%' } })).toEqual({
+ '@media (min-width: 768px)': { margin: '50%' },
+ })
+ })
+
+ it('should support mx', () => {
+ expect(space({ mx: 10 })).toEqual({
+ marginLeft: 10,
+ marginRight: 10,
+ })
+ expect(space({ mx: '50%' })).toEqual({
+ marginLeft: '50%',
+ marginRight: '50%',
+ })
+ expect(space({ mx: { md: '50%' } })).toEqual({
+ '@media (min-width: 768px)': { marginLeft: '50%', marginRight: '50%' },
+ })
+ })
+
+ it('should support mb, mt, ml, mr', () => {
+ expect(space({ mb: 10 })).toEqual({ marginBottom: 10 })
+ expect(space({ mt: 10 })).toEqual({ marginTop: 10 })
+ expect(space({ ml: 10 })).toEqual({ marginLeft: 10 })
+ expect(space({ mr: 10 })).toEqual({ marginRight: 10 })
+ })
+
+ it('should support p', () => {
+ expect(space({ p: 10 })).toEqual({ padding: 10 })
+ expect(space({ p: '50%' })).toEqual({ padding: '50%' })
+ expect(space({ p: { md: '50%' } })).toEqual({
+ '@media (min-width: 768px)': { padding: '50%' },
+ })
+ })
+
+ it('should support px, py', () => {
+ expect(space({ px: 10 })).toEqual({
+ paddingLeft: 10,
+ paddingRight: 10,
+ })
+ expect(space({ px: '50%' })).toEqual({
+ paddingLeft: '50%',
+ paddingRight: '50%',
+ })
+ expect(space({ px: { md: '50%' } })).toEqual({
+ '@media (min-width: 768px)': { paddingLeft: '50%', paddingRight: '50%' },
+ })
+ })
+
+ it('should support pb, pt, pl, pr', () => {
+ expect(space({ pb: 10 })).toEqual({ paddingBottom: 10 })
+ expect(space({ pt: 10 })).toEqual({ paddingTop: 10 })
+ expect(space({ pl: 10 })).toEqual({ paddingLeft: 10 })
+ expect(space({ pr: 10 })).toEqual({ paddingRight: 10 })
+ })
+
+ it('should merge everything', () => {
+ expect(space({ px: { md: '50%' }, mx: { md: 10 } })).toEqual({
+ '@media (min-width: 768px)': {
+ paddingLeft: '50%',
+ paddingRight: '50%',
+ marginLeft: 10,
+ marginRight: 10,
+ },
+ })
+ })
+
+ it('should support variants spaces', () => {
+ expect(
+ space({
+ m: 1,
+ p: 0,
+ theme: { spaces: [0, 8, 16] },
+ })
+ ).toEqual({
+ margin: 8,
+ padding: 0,
+ })
+ })
+
+ it('should expose meta', () => {
+ expect(space.meta).toEqual([
+ 'm',
+ 'mt',
+ 'mr',
+ 'mb',
+ 'ml',
+ 'mx',
+ 'my',
+ 'p',
+ 'pt',
+ 'pr',
+ 'pb',
+ 'pl',
+ 'px',
+ 'py',
+ ])
+ })
+})
diff --git a/src/styles/typography.js b/src/styles/typography.js
new file mode 100644
index 000000000..a4a1ba69c
--- /dev/null
+++ b/src/styles/typography.js
@@ -0,0 +1,54 @@
+import { px } from '../unit'
+import { style, compose } from '../style'
+import { scale } from '../unit'
+
+export const fontFamily = style({
+ prop: 'fontFamily',
+ key: 'fonts',
+})
+
+export const fontSize = style({
+ prop: 'fontSize',
+ key: 'fontSizes',
+ transformValue: scale([12, 14, 16, 20, 24, 32, 48, 64, 72]),
+})
+
+export const lineHeight = style({
+ prop: 'lineHeight',
+ key: 'lineHeights',
+})
+
+export const fontWeight = style({
+ prop: 'fontWeight',
+ key: 'fontWeights',
+})
+
+export const textAlign = style({
+ prop: 'textAlign',
+})
+
+export const letterSpacing = style({
+ prop: 'letterSpacing',
+ key: 'letterSpacings',
+ transformValue: px,
+})
+
+export const textColor = style({
+ prop: 'color',
+ key: 'colors',
+})
+
+export const textTransform = style({
+ prop: 'textTransform',
+})
+
+export const typography = compose(
+ fontFamily,
+ fontSize,
+ lineHeight,
+ fontWeight,
+ textAlign,
+ letterSpacing,
+ textColor,
+ textTransform
+)
diff --git a/src/unit.js b/src/unit.js
new file mode 100644
index 000000000..9120bdcd8
--- /dev/null
+++ b/src/unit.js
@@ -0,0 +1,23 @@
+import { num, negative } from './util'
+
+const DEFAULT_SPACE = [0, 4, 8, 16, 32, 64, 128, 256, 512]
+
+export const unit = unit => value => (num(value) ? `${value}${unit}` : value)
+export const px = unit('px')
+export const percent = n => (!num(n) || n > 1 ? px(n) : `${n * 100}%`)
+
+export const scale = (defaultVariants = DEFAULT_SPACE) => (
+ transformedValue,
+ { rawValue, variants = defaultVariants }
+) => {
+ if (!num(rawValue)) {
+ return variants[rawValue] || rawValue
+ }
+ const abs = Math.abs(rawValue)
+ const neg = negative(rawValue)
+ const value = variants[abs] || abs
+ if (!num(value)) {
+ return neg ? `-${value}` : value
+ }
+ return value * (neg ? -1 : 1)
+}
diff --git a/src/util.js b/src/util.js
new file mode 100644
index 000000000..2c2d90d59
--- /dev/null
+++ b/src/util.js
@@ -0,0 +1,33 @@
+import deepmerge from 'deepmerge' // < 1kb payload overhead when lodash/merge is > 3kb.
+
+export const is = n => n !== undefined && n !== null
+export const num = n => typeof n === 'number' && !Number.isNaN(n)
+export const string = n => typeof n === 'string' && n !== ''
+export const obj = n => typeof n === 'object' && n !== null
+export const func = n => typeof n === 'function'
+export const negative = n => n < 0
+
+export const get = (obj, path) =>
+ String(path)
+ .split('.')
+ .reduce((a, b) => (a && is(a[b]) ? a[b] : undefined), obj)
+
+export function merge(acc, item) {
+ if (!item) {
+ return acc
+ }
+
+ return deepmerge(acc, item, {
+ clone: false, // No need to clone deep, it's way faster.
+ })
+}
+
+export const assign = (target, source) => {
+ const keys = Object.keys(source || {})
+ const totalKeys = keys.length
+ for (let i = 0; i < totalKeys; i += 1) {
+ const key = keys[i]
+ target[key] = source[key]
+ }
+ return target
+}
diff --git a/src/variant.js b/src/variant.js
new file mode 100644
index 000000000..3371a05b7
--- /dev/null
+++ b/src/variant.js
@@ -0,0 +1,4 @@
+import { get } from './util'
+
+export const variant = ({ key, prop = 'variant' }) => props =>
+ get(props.theme, [key, props[prop]].join('.')) || null
diff --git a/src/variants.js b/src/variants.js
new file mode 100644
index 000000000..02fd1abc1
--- /dev/null
+++ b/src/variants.js
@@ -0,0 +1,5 @@
+import { variant } from './variant'
+
+export const buttonStyle = variant({ key: 'buttons' })
+export const textStyle = variant({ key: 'textStyles', prop: 'textStyle' })
+export const colorStyle = variant({ key: 'colorStyles', prop: 'colors' })
diff --git a/test/index.js b/test/index.js
index b1e48c53c..7790321db 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,16 +1,5 @@
import test from 'ava'
-import {
- style,
- get,
- themeGet,
- is,
- num,
- px,
- compose,
- variant,
- cloneFunction,
- mapProps,
-} from '../src'
+import { style, compose, variant } from '../src/index2'
const width = style({
prop: 'width',
@@ -63,16 +52,18 @@ test('handles aliased props', t => {
})
})
-test('long form prop trumps aliased props', t => {
- const style = backgroundColor({
- theme,
- backgroundColor: 'black',
- bg: 'blue',
- })
- t.deepEqual(style, {
- backgroundColor: '#111',
- })
-})
+// Impossible to ensure, due to perf issues
+
+// test('long form prop trumps aliased props', t => {
+// const style = backgroundColor({
+// theme,
+// backgroundColor: 'black',
+// bg: 'blue',
+// })
+// t.deepEqual(style, {
+// backgroundColor: '#111',
+// })
+// })
test('returns null', t => {
const style = color({})
@@ -88,98 +79,116 @@ test('returns an array of responsive style objects', t => {
const style = width({
width: ['100%', '50%'],
})
- t.deepEqual(style, [
- { width: '100%' },
- { '@media screen and (min-width: 40em)': { width: '50%' } },
- ])
+ t.deepEqual(style, {
+ width: '100%',
+ '@media screen and (min-width: 40em)': { width: '50%' },
+ })
})
test('returns an array of responsive style objects for all breakpoints', t => {
const style = width({
width: ['100%', '75%', '50%', '33%', '25%'],
})
- t.deepEqual(style, [
- { width: '100%' },
- { '@media screen and (min-width: 40em)': { width: '75%' } },
- { '@media screen and (min-width: 52em)': { width: '50%' } },
- { '@media screen and (min-width: 64em)': { width: '33%' } },
- ])
+ t.deepEqual(style, {
+ width: '100%',
+ '@media screen and (min-width: 40em)': { width: '75%' },
+ '@media screen and (min-width: 52em)': { width: '50%' },
+ '@media screen and (min-width: 64em)': { width: '33%' },
+ })
})
test('skips undefined responsive values', t => {
const style = width({
width: ['100%', , '50%'],
})
- t.deepEqual(style, [
- { width: '100%' },
- { '@media screen and (min-width: 52em)': { width: '50%' } },
- ])
+ t.deepEqual(style, {
+ width: '100%',
+ '@media screen and (min-width: 52em)': { width: '50%' },
+ })
})
+// It is more strict now, see next test
+
+// test('parses object values', t => {
+// const style = width({
+// width: {
+// _: '100%',
+// 2: '50%',
+// },
+// })
+// t.deepEqual(style, {
+// width: '100%',
+// '@media screen and (min-width: 64em)': { width: '50%' },
+// })
+// })
+
test('parses object values', t => {
const style = width({
width: {
_: '100%',
- 2: '50%',
+ 1: '50%',
},
})
- t.deepEqual(style, [
- { width: '100%' },
- { '@media screen and (min-width: 64em)': { width: '50%' } },
- ])
-})
-
-test('get returns a value', t => {
- const a = get({ blue: '#0cf' }, 'blue')
- t.is(a, '#0cf')
-})
-
-test('get returns the last argument if no value is found', t => {
- const a = get(
- {
- blue: '#0cf',
- },
- 'green',
- '#0f0'
- )
- t.is(a, '#0f0')
-})
-
-test('get returns 0', t => {
- const a = get({}, 0)
- const b = get({ space: [0, 4] }, 0)
- t.is(a, 0)
- t.is(b, 0)
-})
-
-test('get returns deeply nested values', t => {
- const a = get(
- {
- hi: {
- hello: {
- beep: 'boop',
- },
- },
- },
- 'hi.hello.beep'
- )
- t.is(a, 'boop')
-})
-
-test('themeGet returns values from the theme', t => {
- const a = themeGet('colors.blue')({ theme })
- t.is(a, '#07c')
+ t.deepEqual(style, {
+ '@media screen and (min-width: 40em)': { width: '50%' },
+ })
})
-test('themeGet does not throw when value doesnt exist', t => {
- const a = themeGet('colors.blue.5')({ theme })
- t.is(a, null)
-})
-
-test('themeGet accepts a fallback', t => {
- const a = themeGet('colors.lightblue', '#0cf')({ theme })
- t.is(a, '#0cf')
-})
+// We will not export "get" anymore
+
+// test('get returns a value', t => {
+// const a = get({ blue: '#0cf' }, 'blue')
+// t.is(a, '#0cf')
+// })
+
+// test('get returns the last argument if no value is found', t => {
+// const a = get(
+// {
+// blue: '#0cf',
+// },
+// 'green',
+// '#0f0'
+// )
+// t.is(a, '#0f0')
+// })
+
+// test('get returns 0', t => {
+// const a = get({}, 0)
+// const b = get({ space: [0, 4] }, 0)
+// t.is(a, 0)
+// t.is(b, 0)
+// })
+
+// test('get returns deeply nested values', t => {
+// const a = get(
+// {
+// hi: {
+// hello: {
+// beep: 'boop',
+// },
+// },
+// },
+// 'hi.hello.beep'
+// )
+// t.is(a, 'boop')
+// })
+
+// We will not export "themeGet" anymore
+
+// test('themeGet returns values from the theme', t => {
+// const a = themeGet('colors.blue')({ theme })
+// t.is(a, '#07c')
+// })
+
+// test('themeGet does not throw when value doesnt exist', t => {
+// const a = themeGet('colors.blue.5')({ theme })
+// t.is(a, null)
+// })
+
+// test('themeGet accepts a fallback', t => {
+// const a = themeGet('colors.lightblue', '#0cf')({ theme })
+// t.is(a, '#0cf')
+// })
test('compose combines style functions', t => {
const colors = compose(
@@ -191,37 +200,43 @@ test('compose combines style functions', t => {
bg: 'black',
})
t.is(typeof colors, 'function')
- t.deepEqual(styles, [{ color: 'tomato' }, { backgroundColor: 'black' }])
+ t.deepEqual(styles, { color: 'tomato', backgroundColor: 'black' })
})
-test('num returns true for numbers', t => {
- const isNumber = num(0)
- t.true(isNumber)
-})
+// We will not export "num" anymore
-test('num returns false for non-numbers', t => {
- const isNumber = num(null)
- t.false(isNumber)
-})
+// test('num returns true for numbers', t => {
+// const isNumber = num(0)
+// t.true(isNumber)
+// })
-test('is returns true for truthy values', t => {
- const isValue = is(0)
- t.true(isValue)
-})
+// test('num returns false for non-numbers', t => {
+// const isNumber = num(null)
+// t.false(isNumber)
+// })
-test('is returns false for falsey values', t => {
- const a = is(null)
- const b = is(undefined)
- t.false(a)
- t.false(b)
-})
+// We will not export "is" anymore
-test('cloneFunction creates a new function', t => {
- const func = () => 'hi'
- const b = cloneFunction(func)
- t.false(func === b)
- t.is(b(), 'hi')
-})
+// test('is returns true for truthy values', t => {
+// const isValue = is(0)
+// t.true(isValue)
+// })
+
+// test('is returns false for falsey values', t => {
+// const a = is(null)
+// const b = is(undefined)
+// t.false(a)
+// t.false(b)
+// })
+
+// We will not export "cloneFunction" anymore
+
+// test('cloneFunction creates a new function', t => {
+// const func = () => 'hi'
+// const b = cloneFunction(func)
+// t.false(func === b)
+// t.is(b(), 'hi')
+// })
test('variant returns style objects from theme', t => {
const buttons = variant({ key: 'buttons' })
@@ -263,17 +278,15 @@ test('variant prop can be customized', t => {
test('array values longer than breakpoints does not reset returned style object', t => {
const a = width({
- width: [
- '100%',,,,,'50%', '25%',
- ]
+ width: ['100%', , , , , '50%', '25%'],
})
- t.deepEqual(a, [
- { width: '100%' },
- ])
+ t.deepEqual(a, { width: '100%' })
})
-test('mapProps copies propTypes', t => {
- const margin = style({ prop: 'margin' })
- const func = mapProps(props => props)(margin)
- t.is(typeof func.propTypes, 'object')
-})
+// No longer relevant
+
+// test('mapProps copies propTypes', t => {
+// const margin = style({ prop: 'margin' })
+// const func = mapProps(props => props)(margin)
+// t.is(typeof func.propTypes, 'object')
+// })
diff --git a/test/styles.js b/test/styles.js
index d90129bd7..6e39397c0 100644
--- a/test/styles.js
+++ b/test/styles.js
@@ -11,7 +11,7 @@ import {
textStyle,
colorStyle,
borders,
-} from '../src'
+} from '../src/index2'
const theme = {
colors: {
@@ -22,7 +22,7 @@ const theme = {
test('returns color values from theme', t => {
const a = color({ theme, color: 'blue', bg: 'black' })
- t.deepEqual(a, [{ color: '#07c' }, { backgroundColor: '#111' }])
+ t.deepEqual(a, { color: '#07c', backgroundColor: '#111' })
})
test('returns raw color values', t => {
@@ -31,25 +31,29 @@ test('returns raw color values', t => {
color: 'inherit',
bg: 'tomato',
})
- t.deepEqual(a, [{ color: 'inherit' }, { backgroundColor: 'tomato' }])
+ t.deepEqual(a, { color: 'inherit', backgroundColor: 'tomato' })
})
-test('backgroundColor prop overrides bg prop', t => {
- const a = color({
- backgroundColor: 'tomato',
- bg: 'blue',
- })
- t.deepEqual(a, [{ backgroundColor: 'tomato' }])
-})
+// Impossible to ensure, due to perf issues
-test('returns a pixel font-size', t => {
- const a = fontSize({ fontSize: 48 })
- t.deepEqual(a, { fontSize: '48px' })
-})
+// test('backgroundColor prop overrides bg prop', t => {
+// const a = color({
+// backgroundColor: 'tomato',
+// bg: 'blue',
+// })
+// t.deepEqual(a, [{ backgroundColor: 'tomato' }])
+// })
+
+// Useless, font-size default to pixels
+
+// test('returns a pixel font-size', t => {
+// const a = fontSize({ fontSize: 48 })
+// t.deepEqual(a, { fontSize: '48px' })
+// })
test('uses a default font-size scale', t => {
const a = fontSize({ fontSize: 2 })
- t.deepEqual(a, { fontSize: '16px' })
+ t.deepEqual(a, { fontSize: 16 })
})
test('returns a string font-size', t => {
@@ -76,22 +80,22 @@ test('returns an array of style objects', t => {
const styles = space({
m: '4px',
})
- t.deepEqual(styles, [{ margin: '4px' }])
+ t.deepEqual(styles, { margin: '4px' })
})
test('returns 0 values', t => {
const styles = space({ m: 0 })
- t.deepEqual(styles, [{ margin: 0 }])
+ t.deepEqual(styles, { margin: 0 })
})
test('returns negative pixel values', t => {
const styles = space({ m: -2 })
- t.deepEqual(styles, [{ margin: '-8px' }])
+ t.deepEqual(styles, { margin: -8 })
})
test('returns negative em values', t => {
const styles = space({ m: '-16em' })
- t.deepEqual(styles, [{ margin: '-16em' }])
+ t.deepEqual(styles, { margin: '-16em' })
})
test('returns negative theme values', t => {
@@ -101,7 +105,7 @@ test('returns negative theme values', t => {
},
m: -2,
})
- t.deepEqual(styles, [{ margin: '-8px' }])
+ t.deepEqual(styles, { margin: -8 })
})
test('returns positive theme values', t => {
@@ -111,27 +115,25 @@ test('returns positive theme values', t => {
},
m: 2,
})
- t.deepEqual(styles, [{ margin: '2em' }])
+ t.deepEqual(styles, { margin: '2em' })
})
test('returns responsive values', t => {
const styles = space({
m: [0, 2, 3],
})
- t.deepEqual(styles, [
- [
- { margin: 0 },
- { '@media screen and (min-width: 40em)': { margin: '8px' } },
- { '@media screen and (min-width: 52em)': { margin: '16px' } },
- ],
- ])
+ t.deepEqual(styles, {
+ margin: 0,
+ '@media screen and (min-width: 40em)': { margin: 8 },
+ '@media screen and (min-width: 52em)': { margin: 16 },
+ })
})
test('returns aliased values', t => {
const styles = space({
px: 2,
})
- t.deepEqual(styles, [{ paddingLeft: '8px' }, { paddingRight: '8px' }])
+ t.deepEqual(styles, { paddingLeft: 8, paddingRight: 8 })
})
test('returns string values from theme', t => {
@@ -141,7 +143,7 @@ test('returns string values from theme', t => {
},
padding: 1,
})
- t.deepEqual(styles, [{ padding: '1em' }])
+ t.deepEqual(styles, { padding: '1em' })
})
test('returns negative string values from theme', t => {
@@ -151,7 +153,7 @@ test('returns negative string values from theme', t => {
},
margin: -1,
})
- t.deepEqual(styles, [{ margin: '-1em' }])
+ t.deepEqual(styles, { margin: '-1em' })
})
test('returns values from theme object', t => {
@@ -162,73 +164,79 @@ test('returns values from theme object', t => {
margin: 'sm',
})
- t.deepEqual(styles, [{ margin: '1px' }])
+ t.deepEqual(styles, { margin: 1 })
})
test('pl prop sets paddingLeft', t => {
const styles = space({ pl: 2 })
- t.deepEqual(styles, [{ paddingLeft: '8px' }])
+ t.deepEqual(styles, { paddingLeft: 8 })
})
test('pl prop sets paddingLeft 0', t => {
const styles = space({ pl: 0 })
- t.deepEqual(styles, [{ paddingLeft: 0 }])
+ t.deepEqual(styles, { paddingLeft: 0 })
})
+// The order of props matter
test('px prop overrides pl prop', t => {
const styles = space({
pl: 1,
px: 2,
})
- t.deepEqual(styles, [{ paddingLeft: '8px' }, { paddingRight: '8px' }])
+ t.deepEqual(styles, { paddingLeft: 8, paddingRight: 8 })
})
+// The order of props matter
test('py prop overrides pb prop', t => {
const styles = space({
pb: 1,
py: 2,
})
- t.deepEqual(styles, [{ paddingTop: '8px' }, { paddingBottom: '8px' }])
+ t.deepEqual(styles, { paddingTop: 8, paddingBottom: 8 })
})
+// The order of props matter
test('mx prop overrides mr prop', t => {
const styles = space({
mr: 1,
mx: 2,
})
- t.deepEqual(styles, [{ marginLeft: '8px' }, { marginRight: '8px' }])
+ t.deepEqual(styles, { marginLeft: 8, marginRight: 8 })
})
+// The order of props matter
test('my prop overrides mt prop', t => {
const styles = space({
mt: 1,
my: 2,
})
- t.deepEqual(styles, [{ marginTop: '8px' }, { marginBottom: '8px' }])
+ t.deepEqual(styles, { marginTop: 8, marginBottom: 8 })
})
+// The order of props matter
test('margin overrides m prop', t => {
const styles = space({
m: 1,
margin: 2,
})
- t.deepEqual(styles, [{ margin: '8px' }])
+ t.deepEqual(styles, { margin: 8 })
})
-test('space includes propTypes', t => {
- const { propTypes } = space
- t.is(typeof propTypes, 'object')
- t.is(typeof propTypes.m, 'function')
-})
+// PropTypes are no longer included
+// test('space includes propTypes', t => {
+// const { propTypes } = space
+// t.is(typeof propTypes, 'object')
+// t.is(typeof propTypes.m, 'function')
+// })
test('size returns width and height', t => {
const styles = size({
size: 4,
})
- t.deepEqual(styles, [{ width: '4px' }, { height: '4px' }])
+ t.deepEqual(styles, { width: '4px', height: '4px' })
})
-// grid
+// // grid
test('gridGap returns a scalar style', t => {
const a = gridGap({
theme: {
@@ -237,7 +245,7 @@ test('gridGap returns a scalar style', t => {
gridGap: 3,
})
- t.deepEqual(a, { gridGap: '8px' })
+ t.deepEqual(a, { gridGap: 8 })
})
test('gridGap uses the default scale', t => {
@@ -246,7 +254,7 @@ test('gridGap uses the default scale', t => {
gridGap: 2,
})
- t.deepEqual(a, { gridGap: '8px' })
+ t.deepEqual(a, { gridGap: 8 })
})
test('gridRowGap returns a scalar style', t => {
@@ -257,7 +265,7 @@ test('gridRowGap returns a scalar style', t => {
gridRowGap: 3,
})
- t.deepEqual(a, { gridRowGap: '8px' })
+ t.deepEqual(a, { gridRowGap: 8 })
})
test('gridRowGap uses the default scale', t => {
@@ -266,7 +274,7 @@ test('gridRowGap uses the default scale', t => {
gridRowGap: 2,
})
- t.deepEqual(a, { gridRowGap: '8px' })
+ t.deepEqual(a, { gridRowGap: 8 })
})
test('gridColumnGap returns a scalar style', t => {
@@ -277,7 +285,7 @@ test('gridColumnGap returns a scalar style', t => {
gridColumnGap: 3,
})
- t.deepEqual(a, { gridColumnGap: '8px' })
+ t.deepEqual(a, { gridColumnGap: 8 })
})
test('gridColumnGap uses the default scale', t => {
@@ -286,7 +294,7 @@ test('gridColumnGap uses the default scale', t => {
gridColumnGap: 2,
})
- t.deepEqual(a, { gridColumnGap: '8px' })
+ t.deepEqual(a, { gridColumnGap: 8 })
})
test('textStyle prop returns theme.textStyles object', t => {
@@ -332,10 +340,10 @@ test('borders prop returns correct sequence', t => {
borderStyle: 'dashed',
borderColor: 'red',
})
- t.deepEqual(a, [
- { borderBottom: '1px solid' },
- { borderWidth: '2px' },
- { borderStyle: 'dashed' },
- { borderColor: 'red' },
- ])
+ t.deepEqual(a, {
+ borderBottom: '1px solid',
+ borderWidth: '2px',
+ borderStyle: 'dashed',
+ borderColor: 'red',
+ })
})