User Rating 0.0
Total Usage 0 times
Category CSS Tools
Is this tool helpful?

Your feedback helps us improve.

About

CSS ID selectors carry a specificity weight of 100, which is 100 times heavier than a single class selector at 010. A single misplaced #header .nav a rule can cascade into hours of debugging when you try to override it with a class-based selector downstream. Converting #id selectors to their attribute-selector equivalent [id="id"] reduces specificity to 010, matching class-level weight. The selector still targets the same element. The behavior is identical. Only the specificity math changes.

This tool parses your raw CSS, identifies every ID selector in every rule (including nested @media and @supports blocks), and rewrites them to attribute form. It does not touch hex color values like #fff inside declarations, nor does it alter strings or url() references. Pro tip: after conversion, run your test suite. Attribute selectors do not match in document.getElementById calls in JavaScript, so your CSS refactor is safe, but verify any JS that relies on specificity order for style overrides.

css converter id selector attribute selector css specificity css refactoring selector converter css tools

Formulas

CSS specificity is computed as a tuple (a, b, c, d) where each component counts occurrences of a selector category. The conversion performed by this tool changes the b component (ID count) to the c component (class/attribute count).

Specificity(S) = (a, b, c, d)

Where: a = inline style (0 or 1), b = count of ID selectors (#id), c = count of class selectors, attribute selectors, and pseudo-classes, d = count of element selectors and pseudo-elements.

Before: #main .nav b = 1, c = 1 (0,1,1,0) = 110
After: [id="main"] .nav b = 0, c = 2 (0,0,2,0) = 20

The regex pattern used for conversion matches ID selectors while avoiding hex color values inside declaration blocks:

pattern = /#([a-zA-Z_][a-zA-Z0-9_-]*)/g

This pattern is applied only to the selector portion of each CSS rule, never to declaration values. The tool splits the CSS into blocks by tracking brace depth, isolates selectors (text before the opening {), applies the transform, and reassembles the output.

Reference Data

Selector TypeExampleSpecificity (a,b,c)Decimal WeightOverride Difficulty
Inline stylestyle="..."1,0,0,01000Requires !important
ID selector#nav0,1,0,0100Another ID or !important
Attribute selector (id)[id="nav"]0,0,1,010Any class-level rule
Class selector.nav0,0,1,010Another class or higher
Pseudo-class:hover0,0,1,010Same as class
Attribute selector (other)[type="text"]0,0,1,010Same as class
Element selectordiv0,0,0,11Any class or higher
Pseudo-element::before0,0,0,11Same as element
Universal selector*0,0,0,00Anything overrides
Combinators>, ~, +, 0,0,0,00No specificity contribution
:is() / :not():not(#foo)Uses highest argumentVariesDepends on argument
:where():where(.a)0,0,0,00Always zero specificity
Two IDs chained#a #b0,2,0,0200Extremely hard
ID + class#a.b0,1,1,0110Needs ID-level rule
After conversion[id="a"].b0,0,2,020Two classes suffice

Frequently Asked Questions

No. Both #foo and [id="foo"] match exactly the same element: the one with id="foo". The only difference is specificity weight. #foo has specificity (0,1,0,0) while [id="foo"] has (0,0,1,0). The matched DOM node set is identical per the CSS Selectors Level 4 specification.
No. The converter only operates on the selector portion of each CSS rule (the text before the opening {). Hex color values appear inside declaration blocks (after {), so they are never touched. Values like color: #336699; pass through unchanged.
The parser uses a brace-depth counter to recursively descend into at-rule blocks. When it encounters @media, @supports, or any other at-rule with a block body, it processes the inner rules independently. Each nested selector gets the same ID-to-attribute conversion. The at-rule wrapper itself is preserved verbatim.
The regex-based converter will match #foo inside :not(#foo) and convert it to :not([id="foo"]). This is correct behavior. Note that :not() uses the specificity of its argument, so converting the inner ID selector to an attribute selector also reduces the specificity of the entire :not() pseudo-class from ID-level to class-level.
Yes. Custom properties like --my-color: #abcdef; exist inside declaration blocks, which the converter never modifies. The var(--my-id) function is also inside declarations and is left untouched. Only selectors are transformed.
No. This tool only changes your CSS, not your HTML or JavaScript. document.getElementById('foo') still works because the element's id attribute hasn't changed. document.querySelector('#foo') also still works because the browser's query engine accepts both forms. Your JavaScript is unaffected.
The converter handles standard CSS identifiers consisting of letters, digits, hyphens, and underscores. Escaped characters in selectors (e.g., #foo\.bar or #\31 23) are not currently supported and will be left partially converted. For such edge cases, manual review of the output is recommended.