Skip to content

Instantly share code, notes, and snippets.

@clshortfuse
Last active May 13, 2020 19:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clshortfuse/acffca0a5f1412edcf9e132f33febe1c to your computer and use it in GitHub Desktop.
Save clshortfuse/acffca0a5f1412edcf9e132f33febe1c to your computer and use it in GitHub Desktop.
CSS Color 4 Verifier
const RGB_FUNCTION = 'rgba?';
const HSL_FUNCTION = 'hsla?';
const HWB_FUNCTION = 'hwb';
const CMYK_FUNCTION = 'device-cmyk';
const LAB_FUNCTION = 'lab';
const LCH_FUNCTION = 'lch';
const GRAY_FUNCTION = 'gray';
const COLOR_FUNCTION = 'color';
const FUNCTION_START = '\\(';
/** https://www.w3.org/TR/css-syntax-3/#consume-function */
const FUNCTION_END = /(\)|$)/.source;
/** https://www.w3.org/TR/css-syntax-3/#whitespace */
const WHITESPACE = '\\s+';
const COMMENT = /\/\*([^*]|\*(?!\/))*\*\//.source;
const OPTIONAL_WHITESPACE = `\\s*(${COMMENT})*\\s*`;
const COMMA = ',';
const SLASH = '\\/';
const PERCENTAGE = '%';
const OPTIONAL_PERCENTAGE = '%?';
/** https://www.w3.org/TR/css3-values/#angle-value */
const ANGLE_UNIT = '(deg|grad|rad|turn)';
/** https://www.w3.org/TR/css3-values/#number-value */
const NUMBER = /[+-]?((\d+)|(\d*\.\d+))(e[+-]?\d+)?/.source;
/** https://www.w3.org/TR/css-color-4/#named-colors */
const NAMED_COLORS = [
'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black',
'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan',
'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta',
'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen',
'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen',
'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'grey',
'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush',
'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow',
'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen',
'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime',
'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise',
'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy',
'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen',
'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue',
'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey',
'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet',
'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen'];
/** @type {RegExp} */
let regExpCache = null;
/** @type {string} */
let patternCache = null;
/**
* https://www.w3.org/TR/css-color-4/#hex-notation
* @param {number} digits
* @param {number=} maxDigits
* @return {string}
*/
function getHexPattern(digits, maxDigits) {
return `#[A-F0-9]{${digits}${maxDigits > digits ? `,${maxDigits}` : ''}}`;
}
/**
* https://www.w3.org/TR/css-color-4/#rgb-functions
* @param {boolean} [commaless=false]
* @param {boolean} [percentages=false]
* @return {string}
*/
function getRGBPattern(commaless, percentages) {
const optionalAlpha = [
'(',
commaless ? SLASH : COMMA,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
RGB_FUNCTION,
FUNCTION_START,
'(',
OPTIONAL_WHITESPACE, NUMBER, percentages ? PERCENTAGE : '', OPTIONAL_WHITESPACE,
commaless ? WHITESPACE : COMMA,
')',
'{2}',
OPTIONAL_WHITESPACE, NUMBER, percentages ? PERCENTAGE : '', OPTIONAL_WHITESPACE,
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#the-hsl-notation
* @return {string}
*/
function getHSLPattern() {
const optionalAlpha = [
'(',
COMMA,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
HSL_FUNCTION,
FUNCTION_START,
OPTIONAL_WHITESPACE, NUMBER, ANGLE_UNIT, '?', OPTIONAL_WHITESPACE,
'(',
COMMA,
OPTIONAL_WHITESPACE, NUMBER, PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'{2}',
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#the-hsl-notation
* https://www.w3.org/TR/css-color-4/#the-hwb-notation
* @return {string}
*/
function getCommalessHSLorHWBPattern() {
const optionalAlpha = [
'(',
SLASH,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
'(', HSL_FUNCTION, '|', HWB_FUNCTION, ')',
FUNCTION_START,
OPTIONAL_WHITESPACE, NUMBER, ANGLE_UNIT, '?', OPTIONAL_WHITESPACE,
'(',
WHITESPACE,
OPTIONAL_WHITESPACE, NUMBER, PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'{2}',
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#cmyk-colors
* @return {string}
*/
function getDeviceCMYKPattern() {
const optionalAlpha = [
'(',
SLASH,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
// Needs recursion. Using loose check that will consume functions
const optionalFallback = [
'(',
COMMA,
/([^()]*|\([^)]*\))*/.source,
')',
'?',
].join('');
const pattern = [
CMYK_FUNCTION,
FUNCTION_START,
'(',
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
WHITESPACE,
')',
'{3}',
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
optionalAlpha,
optionalFallback,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#funcdef-lab
* @return {string}
*/
function getLabPattern() {
const optionalAlpha = [
'(',
SLASH,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
LAB_FUNCTION,
FUNCTION_START,
'(',
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_WHITESPACE,
WHITESPACE,
')',
'{2}',
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_WHITESPACE,
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#funcdef-lch
* @return {string}
*/
function getLCHPattern() {
const optionalAlpha = [
'(',
SLASH,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
LCH_FUNCTION,
FUNCTION_START,
'(',
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_WHITESPACE,
WHITESPACE,
')',
'{2}',
OPTIONAL_WHITESPACE, NUMBER, ANGLE_UNIT, '?', OPTIONAL_WHITESPACE,
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#grays
* @return {string}
*/
function getGrayPattern() {
const optionalAlpha = [
'(',
SLASH,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_PERCENTAGE, OPTIONAL_WHITESPACE,
')',
'?',
].join('');
const pattern = [
GRAY_FUNCTION,
FUNCTION_START,
OPTIONAL_WHITESPACE, NUMBER, OPTIONAL_WHITESPACE,
WHITESPACE,
optionalAlpha,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#color-function
* @return {string}
*/
function getColorPattern() {
// Loose check that consumes function
const pattern = [
COLOR_FUNCTION,
FUNCTION_START,
/([^()]*|\([^)]*\))*/.source,
FUNCTION_END,
].join('');
return pattern;
}
/**
* https://www.w3.org/TR/css-color-4/#resolving-color-values
* @return {string}
*/
export function getRegexPattern() {
if (patternCache == null) {
const patterns = [
'currentcolor',
'transparent',
...NAMED_COLORS,
// DEPRECATED_SYSTEM_COLORS
getHexPattern(3, 4),
getHexPattern(6),
getHexPattern(8),
getRGBPattern(false, false),
getRGBPattern(false, true),
getRGBPattern(true, false),
getRGBPattern(true, true),
getHSLPattern(),
getCommalessHSLorHWBPattern(),
getDeviceCMYKPattern(),
getLabPattern(),
getLCHPattern(),
getGrayPattern(),
getColorPattern(),
].join('|');
patternCache = [
'^',
OPTIONAL_WHITESPACE,
'(',
...patterns,
')',
OPTIONAL_WHITESPACE,
'$',
].join('');
}
return patternCache;
}
/** @return {RegExp} */
export function getRegExp() {
if (!regExpCache) {
regExpCache = RegExp(getRegexPattern(), 'i');
}
return regExpCache;
}
/**
* @param {string} input
* @return {boolean}
*/
export function test(input) {
return getRegExp().test(input);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment