From 606a04b74b336eddf0ed73feefd7e5b8da6eccab Mon Sep 17 00:00:00 2001 From: Dennis Walsh Date: Mon, 24 Jul 2017 09:21:57 -0400 Subject: [PATCH] feat(Color): Returns most readable color, black or white Uses W3C convention to determine if black or white is more readable (greater luminosity difference) --- .documentation.json | 1 + README.md | 1 + docs/assets/polished.js | 428 ++++++++---------- docs/docs/index.html | 239 +++++++--- src/color/readableColor.js | 46 ++ .../__snapshots__/readableColor.test.js.snap | 41 ++ src/color/test/readableColor.test.js | 71 +++ 7 files changed, 533 insertions(+), 294 deletions(-) create mode 100644 src/color/readableColor.js create mode 100644 src/color/test/__snapshots__/readableColor.test.js.snap create mode 100644 src/color/test/readableColor.test.js diff --git a/.documentation.json b/.documentation.json index cdf1e55b..405c55b2 100644 --- a/.documentation.json +++ b/.documentation.json @@ -46,6 +46,7 @@ "opacify", "parseToHsl", "parseToRgb", + "readableColor", "rgb", "rgba", "saturate", diff --git a/README.md b/README.md index 8c5c7528..39eb8755 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ In the documentation you will see examples using [object spread properties](http
  • opacify
  • parseToHsl
  • parseToRgb
  • +
  • readableColor
  • rgb
  • rgba
  • saturate
  • diff --git a/docs/assets/polished.js b/docs/assets/polished.js index b9e0a91f..efddb5f3 100644 --- a/docs/assets/polished.js +++ b/docs/assets/polished.js @@ -65,7 +65,6 @@ function directionalProperty(property) { values[_key - 1] = arguments[_key]; } - // prettier-ignore // $FlowIgnoreNextLine doesn't understand destructuring with chained defaults. var firstValue = values[0], _values$ = values[1], @@ -438,9 +437,9 @@ function clearFix() { var pseudoSelector = parent + '::after'; return defineProperty({}, pseudoSelector, { - clear: 'both', - content: '""', - display: 'table' + 'clear': 'both', + 'content': '""', + 'display': 'table' }); } @@ -476,12 +475,12 @@ function ellipsis() { var width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '100%'; return { - display: 'inline-block', - maxWidth: width, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - wordWrap: 'normal' + 'display': 'inline-block', + 'maxWidth': width, + 'overflow': 'hidden', + 'textOverflow': 'ellipsis', + 'whiteSpace': 'nowrap', + 'wordWrap': 'normal' }; } @@ -506,9 +505,7 @@ function generateLocalReferences(localFonts) { function generateSources(fontFilePath, localFonts, fileFormats) { var fontReferences = []; if (localFonts) fontReferences.push(generateLocalReferences(localFonts)); - if (fontFilePath) { - fontReferences.push(generateFileReferences(fontFilePath, fileFormats)); - } + if (fontFilePath) fontReferences.push(generateFileReferences(fontFilePath, fileFormats)); return fontReferences.join(', '); } @@ -554,15 +551,9 @@ function fontFace(_ref) { // Error Handling if (!fontFamily) throw new Error('fontFace expects a name of a font-family.'); - if (!fontFilePath && !localFonts) { - throw new Error('fontFace expects either the path to the font file(s) or a name of a local copy.'); - } - if (localFonts && !Array.isArray(localFonts)) { - throw new Error('fontFace expects localFonts to be an array.'); - } - if (!Array.isArray(fileFormats)) { - throw new Error('fontFace expects fileFormats to be an array.'); - } + if (!fontFilePath && !localFonts) throw new Error('fontFace expects either the path to the font file(s) or a name of a local copy.'); + if (localFonts && !Array.isArray(localFonts)) throw new Error('fontFace expects localFonts to be an array.'); + if (!Array.isArray(fileFormats)) throw new Error('fontFace expects fileFormats to be an array.'); var fontFaceDeclaration = { '@font-face': { @@ -933,9 +924,7 @@ function radialGradient(_ref) { position = _ref.position, shape = _ref.shape; - if (!colorStops || colorStops.length < 2) { - throw new Error('radialGradient requries at least 2 color-stops to properly render.'); - } + if (!colorStops || colorStops.length < 2) throw new Error('radialGradient requries at least 2 color-stops to properly render.'); return { backgroundColor: fallback || parseFallback(colorStops), backgroundImage: constructGradientValue(_templateObject, position, shape, extent, colorStops.join(', ')) @@ -1034,32 +1023,32 @@ function selection(styles) { /* eslint-disable key-spacing */ var functionsMap = { - easeInBack: 'cubic-bezier(0.600, -0.280, 0.735, 0.045)', - easeInCirc: 'cubic-bezier(0.600, 0.040, 0.980, 0.335)', - easeInCubic: 'cubic-bezier(0.550, 0.055, 0.675, 0.190)', - easeInExpo: 'cubic-bezier(0.950, 0.050, 0.795, 0.035)', - easeInQuad: 'cubic-bezier(0.550, 0.085, 0.680, 0.530)', - easeInQuart: 'cubic-bezier(0.895, 0.030, 0.685, 0.220)', - easeInQuint: 'cubic-bezier(0.755, 0.050, 0.855, 0.060)', - easeInSine: 'cubic-bezier(0.470, 0.000, 0.745, 0.715)', - - easeOutBack: 'cubic-bezier(0.175, 0.885, 0.320, 1.275)', - easeOutCubic: 'cubic-bezier(0.215, 0.610, 0.355, 1.000)', - easeOutCirc: 'cubic-bezier(0.075, 0.820, 0.165, 1.000)', - easeOutExpo: 'cubic-bezier(0.190, 1.000, 0.220, 1.000)', - easeOutQuad: 'cubic-bezier(0.250, 0.460, 0.450, 0.940)', - easeOutQuart: 'cubic-bezier(0.165, 0.840, 0.440, 1.000)', - easeOutQuint: 'cubic-bezier(0.230, 1.000, 0.320, 1.000)', - easeOutSine: 'cubic-bezier(0.390, 0.575, 0.565, 1.000)', - - easeInOutBack: 'cubic-bezier(0.680, -0.550, 0.265, 1.550)', - easeInOutCirc: 'cubic-bezier(0.785, 0.135, 0.150, 0.860)', - easeInOutCubic: 'cubic-bezier(0.645, 0.045, 0.355, 1.000)', - easeInOutExpo: 'cubic-bezier(1.000, 0.000, 0.000, 1.000)', - easeInOutQuad: 'cubic-bezier(0.455, 0.030, 0.515, 0.955)', - easeInOutQuart: 'cubic-bezier(0.770, 0.000, 0.175, 1.000)', - easeInOutQuint: 'cubic-bezier(0.860, 0.000, 0.070, 1.000)', - easeInOutSine: 'cubic-bezier(0.445, 0.050, 0.550, 0.950)' + 'easeInBack': 'cubic-bezier(0.600, -0.280, 0.735, 0.045)', + 'easeInCirc': 'cubic-bezier(0.600, 0.040, 0.980, 0.335)', + 'easeInCubic': 'cubic-bezier(0.550, 0.055, 0.675, 0.190)', + 'easeInExpo': 'cubic-bezier(0.950, 0.050, 0.795, 0.035)', + 'easeInQuad': 'cubic-bezier(0.550, 0.085, 0.680, 0.530)', + 'easeInQuart': 'cubic-bezier(0.895, 0.030, 0.685, 0.220)', + 'easeInQuint': 'cubic-bezier(0.755, 0.050, 0.855, 0.060)', + 'easeInSine': 'cubic-bezier(0.470, 0.000, 0.745, 0.715)', + + 'easeOutBack': 'cubic-bezier(0.175, 0.885, 0.320, 1.275)', + 'easeOutCubic': 'cubic-bezier(0.215, 0.610, 0.355, 1.000)', + 'easeOutCirc': 'cubic-bezier(0.075, 0.820, 0.165, 1.000)', + 'easeOutExpo': 'cubic-bezier(0.190, 1.000, 0.220, 1.000)', + 'easeOutQuad': 'cubic-bezier(0.250, 0.460, 0.450, 0.940)', + 'easeOutQuart': 'cubic-bezier(0.165, 0.840, 0.440, 1.000)', + 'easeOutQuint': 'cubic-bezier(0.230, 1.000, 0.320, 1.000)', + 'easeOutSine': 'cubic-bezier(0.390, 0.575, 0.565, 1.000)', + + 'easeInOutBack': 'cubic-bezier(0.680, -0.550, 0.265, 1.550)', + 'easeInOutCirc': 'cubic-bezier(0.785, 0.135, 0.150, 0.860)', + 'easeInOutCubic': 'cubic-bezier(0.645, 0.045, 0.355, 1.000)', + 'easeInOutExpo': 'cubic-bezier(1.000, 0.000, 0.000, 1.000)', + 'easeInOutQuad': 'cubic-bezier(0.455, 0.030, 0.515, 0.955)', + 'easeInOutQuart': 'cubic-bezier(0.770, 0.000, 0.175, 1.000)', + 'easeInOutQuint': 'cubic-bezier(0.860, 0.000, 0.070, 1.000)', + 'easeInOutSine': 'cubic-bezier(0.445, 0.050, 0.550, 0.950)' }; /* eslint-enable key-spacing */ @@ -1106,7 +1095,7 @@ var getBorderWidth = function getBorderWidth(pointingDirection, height, width) { return height / 2 + 'px 0 ' + height / 2 + 'px ' + width + 'px'; default: - throw new Error("Passed invalid argument to triangle, please pass correct poitingDirection e.g. 'right'."); + throw new Error('Passed invalid argument to triangle, please pass correct poitingDirection e.g. \'right\'.'); } }; @@ -1262,154 +1251,154 @@ function hslToRgb(hue, saturation, lightness) { // var namedColorMap = { - aliceblue: 'f0f8ff', - antiquewhite: 'faebd7', - aqua: '00ffff', - aquamarine: '7fffd4', - azure: 'f0ffff', - beige: 'f5f5dc', - bisque: 'ffe4c4', - black: '000', - blanchedalmond: 'ffebcd', - blue: '0000ff', - blueviolet: '8a2be2', - brown: 'a52a2a', - burlywood: 'deb887', - cadetblue: '5f9ea0', - chartreuse: '7fff00', - chocolate: 'd2691e', - coral: 'ff7f50', - cornflowerblue: '6495ed', - cornsilk: 'fff8dc', - crimson: 'dc143c', - cyan: '00ffff', - darkblue: '00008b', - darkcyan: '008b8b', - darkgoldenrod: 'b8860b', - darkgray: 'a9a9a9', - darkgreen: '006400', - darkgrey: 'a9a9a9', - darkkhaki: 'bdb76b', - darkmagenta: '8b008b', - darkolivegreen: '556b2f', - darkorange: 'ff8c00', - darkorchid: '9932cc', - darkred: '8b0000', - darksalmon: 'e9967a', - darkseagreen: '8fbc8f', - darkslateblue: '483d8b', - darkslategray: '2f4f4f', - darkslategrey: '2f4f4f', - darkturquoise: '00ced1', - darkviolet: '9400d3', - deeppink: 'ff1493', - deepskyblue: '00bfff', - dimgray: '696969', - dimgrey: '696969', - dodgerblue: '1e90ff', - firebrick: 'b22222', - floralwhite: 'fffaf0', - forestgreen: '228b22', - fuchsia: 'ff00ff', - gainsboro: 'dcdcdc', - ghostwhite: 'f8f8ff', - gold: 'ffd700', - goldenrod: 'daa520', - gray: '808080', - green: '008000', - greenyellow: 'adff2f', - grey: '808080', - honeydew: 'f0fff0', - hotpink: 'ff69b4', - indianred: 'cd5c5c', - indigo: '4b0082', - ivory: 'fffff0', - khaki: 'f0e68c', - lavender: 'e6e6fa', - lavenderblush: 'fff0f5', - lawngreen: '7cfc00', - lemonchiffon: 'fffacd', - lightblue: 'add8e6', - lightcoral: 'f08080', - lightcyan: 'e0ffff', - lightgoldenrodyellow: 'fafad2', - lightgray: 'd3d3d3', - lightgreen: '90ee90', - lightgrey: 'd3d3d3', - lightpink: 'ffb6c1', - lightsalmon: 'ffa07a', - lightseagreen: '20b2aa', - lightskyblue: '87cefa', - lightslategray: '789', - lightslategrey: '789', - lightsteelblue: 'b0c4de', - lightyellow: 'ffffe0', - lime: '0f0', - limegreen: '32cd32', - linen: 'faf0e6', - magenta: 'f0f', - maroon: '800000', - mediumaquamarine: '66cdaa', - mediumblue: '0000cd', - mediumorchid: 'ba55d3', - mediumpurple: '9370db', - mediumseagreen: '3cb371', - mediumslateblue: '7b68ee', - mediumspringgreen: '00fa9a', - mediumturquoise: '48d1cc', - mediumvioletred: 'c71585', - midnightblue: '191970', - mintcream: 'f5fffa', - mistyrose: 'ffe4e1', - moccasin: 'ffe4b5', - navajowhite: 'ffdead', - navy: '000080', - oldlace: 'fdf5e6', - olive: '808000', - olivedrab: '6b8e23', - orange: 'ffa500', - orangered: 'ff4500', - orchid: 'da70d6', - palegoldenrod: 'eee8aa', - palegreen: '98fb98', - paleturquoise: 'afeeee', - palevioletred: 'db7093', - papayawhip: 'ffefd5', - peachpuff: 'ffdab9', - peru: 'cd853f', - pink: 'ffc0cb', - plum: 'dda0dd', - powderblue: 'b0e0e6', - purple: '800080', - rebeccapurple: '639', - red: 'f00', - rosybrown: 'bc8f8f', - royalblue: '4169e1', - saddlebrown: '8b4513', - salmon: 'fa8072', - sandybrown: 'f4a460', - seagreen: '2e8b57', - seashell: 'fff5ee', - sienna: 'a0522d', - silver: 'c0c0c0', - skyblue: '87ceeb', - slateblue: '6a5acd', - slategray: '708090', - slategrey: '708090', - snow: 'fffafa', - springgreen: '00ff7f', - steelblue: '4682b4', - tan: 'd2b48c', - teal: '008080', - thistle: 'd8bfd8', - tomato: 'ff6347', - turquoise: '40e0d0', - violet: 'ee82ee', - wheat: 'f5deb3', - white: 'fff', - whitesmoke: 'f5f5f5', - yellow: 'ff0', - yellowgreen: '9acd32' + 'aliceblue': 'f0f8ff', + 'antiquewhite': 'faebd7', + 'aqua': '00ffff', + 'aquamarine': '7fffd4', + 'azure': 'f0ffff', + 'beige': 'f5f5dc', + 'bisque': 'ffe4c4', + 'black': '000', + 'blanchedalmond': 'ffebcd', + 'blue': '0000ff', + 'blueviolet': '8a2be2', + 'brown': 'a52a2a', + 'burlywood': 'deb887', + 'cadetblue': '5f9ea0', + 'chartreuse': '7fff00', + 'chocolate': 'd2691e', + 'coral': 'ff7f50', + 'cornflowerblue': '6495ed', + 'cornsilk': 'fff8dc', + 'crimson': 'dc143c', + 'cyan': '00ffff', + 'darkblue': '00008b', + 'darkcyan': '008b8b', + 'darkgoldenrod': 'b8860b', + 'darkgray': 'a9a9a9', + 'darkgreen': '006400', + 'darkgrey': 'a9a9a9', + 'darkkhaki': 'bdb76b', + 'darkmagenta': '8b008b', + 'darkolivegreen': '556b2f', + 'darkorange': 'ff8c00', + 'darkorchid': '9932cc', + 'darkred': '8b0000', + 'darksalmon': 'e9967a', + 'darkseagreen': '8fbc8f', + 'darkslateblue': '483d8b', + 'darkslategray': '2f4f4f', + 'darkslategrey': '2f4f4f', + 'darkturquoise': '00ced1', + 'darkviolet': '9400d3', + 'deeppink': 'ff1493', + 'deepskyblue': '00bfff', + 'dimgray': '696969', + 'dimgrey': '696969', + 'dodgerblue': '1e90ff', + 'firebrick': 'b22222', + 'floralwhite': 'fffaf0', + 'forestgreen': '228b22', + 'fuchsia': 'ff00ff', + 'gainsboro': 'dcdcdc', + 'ghostwhite': 'f8f8ff', + 'gold': 'ffd700', + 'goldenrod': 'daa520', + 'gray': '808080', + 'green': '008000', + 'greenyellow': 'adff2f', + 'grey': '808080', + 'honeydew': 'f0fff0', + 'hotpink': 'ff69b4', + 'indianred': 'cd5c5c', + 'indigo': '4b0082', + 'ivory': 'fffff0', + 'khaki': 'f0e68c', + 'lavender': 'e6e6fa', + 'lavenderblush': 'fff0f5', + 'lawngreen': '7cfc00', + 'lemonchiffon': 'fffacd', + 'lightblue': 'add8e6', + 'lightcoral': 'f08080', + 'lightcyan': 'e0ffff', + 'lightgoldenrodyellow': 'fafad2', + 'lightgray': 'd3d3d3', + 'lightgreen': '90ee90', + 'lightgrey': 'd3d3d3', + 'lightpink': 'ffb6c1', + 'lightsalmon': 'ffa07a', + 'lightseagreen': '20b2aa', + 'lightskyblue': '87cefa', + 'lightslategray': '789', + 'lightslategrey': '789', + 'lightsteelblue': 'b0c4de', + 'lightyellow': 'ffffe0', + 'lime': '0f0', + 'limegreen': '32cd32', + 'linen': 'faf0e6', + 'magenta': 'f0f', + 'maroon': '800000', + 'mediumaquamarine': '66cdaa', + 'mediumblue': '0000cd', + 'mediumorchid': 'ba55d3', + 'mediumpurple': '9370db', + 'mediumseagreen': '3cb371', + 'mediumslateblue': '7b68ee', + 'mediumspringgreen': '00fa9a', + 'mediumturquoise': '48d1cc', + 'mediumvioletred': 'c71585', + 'midnightblue': '191970', + 'mintcream': 'f5fffa', + 'mistyrose': 'ffe4e1', + 'moccasin': 'ffe4b5', + 'navajowhite': 'ffdead', + 'navy': '000080', + 'oldlace': 'fdf5e6', + 'olive': '808000', + 'olivedrab': '6b8e23', + 'orange': 'ffa500', + 'orangered': 'ff4500', + 'orchid': 'da70d6', + 'palegoldenrod': 'eee8aa', + 'palegreen': '98fb98', + 'paleturquoise': 'afeeee', + 'palevioletred': 'db7093', + 'papayawhip': 'ffefd5', + 'peachpuff': 'ffdab9', + 'peru': 'cd853f', + 'pink': 'ffc0cb', + 'plum': 'dda0dd', + 'powderblue': 'b0e0e6', + 'purple': '800080', + 'rebeccapurple': '639', + 'red': 'f00', + 'rosybrown': 'bc8f8f', + 'royalblue': '4169e1', + 'saddlebrown': '8b4513', + 'salmon': 'fa8072', + 'sandybrown': 'f4a460', + 'seagreen': '2e8b57', + 'seashell': 'fff5ee', + 'sienna': 'a0522d', + 'silver': 'c0c0c0', + 'skyblue': '87ceeb', + 'slateblue': '6a5acd', + 'slategray': '708090', + 'slategrey': '708090', + 'snow': 'fffafa', + 'springgreen': '00ff7f', + 'steelblue': '4682b4', + 'tan': 'd2b48c', + 'teal': '008080', + 'thistle': 'd8bfd8', + 'tomato': 'ff6347', + 'turquoise': '40e0d0', + 'violet': 'ee82ee', + 'wheat': 'f5deb3', + 'white': 'fff', + 'whitesmoke': 'f5f5f5', + 'yellow': 'ff0', + 'yellowgreen': '9acd32' }; /** @@ -1443,9 +1432,7 @@ var hslaRegex = /^hsla\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*([- * const color2 = 'hsla(210, 10%, 40%, 0.75)'; */ function parseToRgb(color) { - if (typeof color !== 'string') { - throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); - } + if (typeof color !== 'string') throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); var normalizedColor = nameToHex(color); if (normalizedColor.match(hexRegex)) { return { @@ -1505,7 +1492,7 @@ function parseToRgb(color) { alpha: parseFloat('' + hslaMatched[4]) }; } - throw new Error("Couldn't parse the color string. Please provide the color as a string in hex, rgb, rgba, hsl or hsla notation."); + throw new Error('Couldn\'t parse the color string. Please provide the color as a string in hex, rgb, rgba, hsl or hsla notation.'); } // @@ -1857,7 +1844,6 @@ function curried(f, length, acc) { }; } -// eslint-disable-next-line no-redeclare function curry(f) { // eslint-disable-line no-redeclare return curried(f, f.length, []); @@ -2382,12 +2368,8 @@ var curriedSetSaturation = /*#__PURE__*/curry(setSaturation); // eslint-disable- */ function shade(percentage, color) { - if (typeof percentage !== 'number' || percentage > 1 || percentage < -1) { - throw new Error('Passed an incorrect argument to shade, please pass a percentage less than or equal to 1 and larger than or equal to -1.'); - } - if (typeof color !== 'string') { - throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); - } + if (typeof percentage !== 'number' || percentage > 1 || percentage < -1) throw new Error('Passed an incorrect argument to shade, please pass a percentage less than or equal to 1 and larger than or equal to -1.'); + if (typeof color !== 'string') throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); return curriedMix(percentage, color, 'rgb(0, 0, 0)'); } @@ -2420,12 +2402,8 @@ var curriedShade = /*#__PURE__*/curry(shade); // eslint-disable-line spaced-comm */ function tint(percentage, color) { - if (typeof percentage !== 'number' || percentage > 1 || percentage < -1) { - throw new Error('Passed an incorrect argument to tint, please pass a percentage less than or equal to 1 and larger than or equal to -1.'); - } - if (typeof color !== 'string') { - throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); - } + if (typeof percentage !== 'number' || percentage > 1 || percentage < -1) throw new Error('Passed an incorrect argument to tint, please pass a percentage less than or equal to 1 and larger than or equal to -1.'); + if (typeof color !== 'string') throw new Error('Passed an incorrect argument to a color function, please pass a string representation of a color.'); return curriedMix(percentage, color, 'rgb(255, 255, 255)'); } @@ -2524,7 +2502,7 @@ function animation() { } var code = args.map(function (arg) { if (multiMode && !Array.isArray(arg) || !multiMode && Array.isArray(arg)) { - throw new Error("To pass multiple animations please supply them in arrays, e.g. animation(['rotate', '2s'], ['move', '1s'])\nTo pass a single animation please supply them in simple values, e.g. animation('rotate', '2s')"); + throw new Error('To pass multiple animations please supply them in arrays, e.g. animation([\'rotate\', \'2s\'], [\'move\', \'1s\'])\nTo pass a single animation please supply them in simple values, e.g. animation(\'rotate\', \'2s\')'); } if (Array.isArray(arg) && arg.length > 8) { throw new Error('The animation shorthand arrays can only have 8 elements. See the specification for more information: http://mdn.io/animation'); @@ -2534,7 +2512,7 @@ function animation() { }).join(', '); return { - animation: code + 'animation': code }; } @@ -2597,7 +2575,7 @@ function backgrounds() { } return { - background: properties.join(', ') + 'background': properties.join(', ') }; } @@ -2657,9 +2635,7 @@ function borderColor() { function borderRadius(side, radius) { var uppercaseSide = capitalizeString(side); - if (!radius || typeof radius !== 'string') { - throw new Error('borderRadius expects a radius value as a string as the second argument.'); - } + if (!radius || typeof radius !== 'string') throw new Error('borderRadius expects a radius value as a string as the second argument.'); if (uppercaseSide === 'Top' || uppercaseSide === 'Bottom') { var _ref; @@ -2753,9 +2729,7 @@ function statefulSelectors(states, template, stateMap) { if (states.length === 0) return generateSelectors(template, null); var selectors = []; for (var i = 0; i < states.length; i += 1) { - if (stateMap && stateMap.indexOf(states[i]) < 0) { - throw new Error('You passed an unsupported selector state to this method.'); - } + if (stateMap && stateMap.indexOf(states[i]) < 0) throw new Error('You passed an unsupported selector state to this method.'); selectors.push(generateSelectors(template, states[i])); } selectors = selectors.join(','); @@ -3047,7 +3021,7 @@ function transitions() { } return { - transition: properties.join(', ') + 'transition': properties.join(', ') }; } diff --git a/docs/docs/index.html b/docs/docs/index.html index 26605c53..1463d3cd 100644 --- a/docs/docs/index.html +++ b/docs/docs/index.html @@ -344,6 +344,16 @@

    +
  • + readableColor + + + +
  • + +
  • @@ -845,7 +855,7 @@

    -
    + src/mixins/clearFix.js @@ -931,7 +941,7 @@

    - + src/mixins/ellipsis.js @@ -1020,7 +1030,7 @@

    - + src/mixins/fontFace.js @@ -1182,7 +1192,7 @@

    - + src/mixins/hiDPI.js @@ -1274,7 +1284,7 @@

    - + src/mixins/hideText.js @@ -1349,7 +1359,7 @@

    - + src/mixins/normalize.js @@ -1432,7 +1442,7 @@

    - + src/mixins/placeholder.js @@ -1535,7 +1545,7 @@

    - + src/mixins/radialGradient.js @@ -1675,7 +1685,7 @@

    - + src/mixins/retinaImage.js @@ -1800,7 +1810,7 @@

    - + src/mixins/selection.js @@ -1899,7 +1909,7 @@

    - + src/mixins/timingFunctions.js @@ -1982,7 +1992,7 @@

    - + src/mixins/triangle.js @@ -2120,7 +2130,7 @@

    - + src/mixins/wordWrap.js @@ -2218,7 +2228,7 @@

    - + src/color/adjustHue.js @@ -2318,7 +2328,7 @@

    - + src/color/complement.js @@ -2408,7 +2418,7 @@

    - + src/color/darken.js @@ -2507,7 +2517,7 @@

    - + src/color/desaturate.js @@ -2607,7 +2617,7 @@

    - + src/color/grayscale.js @@ -2697,7 +2707,7 @@

    - + src/color/hsl.js @@ -2804,7 +2814,7 @@

    - + src/color/hsla.js @@ -2922,7 +2932,7 @@

    - + src/color/invert.js @@ -3013,7 +3023,7 @@

    - + src/color/lighten.js @@ -3112,7 +3122,7 @@

    - + src/color/mix.js @@ -3227,7 +3237,7 @@

    - + src/color/opacify.js @@ -3324,7 +3334,7 @@

    - + src/color/parseToHsl.js @@ -3403,7 +3413,7 @@

    - + src/color/parseToRgb.js @@ -3467,6 +3477,101 @@

    + + + + + +
    + + +
    + +

    + readableColor +

    + + + + src/color/readableColor.js + + +
    + + +

    Selects black or white for best contrast depending on the luminosity of the given color. +Follows W3C specs for readability at https://www.w3.org/TR/WCAG20-TECHS/G18.html

    + + +
    readableColor(color: string): string
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + color (string) + +
    + +
    + +
    + + + + + + +
    Returns
    + string + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  color: readableColor('#000'),
    +  color: readableColor('papayawhip'),
    +  color: readableColor('rgb(255,0,0)'),
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  color: ${readableColor('#000')};
    +  color: ${readableColor('papayawhip')};
    +  color: ${readableColor('rgb(255,0,0)')};
    +`
    +
    +// CSS in JS Output
    +
    +element {
    +  color: "#fff";
    +  color: "#fff";
    +  color: "#000";
    +}
    + + + + + + + +
    @@ -3482,7 +3587,7 @@

    - + src/color/rgb.js @@ -3589,7 +3694,7 @@

    - + src/color/rgba.js @@ -3714,7 +3819,7 @@

    - + src/color/saturate.js @@ -3815,7 +3920,7 @@

    - + src/color/setHue.js @@ -3914,7 +4019,7 @@

    - + src/color/setLightness.js @@ -4013,7 +4118,7 @@

    - + src/color/setSaturation.js @@ -4112,7 +4217,7 @@

    - + src/color/shade.js @@ -4205,7 +4310,7 @@

    - + src/color/tint.js @@ -4298,7 +4403,7 @@

    - + src/color/transparentize.js @@ -4407,7 +4512,7 @@

    - + src/shorthands/animation.js @@ -4508,7 +4613,7 @@

    - + src/shorthands/backgroundImages.js @@ -4591,7 +4696,7 @@

    - + src/shorthands/backgrounds.js @@ -4674,7 +4779,7 @@

    - + src/shorthands/borderColor.js @@ -4760,7 +4865,7 @@

    - + src/shorthands/borderRadius.js @@ -4852,7 +4957,7 @@

    - + src/shorthands/borderStyle.js @@ -4938,7 +5043,7 @@

    - + src/shorthands/borderWidth.js @@ -5024,7 +5129,7 @@

    - + src/shorthands/buttons.js @@ -5114,7 +5219,7 @@

    - + src/shorthands/margin.js @@ -5200,7 +5305,7 @@

    - + src/shorthands/padding.js @@ -5286,7 +5391,7 @@

    - + src/shorthands/position.js @@ -5400,7 +5505,7 @@

    - + src/shorthands/size.js @@ -5493,7 +5598,7 @@

    - + src/shorthands/textInputs.js @@ -5595,7 +5700,7 @@

    - + src/shorthands/transitions.js @@ -5690,7 +5795,7 @@

    - + src/helpers/directionalProperty.js @@ -5784,7 +5889,7 @@

    - + src/helpers/em.js @@ -5877,7 +5982,7 @@

    - + src/helpers/modularScale.js @@ -5980,7 +6085,7 @@

    - + src/helpers/rem.js @@ -6073,7 +6178,7 @@

    - + src/helpers/stripUnit.js @@ -6173,7 +6278,7 @@

    - + src/shorthands/animation.js @@ -6227,7 +6332,7 @@

    - + src/shorthands/buttons.js @@ -6281,7 +6386,7 @@

    - + src/mixins/fontFace.js @@ -6394,7 +6499,7 @@

    - + src/types/color.js @@ -6471,7 +6576,7 @@

    - + src/types/color.js @@ -6554,7 +6659,7 @@

    - + src/shorthands/textInputs.js @@ -6608,7 +6713,7 @@

    - + src/mixins/triangle.js @@ -6662,7 +6767,7 @@

    - + src/mixins/radialGradient.js @@ -6751,7 +6856,7 @@

    - + src/helpers/modularScale.js @@ -6805,7 +6910,7 @@

    - + src/types/color.js @@ -6888,7 +6993,7 @@

    - + src/types/color.js @@ -6965,7 +7070,7 @@

    - + src/mixins/timingFunctions.js @@ -7019,7 +7124,7 @@

    - + src/color/toColorString.js diff --git a/src/color/readableColor.js b/src/color/readableColor.js new file mode 100644 index 00000000..ca7e0592 --- /dev/null +++ b/src/color/readableColor.js @@ -0,0 +1,46 @@ +// @flow + +import parseToRgb from './parseToRgb' +import curry from '../internalHelpers/_curry' + +const h = (c: number): number => + c / 255 <= 0.03928 ? c / 255 / 12.92 : ((c / 255 + 0.055) / 1.055) ** 2.4 + +/** + * Selects black or white for best contrast depending on the luminosity of the given color. + * Follows W3C specs for readability at https://www.w3.org/TR/WCAG20-TECHS/G18.html + * + * @example + * // Styles as object usage + * const styles = { + * color: readableColor('#000'), + * color: readableColor('papayawhip'), + * color: readableColor('rgb(255,0,0)'), + * } + * + * // styled-components usage + * const div = styled.div` + * color: ${readableColor('#000')}; + * color: ${readableColor('papayawhip')}; + * color: ${readableColor('rgb(255,0,0)')}; + * ` + * + * // CSS in JS Output + * + * element { + * color: "#fff"; + * color: "#fff"; + * color: "#000"; + * } + */ + +function readableColor(color: string): string { + const c = parseToRgb(color) + return h(c.red) * 0.2126 + h(c.green) * 0.7152 + h(c.blue) * 0.0722 > 0.179 + ? '#000' + : '#fff' +} + +// Don’t inline this variable into export because Rollup will remove the /*#__PURE__*/ comment +const curriedReadableColor = /*#__PURE__*/ curry(readableColor) // eslint-disable-line spaced-comment +export default curriedReadableColor diff --git a/src/color/test/__snapshots__/readableColor.test.js.snap b/src/color/test/__snapshots__/readableColor.test.js.snap new file mode 100644 index 00000000..b4c1aac5 --- /dev/null +++ b/src/color/test/__snapshots__/readableColor.test.js.snap @@ -0,0 +1,41 @@ +exports[`readableColor should return black given gray, #787878 1`] = `"#000"`; + +exports[`readableColor should return black given gray, hsl(0, 0%, 47%) 1`] = `"#000"`; + +exports[`readableColor should return black given palevioletred, "palevioletred" 1`] = `"#000"`; + +exports[`readableColor should return black given papayawhip, "papayawhip" 1`] = `"#000"`; + +exports[`readableColor should return black given red, #FF0000 1`] = `"#000"`; + +exports[`readableColor should return black given red, hsl(0, 100%, 50%) 1`] = `"#000"`; + +exports[`readableColor should return black given rgb(120,120,120) 1`] = `"#000"`; + +exports[`readableColor should return black given white hex, #fff 1`] = `"#000"`; + +exports[`readableColor should return black given white, "white" 1`] = `"#000"`; + +exports[`readableColor should return black given white, rgb(255,255,255) 1`] = `"#000"`; + +exports[`readableColor should return white given black, "black" 1`] = `"#fff"`; + +exports[`readableColor should return white given black, #000 1`] = `"#fff"`; + +exports[`readableColor should return white given black, rgb(0,0,0) 1`] = `"#fff"`; + +exports[`readableColor should return white given black, rgba(0,0,0,0.1) 1`] = `"#fff"`; + +exports[`readableColor should return white given black, rgba(0,0,0,0.7) 1`] = `"#fff"`; + +exports[`readableColor should return white given blue, #0000FF 1`] = `"#fff"`; + +exports[`readableColor should return white given blue, hsl(250, 100%, 50%) 1`] = `"#fff"`; + +exports[`readableColor should return white given blue, hsla(250, 100%, 50%, 0.2) 1`] = `"#fff"`; + +exports[`readableColor should return white given gray, #757575 1`] = `"#fff"`; + +exports[`readableColor should return white given gray, hsl(0, 0%, 45%) 1`] = `"#fff"`; + +exports[`readableColor should return white given rgb(117,117,117) 1`] = `"#fff"`; diff --git a/src/color/test/readableColor.test.js b/src/color/test/readableColor.test.js new file mode 100644 index 00000000..bee3580e --- /dev/null +++ b/src/color/test/readableColor.test.js @@ -0,0 +1,71 @@ +// @flow +import readableColor from '../readableColor' + +describe('readableColor', () => { + it('should return black given white hex, #fff', () => { + expect(readableColor('#fff')).toMatchSnapshot() + }) + + it('should return white given black, #000', () => { + expect(readableColor('#000')).toMatchSnapshot() + }) + + it('should return black given red, #FF0000', () => { + expect(readableColor('#FF0000')).toMatchSnapshot() + }) + it('should return white given blue, #0000FF', () => { + expect(readableColor('#0000FF')).toMatchSnapshot() + }) + it('should return black given gray, #787878', () => { + expect(readableColor('#787878')).toMatchSnapshot() + }) + it('should return white given gray, #757575', () => { + expect(readableColor('#757575')).toMatchSnapshot() + }) + it('should return black given white, rgb(255,255,255)', () => { + expect(readableColor('rgb(255,255,255)')).toMatchSnapshot() + }) + it('should return white given black, rgb(0,0,0)', () => { + expect(readableColor('rgb(0,0,0)')).toMatchSnapshot() + }) + it('should return black given rgb(120,120,120)', () => { + expect(readableColor('rgb(120,120,120)')).toMatchSnapshot() + }) + + it('should return white given rgb(117,117,117)', () => { + expect(readableColor('rgb(117,117,117)')).toMatchSnapshot() + }) + it('should return white given black, rgba(0,0,0,0.7)', () => { + expect(readableColor('rgba(0,0,0,0.7)')).toMatchSnapshot() + }) + it('should return white given black, rgba(0,0,0,0.1)', () => { + expect(readableColor('rgba(0,0,0,0.1)')).toMatchSnapshot() + }) + it('should return white given black, "black"', () => { + expect(readableColor('black')).toMatchSnapshot() + }) + it('should return black given papayawhip, "papayawhip"', () => { + expect(readableColor('papayawhip')).toMatchSnapshot() + }) + it('should return black given palevioletred, "palevioletred"', () => { + expect(readableColor('palevioletred')).toMatchSnapshot() + }) + it('should return black given white, "white"', () => { + expect(readableColor('white')).toMatchSnapshot() + }) + it('should return black given red, hsl(0, 100%, 50%)', () => { + expect(readableColor('hsl(0, 100%, 50%)')).toMatchSnapshot() + }) + it('should return white given blue, hsl(250, 100%, 50%)', () => { + expect(readableColor('hsl(250, 100%, 50%)')).toMatchSnapshot() + }) + it('should return black given gray, hsl(0, 0%, 47%)', () => { + expect(readableColor('hsl(0, 0%, 47%)')).toMatchSnapshot() + }) + it('should return white given gray, hsl(0, 0%, 45%)', () => { + expect(readableColor('hsl(0, 0%, 45%)')).toMatchSnapshot() + }) + it('should return white given blue, hsla(250, 100%, 50%, 0.2)', () => { + expect(readableColor('hsla(250, 100%, 50%, 0.2)')).toMatchSnapshot() + }) +})