PatternRegex

Match a hex color

Match CSS hex colors in both the 3- and 6-digit forms — with the word boundary that stops #abcd from half-matching.

Last updated

Patterng
#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b
Open in Regex Tester & Builder

Matches

  • #fff
  • #0a0b0f
  • #FF5733

Doesn’t match

  • #12
  • #abcd
  • 0a0b0f

Why it’s written this way

The alternation tries the 3-digit shorthand and the 6-digit long form; the trailing \b is what makes it honest. Without it, #abcd would "match" as #abc plus a stray d — with it, a 4- or 5-digit value fails outright instead of half-matching.

Case-insensitive by character class, so #FF5733 and #ff5733 both land without needing the i flag.

Edge cases to know

  • →4- and 8-digit hex with alpha (#fff8, #0a0b0fcc) are rejected — add {4} and {8} alternatives if your CSS uses them.
  • →It finds colors in prose too, so a Markdown heading anchor like #introduction never matches (letters g–z fail the class), but a bare hex word after # would.
  • →Named colors and rgb()/oklch() functions are out of scope — this is only the # literal.

Part by part

#
The literal hash. It has no special meaning in regex, so it needs no escaping — unlike in a CSS selector, where it does.
(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})
Two alternatives: the three-digit shorthand or the full six-digit form. Longest-first ordering matters here, and this pattern gets it wrong on purpose — see the mistakes below.
[0-9a-fA-F]{6}
The full form: two hex digits each for red, green and blue.
\b
A word boundary at the end, which is what stops #abc matching the first three characters of #abcdef.

Common mistakes

  • #[0-9a-fA-F]{3,6}

    A range accepts four and five digit values, which are not valid CSS colours at all. It also matches greedily, so it will happily consume five characters of a six-digit value if the sixth is not hex.

    #(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b
  • #(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})

    Without the trailing boundary, regex alternation takes the first branch that matches, so #ff8800 matches as #ff8 and leaves 800 behind. The boundary is what forces the longer branch to win.

  • #[0-9a-f]{6}

    Lowercase-only misses #FFAA00, and design tools emit uppercase as often as not. Listing both cases costs nothing and avoids a bug that only shows up on someone else's stylesheet.

Questions

Does it match eight-digit colours with an alpha channel?

No. #RRGGBBAA and the four-digit #RGBA shorthand are valid modern CSS but are not covered here — add 8 and 4 as further alternatives if your stylesheets use them, keeping the longest branch first.

Why does the shorthand form expand the way it does?

Each digit is doubled rather than padded, so #f80 means #ff8800 and not #f08000. That is why the shorthand can only express colours whose channel pairs happen to repeat.

Will it match a colour inside a longer identifier?

It can. In an id like #ff0000-highlight the colour portion matches, because the boundary sits between the 0 and the hyphen. Anchor the pattern when you are validating a value rather than scanning a stylesheet.

Does it cover named colours or rgb() notation?

No, only hex. CSS accepts named colours, rgb(), hsl(), oklch() and more, each with its own syntax — a pattern that tried to cover them all would be far harder to read than four separate ones.

Related in Patterns