Understanding Selector Priority Calculation Rules

In CSS development, selector specificity is the key mechanism that determines the order in which styles take effect. When multiple rules target the same element, browsers decide which style to apply based on the "weight" of the selectors. Understanding these rules not only prevents unexpected style overrides but also helps developers write clearer and more maintainable code. The core of priority calculation lies in quantifying the "strength" of different selector combinations, ensuring that declaration order within CSS files no longer relies entirely on position but is instead judged based on logical structure.
CSS selector specificity is not determined by writing order; rather, it is composed of the type and quantity of selectors themselves. The weight calculation follows a strict hierarchy where ID selectors possess the highest base value, followed by class and attribute selectors, and finally element and pseudo-element selectors (excluding :lang).

Specifically, the allocation rules for weights are as follows:

  • ID Selectors: Each ID selector contributes 100 points. Since IDs in a page are typically unique, selectors with multiple IDs often have extremely high priority but can lead to hard-to-control style conflicts.
  • Class, Attribute, and Pseudo-class Selectors: Each class selector (.class), attribute selector ([attr]), or pseudo-class selector (:hover, :nth-child(), etc.) contributes 10 points. These are the primary tools for building complex styling logic; they offer high flexibility with moderate priority.
  • Element and Pseudo-element Selectors: Each element selector (div, p) or pseudo-element selector (::before, ::after) contributes 1 point. They represent the most basic tag hierarchy.

Calculation Logic and Example Demonstrations

To intuitively demonstrate how priorities accumulate, we can convert the above rules into mathematical formulas for calculation. Assume two CSS rules target the same <div> element:

/* Rule A */
#header .nav-item { color: red; }

/* Rule B */
.header .nav-item:hover { color: blue; }

Let's break down the weights of both:

  • Rule A contains one ID selector (#header) and one class selector (.nav-item). The calculation is $100 + 10 = 110$.
  • Rule B consists only of two class selectors (.header and .nav-item) and one pseudo-class selector (:hover). The calculation is $10 + 10 + 10 = 30$.

Although Rule A appears earlier in the file, its weight (110) far exceeds that of Rule B (30), meaning color: red will ultimately take effect. This mechanism implies developers do not need to deliberately place high-priority styles at the end of files; instead, they can naturally increase priority by combining selectors.

Handling Special Cases: Universal Selector and Inheritance

In actual development, there are special cases that affect calculation results, most commonly the universal selector (*) and pseudo-class :lang().

  1. Universal Selector: Selectors using the form * or element * have extremely low weight. For example, the priority of * .class is only 10 points, while .class itself is also 10 points. This makes * often used for resetting default styles because it rarely overrides specific class selector rules.
  2. Pseudo-class :lang(): This is a special case. Although it belongs to pseudo-classes, in priority calculations, :lang() is not counted towards the weight score (i.e., treated as 0 points). This means div:lang(en) has the same priority as an ordinary div, without adding extra 10 points like other pseudo-classes do.

Furthermore, when a selector contains multiple parts, the weights of each part are independently accumulated. For example, the priority of #a .b #c .d is $100 + 10 + 100 + 10 = 220$ points. This linear accumulation characteristic makes it very simple and safe to build high-priority rules by combining basic selectors.

Best Practices and Coding Standards

After mastering the priority calculation rules, developers should follow these best practices to optimize CSS architecture:

  • Avoid Overusing ID Selectors: Although IDs have the highest priority, they are rarely used in modern frontend frameworks due to potential maintenance difficulties and unpredictable style conflicts. Prefer combining class names instead.
  • Utilize BEM Naming Convention: Adopting Block Element Modifier (block-element-modifier) mode, such as .block__element--modifier, allows for building clear hierarchical structures without relying on IDs.
  • Maintain File Organization Logic: Although priority does not depend on position, placing base styles at the top and specific override styles at the bottom remains a good reading habit, helping to quickly locate key rules.

By deeply understanding selector priority calculation rules, developers can more precisely control style presentation effects, reduce debugging time, and build more robust and scalable CSS architectures.