/^/
MediumWeb4 min read

CSS Class Selector Regex

Validates a single CSS class selector: a leading dot followed by a legal CSS identifier, such as .btn, .btn-primary, or .btn_2, using ASCII letters, digits, underscores, and hyphens.

#css#selector#web#class#stylesheet

Regex Pattern

^\.-?[_a-zA-Z][_a-zA-Z0-9-]*$

Pattern Breakdown

Hover over a token to see what it does.

^\.-?[_a-zA-Z][_a-zA-Z0-9-]*$
TokenMeaning
^Anchors the match to the start of the string
\.A literal dot marking the start of a CSS class selector
-?An optional single leading hyphen, allowed before the identifier's first letter
[_a-zA-Z]The identifier itself must start with a letter or underscore
[_a-zA-Z0-9-]*Zero or more letters, digits, underscores, or hyphens make up the rest of the class name
$Anchors the match to the end of the string

Detailed Explanation

What it does

This pattern checks that a string is a single, well-formed CSS class selector: a dot followed by a name that starts with a letter or underscore (optionally preceded by one hyphen) and continues with letters, digits, underscores, or hyphens. It rejects selectors with no leading dot, names starting with a digit, and embedded spaces.

Why it works

The CSS specification allows a class name to begin with an optional single hyphen followed by a letter or underscore, then any run of letters, digits, underscores, or hyphens. The pattern mirrors that: \. anchors the required dot, -? allows the one optional leading hyphen, [_a-zA-Z] enforces a non-digit identifier start, and the trailing [_a-zA-Z0-9-]* class permits the remaining legal characters, all locked to the full string by ^/$.

Common use cases

  • Validating a single class name before injecting it into a dynamically generated stylesheet
  • Linting CSS-in-JS or utility-class strings for typos before they reach the browser
  • Sanitizing user-configurable class names in a theming or design-system tool
  • Filtering safe class tokens when building a class-name allowlist for a CMS or page builder

Edge cases

  • A single leading hyphen before the identifier, like .-webkit-box, is valid and mirrors real-world vendor-prefixed utility classes
  • Underscore-led names, like ._private, are valid since underscore is an allowed identifier-start character
  • A name with a double leading hyphen, like .--custom, is rejected here because only one optional hyphen is permitted before the letter/underscore
  • A bare word with no leading dot, like btn, is rejected since the dot is mandatory for a class selector

Limitations

  • Does not support CSS escape sequences (e.g. \.foo\:hover) used to escape special characters inside class names
  • Does not match multiple chained class selectors like .btn.btn-primary or combinators like .btn > .icon
  • Only covers ASCII characters; real CSS identifiers can include escaped Unicode code points

Interactive Tester

Edit the pattern or text below — matching runs live in your browser.

.btn .btn-primary .btn_2

Test Cases

Editable — add your own inputs to see if they pass.

InputExpectedResult
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass

Language Variants

Production-ready examples in 12 languages.

const cssClassSelectorRegex = /^\.-?[_a-zA-Z][_a-zA-Z0-9-]*$/;
console.log(cssClassSelectorRegex.test('.btn-primary')); // true

Common Mistakes

Forgetting to escape the leading dot, writing ^.-?[_a-zA-Z]... where the unescaped . matches any character instead of a literal dot

Fix: Escape the dot as \. so it only matches a literal period at the start of the selector

Allowing the identifier to start with a digit by writing [_a-zA-Z0-9] for the first character too

Fix: Keep the first identifier character restricted to [_a-zA-Z] (no digits), matching real CSS identifier-start rules

Assuming this pattern also matches compound or descendant selectors like .btn.active or .card .title

Fix: This pattern only validates one bare class selector; parse or split on '.' and whitespace separately for compound selectors

Performance Notes

  • All quantifiers use simple, non-overlapping character classes, so matching runs in linear time with no catastrophic backtracking
  • The optional -? and single required identifier-start character keep the automaton small and fast to evaluate
  • Cheap enough to validate every class name in a large stylesheet or class-name allowlist without noticeable cost

Browser Compatibility

EngineSupportedNotes
ChromeYes
FirefoxYes
SafariYes
EdgeYes
Node.jsYes