Skip to content

Instantly share code, notes, and snippets.

@olmokramer
Last active December 9, 2023 12:52
Show Gist options
  • Save olmokramer/82ccce673f86db7cda5e to your computer and use it in GitHub Desktop.
Save olmokramer/82ccce673f86db7cda5e to your computer and use it in GitHub Desktop.
Regex for CSS colors: hex, rgb(a), hsl(a)
/(#([0-9a-f]{3}){1,2}|(rgba|hsla)\(\d{1,3}%?(,\s?\d{1,3}%?){2},\s?(1|0?\.\d+)\)|(rgb|hsl)\(\d{1,3}%?(,\s?\d{1,3}%?\)){2})/i
@khalilgharbaoui
Copy link

khalilgharbaoui commented Feb 8, 2020

This is @alsotang's version with the addition of transparant and its more strict.
The named colours must be exactly that. the $ makes sure it ends that way.
Also a notable part is:

#(?:[0-9a-fA-F]{2}){2,4}$|(#[0-9a-fA-F]{3}$)|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\)$
VS
#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\)

/(#(?:[0-9a-fA-F]{2}){2,4}$|(#[0-9a-fA-F]{3}$)|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\)$|black$|silver$|gray$|whitesmoke$|maroon$|red$|purple$|fuchsia$|green$|lime$|olivedrab$|yellow$|navy$|blue$|teal$|aquamarine$|orange$|aliceblue$|antiquewhite$|aqua$|azure$|beige$|bisque$|blanchedalmond$|blueviolet$|brown$|burlywood$|cadetblue$|chartreuse$|chocolate$|coral$|cornflowerblue$|cornsilk$|crimson$|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$|gainsboro$|ghostwhite$|goldenrod$|gold$|greenyellow$|grey$|honeydew$|hotpink$|indianred$|indigo$|ivory$|khaki$|lavenderblush$|lavender$|lawngreen$|lemonchiffon$|lightblue$|lightcoral$|lightcyan$|lightgoldenrodyellow$|lightgray$|lightgreen$|lightgrey$|lightpink$|lightsalmon$|lightseagreen$|lightskyblue$|lightslategray$|lightslategrey$|lightsteelblue$|lightyellow$|limegreen$|linen$|mediumaquamarine$|mediumblue$|mediumorchid$|mediumpurple$|mediumseagreen$|mediumslateblue$|mediumspringgreen$|mediumturquoise$|mediumvioletred$|midnightblue$|mintcream$|mistyrose$|moccasin$|navajowhite$|oldlace$|olive$|orangered$|orchid$|palegoldenrod$|palegreen$|paleturquoise$|palevioletred$|papayawhip$|peachpuff$|peru$|pink$|plum$|powderblue$|rosybrown$|royalblue$|saddlebrown$|salmon$|sandybrown$|seagreen$|seashell$|sienna$|skyblue$|slateblue$|slategray$|slategrey$|snow$|springgreen$|steelblue$|tan$|thistle$|tomato$|transparent$|turquoise$|violet$|wheat$|white$|yellowgreen$|rebeccapurple$)/i

@NicolasLetellier
Copy link

Very useful, thanks!

Just a little comment: If you include the "ignore case" flag /i at the very end of the regex, no need to check for uppercase letters ( [0-9a-f] is then enough, instead of [0-9a-fA-F]).

@frattaro
Copy link

Added currentColor, removed A-F given /i comment above.

I just tested in latest Chromium, doesn't appear to care about case-sensitivity of "currentColor" -- I thought it might be one of those weird case-sensitivity exceptions, but it isn't... thankfully.

/(#(?:[0-9a-f]{2}){2,4}$|(#[0-9a-f]{3}$)|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\)$|black$|silver$|gray$|whitesmoke$|maroon$|red$|purple$|fuchsia$|green$|lime$|olivedrab$|yellow$|navy$|blue$|teal$|aquamarine$|orange$|aliceblue$|antiquewhite$|aqua$|azure$|beige$|bisque$|blanchedalmond$|blueviolet$|brown$|burlywood$|cadetblue$|chartreuse$|chocolate$|coral$|cornflowerblue$|cornsilk$|crimson$|currentcolor$|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$|gainsboro$|ghostwhite$|goldenrod$|gold$|greenyellow$|grey$|honeydew$|hotpink$|indianred$|indigo$|ivory$|khaki$|lavenderblush$|lavender$|lawngreen$|lemonchiffon$|lightblue$|lightcoral$|lightcyan$|lightgoldenrodyellow$|lightgray$|lightgreen$|lightgrey$|lightpink$|lightsalmon$|lightseagreen$|lightskyblue$|lightslategray$|lightslategrey$|lightsteelblue$|lightyellow$|limegreen$|linen$|mediumaquamarine$|mediumblue$|mediumorchid$|mediumpurple$|mediumseagreen$|mediumslateblue$|mediumspringgreen$|mediumturquoise$|mediumvioletred$|midnightblue$|mintcream$|mistyrose$|moccasin$|navajowhite$|oldlace$|olive$|orangered$|orchid$|palegoldenrod$|palegreen$|paleturquoise$|palevioletred$|papayawhip$|peachpuff$|peru$|pink$|plum$|powderblue$|rosybrown$|royalblue$|saddlebrown$|salmon$|sandybrown$|seagreen$|seashell$|sienna$|skyblue$|slateblue$|slategray$|slategrey$|snow$|springgreen$|steelblue$|tan$|thistle$|tomato$|transparent$|turquoise$|violet$|wheat$|white$|yellowgreen$|rebeccapurple$)/i

@hrieke
Copy link

hrieke commented May 26, 2020

License?
Thanks

@olmokramer
Copy link
Author

@hrieke My original one is public domain. Do with it whatever you want :)

@khalilgharbaoui
Copy link

khalilgharbaoui commented May 27, 2020

@olmokramer made a gem out of it and referenced the original gist in the credits section.

https://github.com/khalilgharbaoui/coloregex#credits
👍

@olmokramer
Copy link
Author

@khalilgharbaoui That's cool, always nice to see your work being used :)

Please be aware, though, that most of the regexes here still contain pretty serious bugs.

For example, a lot of patterns have a clause similar to (rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\), but this is obviously wrong for multiple reasons:

  • It accepts rgb(0, 0, 0, 0) and rgba(0, 0, 0), though rgb can only have 3 arguments and rgba must have 4 arguments.
  • It accepts rgb(0, 0%, 0%), though the arguments to rgb cannot be percentages. Joining the rgb and hsl cases was never a good idea, because rgb must always reject percentages, while hsl must accept them.
  • It accepts rgb(-1, -1, -1), though the minimum allowed value is 0.
  • It accepts rgb(999, 999, 999), though the maximum allowed value is 255.
  • It accepts hsl(0%, 0, 0), though the first argument cannot be a percentage, and the second and third argument must be percentages.

To name a few. This is not a problem if you just want to find correctly formatted colours in a body of text, but it is a problem if the regular expression is used to validate colours. The $ everywhere make it impossible to use this regex to find all colours in a body of text, though, as it will only find them at the end of the string, so the only use case that's left is validation, which, as I pointed out, the regex isn't very good at.

So I would suggest you decide what is your use case for the package, and either (or both :)

  • Replace all those $ with \b so that the regex can be used for searching again.
  • Or fix the bugs in the regex to follow the specification so that it can be used for validation.

@arnelenero
Copy link

arnelenero commented Mar 25, 2022

@olmokramer Sorry but of all the items in your bullet list above, only the last item seems to be true as regards valid values, at least nowadays (I know you posted a couple of years back)

  • rgb() and rgba() are now interchangeable, and so are hsl() and hsla(). They are now considered aliases.
  • second item is still partially true, but for different reason. rgb() accepts percentages. But only when ALL 3 color components are %.
  • rgb() accepts out of range values, including negative. The renderer just clamps it into the accepted range. This includes alpha value.

@arnelenero
Copy link

arnelenero commented Mar 25, 2022

Also I would like to point out that in general, a single regex that captures all possible values across all color spaces, hex, named colors, etc. is difficult to formulate especially if:

  • you need to retrieve values of the color components from capturing groups
  • you need to impose strict rules per color model (rgb vs. hsl)

It is still better to test the color string against different specialized patterns to determine which one matches (if any).

@xkeshav
Copy link

xkeshav commented Apr 16, 2023

none keyword allowed in latest syntax (while values are space seprated not comma separated ) hen how to capture these

following are valid css

  • rgb(none 10 20)
  • hsl(120deg none none)
  • hwb(200 none 0.5)

@Chasmical
Copy link

Chasmical commented Dec 9, 2023

Here's the regular expression I've got. It recognizes hex-colors and simple color functions. I'm using it in a remarkjs plugin to add color squares next to colors, similar to how GitHub does this: #4F82A9. It might be a bit too big, but it runs on the server for me, so it's fine.

/^(#[0-9a-fA-F]{3,4}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8}|rgb\(\s*\d*(?:\.\d*)?\s*,\s*\d*(?:\.\d*)?\s*,\s*\d*(?:\.\d*)?\s*\)|rgba\(\s*(?:\d*(?:\.\d*)?%|0)\s*,\s*(?:\d*(?:\.\d*)?%|0)\s*,\s*(?:\d*(?:\.\d*)?%|0)\s*,\s*\d*(?:\.\d*)?%?\s*\)|rgba?\(\s*\d*(?:\.\d*)?\s*\d*(?:\.\d*)?\s*\d*(?:\.\d*)?\s*(?:\/\s*\d*(?:\.\d*)?%?\s*)?\)|rgba?\(\s*(?:\d*(?:\.\d*)?%|0)\s*(?:\d*(?:\.\d*)?%|0)\s*(?:\d*(?:\.\d*)?%|0)\s*(?:\/\s*\d*(?:\.\d*)?%?\s*)?\)|hsl\(\s*\d*(?:\.\d*)?(?:deg|grad|rad|turn)?\s*,\d*(?:\.\d*)?%\s*,\s*\d*(?:\.\d*)?%\s*\)|hsla\(\s*\d*(?:\.\d*)?(?:deg|grad|rad|turn)?\s*,(?:\d*(?:\.\d*)?%|0)\s*,\s*(?:\d*(?:\.\d*)?%|0)\s*,\s*\d*(?:\.\d*)?%?\s*\)|h(?:sla?|wb)\(\s*\d*(?:\.\d*)?(?:deg|grad|rad|turn)?\s*\d*(?:\.\d*)?\s*\d*(?:\.\d*)?\s*(?:\/\s*\d*(?:\.\d*)?\s*)?\)|h(?:sla?|wb)\(\s*\d*(?:\.\d*)?(?:deg|grad|rad|turn)?\s*(?:\d*(?:\.\d*)?%|0)\s*(?:\d*(?:\.\d*)?%|0)\s*(?:\/\s*\d*(?:\.\d*)?%?\s*)?\))$/

And I'm constructing it like this (JavaScript):

const num = String.raw`\d*(?:\.\d*)?`;
const hue = `${num}(?:deg|grad|rad|turn)?`;

const colorFuncs = [
  // Hex colors: #000, #0000, #000000, #00000000
  `#[0-9a-fA-F]{3,4}`,
  `#[0-9a-fA-F]{6}`,
  `#[0-9a-fA-F]{8}`,
  // Legacy rgb/rgba: rgb(1,2,3), rgba(1,2,3,4)
  String.raw`rgb\(\s*${num}\s*,\s*${num}\s*,\s*${num}\s*\)`,
  String.raw`rgba\(\s*(?:${num}%|0)\s*,\s*(?:${num}%|0)\s*,\s*(?:${num}%|0)\s*,\s*${num}%?\s*\)`,
  // Modern rgb/rgba: rgba(1 2 3 / 0.5)
  String.raw`rgba?\(\s*${num}\s*${num}\s*${num}\s*(?:/\s*${num}%?\s*)?\)`,
  String.raw`rgba?\(\s*(?:${num}%|0)\s*(?:${num}%|0)\s*(?:${num}%|0)\s*(?:/\s*${num}%?\s*)?\)`,
  // Legacy hsl/hsla: hsl(1deg,2%,3%)
  String.raw`hsl\(\s*${hue}\s*,${num}%\s*,\s*${num}%\s*\)`,
  String.raw`hsla\(\s*${hue}\s*,(?:${num}%|0)\s*,\s*(?:${num}%|0)\s*,\s*${num}%?\s*\)`,
  // Modern hsl/hsla/hwb: hsla(1deg 2% 3% / 0.5)
  String.raw`h(?:sla?|wb)\(\s*${hue}\s*${num}\s*${num}\s*(?:/\s*${num}\s*)?\)`,
  String.raw`h(?:sla?|wb)\(\s*${hue}\s*(?:${num}%|0)\s*(?:${num}%|0)\s*(?:/\s*${num}%?\s*)?\)`,
];

export const ColorRegex = new RegExp(`^(${colorFuncs.join("|")})$`);

Here's what this regex doesn't recognize: lab(), lch(), oklab(), oklch(), color(), none keyword, <system-color> type, transparent, currentColor and named colors. Feel free to extend on this one, I'm not planning to, since for my use case I don't need that much specificity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment