Suggest an editImprove this articleRefine the answer for “CSS selector specificity”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**CSS selector specificity** is a (A,B,C,D) score that decides which rule applies when multiple selectors match the same element. ```css div { color: blue; } /* 0,0,0,1 */ .box { color: red; } /* 0,0,1,0 - wins */ #main { color: green; } /* 0,1,0,0 - wins over both */ ``` **Key:** compare columns left to right. Higher column wins. Equal scores go to the last declared rule.Shown above the full answer for quick recall.Answer (EN)Image**CSS selector specificity** is a scoring system browsers use to decide which CSS rule wins when multiple rules target the same element. ## Theory ### TL;DR - Think of it like a bidding system: inline styles bid $1000, each ID bids $100, each class bids $10, each tag bids $1. Highest total wins. - Scores are written as four numbers (A,B,C,D): inline styles / IDs / classes+attributes+pseudo-classes / elements+pseudo-elements. - If scores tie, the rule declared last in the stylesheet wins. - `!important` bypasses specificity entirely and creates its own override layer. - Prefer classes over IDs for most rules. They are much easier to override later. ### Quick example ```css div { color: blue; } /* 0,0,0,1 - element */ .highlight { color: red; } /* 0,0,1,0 - class, beats element */ #main .highlight { color: green; } /* 0,1,1,0 - ID+class, beats class */ ``` ```html <div id="main" class="highlight">What color is this?</div> ``` The text is green. `#main .highlight` scores (0,1,1,0), which beats both (0,0,1,0) and (0,0,0,1). Browsers compare columns left to right. The first column where the numbers differ decides the winner. ### How the score is calculated Each selector component adds to one of four columns: | Category | Column | Examples | |---|---|---| | Inline styles | A (1,0,0,0) | `style="color:red"` | | IDs | B (0,1,0,0) | `#header`, `#nav` | | Classes, attributes, pseudo-classes | C (0,0,1,0) | `.btn`, `[type="text"]`, `:hover` | | Elements and pseudo-elements | D (0,0,0,1) | `div`, `p`, `::before` | The universal selector `*` scores zero. Combinators like `>`, `+`, and `~` also score zero. Only the actual components count. So `div > p.class` equals one tag + one tag + one class = (0,0,1,2). And `:nth-child(2n)` is a pseudo-class, so it scores (0,0,1,0), the same weight as a regular class. This is also why `[type="button"]` and `.btn` tie. Attribute selectors sit in the same column as classes. ### When specificity matters - You added a class rule but the element still shows styles from an older ID rule. The ID wins because (0,1,0,0) beats (0,0,1,0). - You are overriding a component library. Bootstrap's `.btn-primary` is (0,0,2,0) when combined with `.btn`. A single custom class at (0,0,1,0) will not beat it. Match the depth or add a parent class. - A style is not applying and you cannot figure out why. Open DevTools, check the Computed tab. Crossed-out declarations lost to higher specificity somewhere in the [CSS cascade](/questions/css-cascade). - You need a theming layer. Chaining classes like `.theme-dark .card` gives (0,0,2,0) and stays much easier to override than any ID-based rule. ### Common mistakes **Mistake 1: assuming source order always decides** ```css .box { color: red; } /* 0,0,1,0 */ div.box { color: blue; } /* 0,0,1,1 - wins despite appearing after */ ``` Source order is only a tiebreaker when specificity scores are equal. If they differ, the higher score wins regardless of order. **Mistake 2: chaining IDs** ```css #header #nav #item { padding: 10px; } /* 0,3,0,0 */ ``` Now you need at least four chained classes just to override this one rule. BEM classes like `.header__nav-item` score (0,0,1,0) and stay easy to work with. **Mistake 3: forgetting attribute selectors score like classes** ```css [type="button"] { border: 1px solid; } /* 0,0,1,0 - same as .btn */ button.primary { border: 2px solid; } /* 0,0,2,0 - wins */ ``` Many developers assume attribute selectors are weaker than class selectors. They are not. **Mistake 4: reaching for `!important` first** In Tailwind projects this creates real maintenance problems. Once one rule uses `!important`, the next override needs it too. The fix: increase legitimate specificity by adding a parent selector or extra class instead. ### Real-world usage - **Bootstrap**: `.btn-primary` scores (0,0,2,0), beating `.btn` at (0,0,1,0). Custom themes need to match that compound level. - **Material-UI**: Override components with `#id.class` or `& .MuiButton-root` in JSS to reach (0,1,1,0) or (0,0,2,0). - **Tailwind**: Each utility class scores (0,0,1,0). They stack predictably, but conflicts appear when component styles load alongside utilities. - **Styled-components and CSS Modules**: Generate unique class names with a hash to sidestep conflicts entirely, no specificity math needed. One thing I see repeatedly in production: a developer adds an ID selector to fix a broken override, and six months later nobody dares touch that rule. Stick to classes. ### Follow-up questions **Q:** What is the specificity of `div > p.class`? **A:** (0,0,1,2). One class and two elements. The `>` combinator adds zero points. **Q:** How does `!important` fit into specificity? **A:** It does not. Browsers place `!important` rules in a separate pool above normal rules. Within that pool, specificity still applies. Author `!important` beats normal author rules, but user `!important` beats author `!important`. **Q:** What does the universal selector `*` score? **A:** Zero. (0,0,0,0). Combinators score the same way. Only actual components such as IDs, classes, and elements count. **Q:** Two rules have equal specificity. Which one wins? **A:** The one declared later in the stylesheet. **Q:** In a shadow DOM, does host specificity affect `::part()` styles? **A:** No. `::part()` flattens to the component's own specificity context. A host ID adds to the exported part's score but does not pierce internal shadow DOM styles. This is Chrome 89+ behavior and trips developers who assume global cascade rules apply inside shadow boundaries. ## Examples ### Basic: three selectors targeting one element ```html <p id="intro" class="lead">Hello</p> ``` ```css p { color: black; } /* 0,0,0,1 */ .lead { color: navy; } /* 0,0,1,0 */ #intro { color: darkgreen; } /* 0,1,0,0 - wins */ ``` The paragraph is dark green. Each step up the scoring table carries ten times the weight of the column below it. The ID at (0,1,0,0) cannot be beaten by any number of classes stacked on the same element. ### Intermediate: overriding a component library ```css /* Bootstrap base */ .btn { padding: 6px 12px; } /* 0,0,1,0 */ .btn.btn-primary { background: #0d6efd; } /* 0,0,2,0 */ /* Your theme override - loads after Bootstrap */ .custom-theme .btn-primary { background: #e63946; } /* 0,0,2,0 - ties, but declared later = wins */ ``` Adding a parent class `.custom-theme` bumps the rule to (0,0,2,0), matching Bootstrap's compound selector depth. Because your CSS loads after the framework, declaration order breaks the tie in your favor.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.