Calculadora de Especificidad CSS

Calcula especificidad CSS como (a,b,c) con desglose por selector

Understanding CSS Specificity

CSS specificity is the algorithm browsers use to determine which CSS declaration applies when multiple rules target the same element. Every selector in your stylesheet carries a specificity weight, and when two or more declarations compete for the same property on the same element, the one with the higher specificity wins β€” regardless of source order. Understanding this mechanism is essential for writing predictable, maintainable stylesheets and avoiding the frustrating debugging sessions that arise from unexpected style overrides.

The specificity calculation follows a three-component scoring system defined in the W3C Selectors specification. Each selector is reduced to a tuple (a, b, c) where a counts ID selectors, b counts class selectors, attribute selectors, and pseudo-classes, and c counts type (element) selectors and pseudo-elements. This tool parses any CSS selector and computes its specificity score instantly, helping you resolve cascade conflicts without trial and error.

The Specificity Scoring System: (a, b, c)

Specificity is not a single number β€” it is a three-component tuple compared from left to right. A selector with specificity (1, 0, 0) always beats (0, 15, 15) because the first component (ID selectors) takes absolute precedence over the second and third. This means a single ID selector outweighs any number of classes or elements combined.

The components are calculated as follows: Component a increments by 1 for each ID selector (e.g., #header). Component b increments by 1 for each class selector (.nav), attribute selector ([type="text"]), or pseudo-class (:hover, :nth-child(2), :focus). Component c increments by 1 for each type selector (div, p, h1) or pseudo-element (::before, ::after, ::placeholder).

The universal selector (*), combinators (>, +, ~, space), and the negation pseudo-class :not() itself do not contribute to specificity β€” though the arguments inside :not() do count. For example, :not(.active) adds (0, 1, 0) from the .active argument.

Inline Styles, !important, and the Full Cascade

Beyond selector specificity, the CSS cascade has additional layers. Inline styles applied via the style attribute on an element override all selector-based specificity. They effectively have a specificity of (1, 0, 0, 0) in a four-component model β€” higher than any selector written in a stylesheet. The only way to override an inline style from a stylesheet is with !important.

The !important annotation elevates a declaration above all normal declarations, including inline styles. However, when multiple !important declarations compete, normal specificity rules apply among them. This creates a dangerous escalation pattern often called "specificity wars" β€” once you start using !important to override styles, you often need more !important rules to override those overrides, leading to unmaintainable CSS.

The full cascade priority (from lowest to highest) is: user agent styles, normal author styles (ordered by specificity), !important author styles, !important user agent styles. Within each origin, specificity determines the winner, and among equal specificity, source order (last declaration wins) is the tiebreaker.

Modern CSS Features: :is(), :where(), :has(), and @layer

Modern CSS introduces pseudo-classes with special specificity behaviors. The :is() pseudo-class (formerly :matches()) takes the specificity of its most specific argument. For example, :is(#id, .class, div) has specificity (1, 0, 0) because #id is the highest-specificity argument. This makes :is() useful for grouping selectors without writing them out individually, but you must be aware that even unused arguments affect the specificity of the entire selector.

In contrast, :where() has zero specificity regardless of its arguments. This makes it ideal for writing base styles or resets that are easy to override. A selector like :where(#id, .class) p has specificity (0, 0, 1) β€” only the p type selector contributes. This is a powerful tool for library authors who want their defaults to be easily overridable by consumers.

The :has() relational pseudo-class takes the specificity of its most specific argument, similar to :is(). So div:has(.active) has specificity (0, 1, 1) β€” one class from .active and one type from div. Finally, CSS Cascade Layers (@layer) introduce an entirely new dimension to the cascade. Layers are ordered by declaration order, and styles in later layers override earlier layers regardless of specificity. Unlayered styles always beat layered styles. This gives developers explicit control over cascade priority without resorting to specificity hacks or !important.

Common Specificity Problems and Debugging Strategies

The most frequent specificity problem is unintended overrides: you write a rule expecting it to apply, but a more specific selector elsewhere wins. Debugging starts with browser DevTools β€” the Styles panel shows all matching declarations with strikethrough on overridden ones, and hovering reveals the specificity score. If you see your rule crossed out, compare its specificity tuple against the winning selector.

Common anti-patterns include: over-qualifying selectors (e.g., div#header ul.nav li a.link when .nav-link suffices), using IDs for styling (which creates high-specificity anchors that are hard to override), nesting selectors too deeply in preprocessors like Sass, and scattering !important throughout the codebase. Each of these makes future changes harder because new rules must match or exceed the existing specificity.

Best practices for managing specificity include: using a flat class-based methodology like BEM (Block Element Modifier) which keeps specificity uniformly at (0, 1, 0), reserving IDs for JavaScript hooks rather than styling, leveraging :where() for overridable defaults, and adopting @layer to establish clear cascade boundaries between resets, base styles, components, and utilities.

Code Examples

Comparing specificity scores for common selectors

/* Specificity: (0, 0, 1) β€” one type selector */
p { color: black; }

/* Specificity: (0, 1, 0) β€” one class selector */
.highlight { color: blue; }

/* Specificity: (0, 1, 1) β€” one class + one type */
p.highlight { color: green; }

/* Specificity: (1, 0, 0) β€” one ID selector */
#main { color: red; }

/* Specificity: (1, 1, 1) β€” one ID + one class + one type */
div#main .content p { color: purple; }

/* Specificity: (0, 2, 1) β€” two classes + one type */
ul.nav li.active { color: orange; }

/* :where() contributes zero specificity */
/* Specificity: (0, 0, 1) β€” only the p counts */
:where(.article, #post) p { color: gray; }

/* :is() takes highest argument specificity */
/* Specificity: (1, 0, 1) β€” #post is highest + p type */
:is(.article, #post) p { color: teal; }

/* :has() takes argument specificity */
/* Specificity: (0, 1, 1) β€” .active class + div type */
div:has(.active) { border: 1px solid gold; }

Output:

Selector                        | Specificity
--------------------------------|------------
p                               | (0, 0, 1)
.highlight                      | (0, 1, 0)
p.highlight                     | (0, 1, 1)
#main                           | (1, 0, 0)
div#main .content p             | (1, 1, 3)
ul.nav li.active                | (0, 2, 2)
:where(.article, #post) p       | (0, 0, 1)
:is(.article, #post) p          | (1, 0, 1)
div:has(.active)                | (0, 1, 1)

Standards & Specifications