camelCase vs kebab-case: Why Different Languages Use Different Naming
Within the same project, you might write a JavaScript variable as myVariable, a CSS class as my-class, and a URL as my-page-slug. That inconsistency can look like arbitrary style preference, but it actually comes down to a hard technical reason: the grammar constraints of each language. This guide explains, at the syntax level, why programming languages practically force camelCase while CSS and URLs are free to use kebab-case.
1. Why can't a hyphen go in a variable name? The subtraction-operator collision
In the grammar of programming languages like JavaScript, Java, C, and Python, the hyphen (-) is already reserved as the subtraction operator. If my-variable were allowed as a variable name, the parser would have no way to tell whether it's a single identifier or the subtraction expression "the variable variable subtracted from the variable my." Try running let my-variable = 5; in a browser console and you'll get a SyntaxError. Language designers had to eliminate this ambiguity at the root, and as a result most C-family and scripting languages banned hyphens from identifiers entirely.
2. Why the alternative became camelCase: the competition with underscores
With hyphens off the table, languages needed some other way to combine multiple words into one identifier, and there were essentially two candidates: snake_case, which inserts an underscore (_) between words, and camelCase, which marks word boundaries with a capital letter. Both are perfectly valid syntactically, so different languages and communities made different choices. The C standard library and Python carried on the snake_case tradition, while Java adopted camelCase as its official naming convention from the start — and JavaScript inherited that convention directly from Java. In other words, camelCase itself isn't the only syntactically valid option; the one constraint that applies to both conventions equally is simply that hyphens are off-limits.
| Convention | Uses hyphens | Valid as a programming-language identifier? |
|---|---|---|
| camelCase (myVariable) | No | Yes |
| snake_case (my_variable) | No | Yes |
| kebab-case (my-variable) | Yes | No (collides with the subtraction operator) |
3. Why CSS and URLs are free to use kebab-case
CSS class names, HTML attribute names, and URL slugs were never "identifiers" in a programming language to begin with — they're just plain strings. A CSS parser has no reason to interpret .my-class as a subtraction expression, because CSS syntax has no arithmetic operations at all. The same goes for URL paths: browsers and servers don't treat any character in a path as an operator, so there's zero syntactic constraint on using hyphens. Kebab-case actually has a few practical advantages on top of that. Sticking to lowercase letters and hyphens avoids the confusion that case-insensitive filesystems (like Windows) or certain URL-handling environments can create by treating myPage and mypage as the same thing, and search engines recognize hyphens as word separators, which helps both readability and SEO for URL slugs. That's also why kebab-case is the default output for tools like the URL slug generator and slug checker.
4. A practical convention matrix: where each case actually shows up
snake_case — Python variable and function names (PEP8), SQL/DB column names
kebab-case — CSS class names, URL slugs, HTML attributes (areas where hyphens are grammatically free to use)
PascalCase — JS/TS and C# class names, React/Vue component names
UPPER_SNAKE_CASE — Environment variables (.env), constant declarations
What's interesting is that the case convention shifts depending on context even within the same JavaScript ecosystem. Variables and function names use camelCase because grammar requires it, but when that same JS file deals with class names via CSS-in-JS, it writes kebab-case straight into a string, as in 'my-class'. The grammar constraint hinges on whether something is used as an identifier or as a string — it's not a matter of the language's taste.
5. What's easy to miss during conversion: abbreviations and numbers
Run real codebase identifiers through the text case detector and most convert without a hitch, but patterns where a number or another capital letter immediately follows an abbreviation — HTML5Parser, userID2Name, and the like — can come back as "unknown" because the detection rules can't pin down a clean word boundary. This isn't specific to this one tool; it's a general ambiguity in any rule-based case conversion that treats "lowercase-to-uppercase transitions" as word boundaries, whenever consecutive capital letters (an abbreviation) get in the way. When converting identifiers that contain abbreviations, it's worth always eyeballing the result once more.
Frequently Asked Questions
Q. Why can't JavaScript variable names contain hyphens?
Because the hyphen is already reserved as the subtraction operator. If my-variable were allowed as an identifier, the parser couldn't tell it apart from a subtraction expression, so it's treated as a syntax error.
Q. Why are CSS class names free to use hyphens?
CSS syntax has no arithmetic operators to begin with, and class names are treated as plain strings rather than programming-language identifiers, so there's no risk of a hyphen being mistaken for an operator.
Q. snake_case avoids the hyphen problem too — why does JS use camelCase instead?
Both are syntactically valid, but JavaScript inherited Java's naming conventions, and camelCase became the community standard from there. Grammar only forces "no hyphens" — the choice between camelCase and snake_case is a language/community convention, not a syntax requirement.
Q. Does the text case detector correctly identify the case of every string?
It correctly identifies standard patterns like camelCase, PascalCase, snake_case, and kebab-case. But patterns where a number or another uppercase letter immediately follows an abbreviation (like HTML5Parser) can come back as "unknown" due to the limits of word-boundary detection, so it's worth double-checking those results.