CSS Selectors and Specificity: Targeting HTML Elements - johnverz22/webdev1-lessons GitHub Wiki
CSS (Cascading Style Sheets) uses selectors to target HTML elements for styling. Specificity is the mechanism that determines which styles are applied when multiple rules could affect the same element. Understanding both concepts is crucial for writing maintainable and predictable CSS.
- Basic Selectors
- Combinators
- Pseudo-classes
- Pseudo-elements
- Attribute Selectors
- Understanding Specificity
- Specificity Calculation
- Specificity Hierarchy
- The !important Exception
- Best Practices
- Debugging Specificity Issues
- Practical Examples
- Browser Support
Basic Selectors
Universal Selector
Targets all elements on the page.
* {
margin: 0;
padding: 0;
}
Type/Element Selector
Targets elements by their HTML tag name.
p {
color: blue;
}
Class Selector
Targets elements with a specific class attribute.
.note {
font-style: italic;
}
ID Selector
Targets a unique element with a specific ID.
#header {
background-color: #333;
}
Grouping Selector
Applies the same styles to multiple selectors.
h1, h2, h3 {
font-family: Arial, sans-serif;
}
Combinators
Descendant Combinator (space)
Targets elements that are descendants of another element.
article p {
font-size: 16px;
}
Child Combinator (>)
Targets direct children of an element.
ul > li {
list-style: square;
}
Adjacent Sibling Combinator (+)
Targets an element that directly follows another.
h2 + p {
font-weight: bold;
}
General Sibling Combinator (~)
Targets elements that are siblings of another element.
h2 ~ p {
color: gray;
}
Pseudo-classes
Pseudo-classes select elements based on their state or position.
Link States
a:link { color: blue; }
a:visited { color: purple; }
a:hover { text-decoration: underline; }
a:active { color: red; }
Form States
input:focus { border-color: blue; }
input:disabled { background-color: #eee; }
input:checked { border: 2px solid green; }
Structural Pseudo-classes
li:first-child { font-weight: bold; }
li:last-child { border-bottom: none; }
li:nth-child(2n) { background-color: #f0f0f0; } /* Even items */
li:nth-child(2n+1) { background-color: #fff; } /* Odd items */
p:only-child { font-style: italic; }
Other Common Pseudo-classes
div:empty { display: none; }
.error:not(.warning) { color: red; }
:root { --main-color: blue; }
Pseudo-elements
Pseudo-elements create "virtual" elements for styling specific parts of an element.
p::first-line { font-variant: small-caps; }
p::first-letter { font-size: 2em; }
p::before { content: "→ "; }
p::after { content: " ←"; }
input::placeholder { color: #999; }
p::selection { background: yellow; }
Attribute Selectors
Target elements based on their attributes.
Basic Attribute Selectors
[title] { cursor: help; } /* Elements with title attribute */
[type="checkbox"] { margin-right: 5px; }
[lang="fr"] { font-style: italic; }
Pattern Matching Attribute Selectors
[class^="icon-"] { /* Class starts with "icon-" */
background-repeat: no-repeat;
}
[href$=".pdf"] { /* href ends with ".pdf" */
background-image: url('pdf-icon.png');
}
[data-info*="user"] { /* data-info contains "user" */
border: 1px solid blue;
}
[lang|="en"] { /* lang is "en" or begins with "en-" */
color: blue;
}
[title~="flower"] { /* title contains the word "flower" */
color: pink;
}
Understanding Specificity
Specificity determines which CSS rule is applied when multiple rules could style the same element. When multiple declarations conflict, the one with the highest specificity wins.
Key principles:
- Specificity is hierarchical
- Specificity is cumulative within its level
- A more specific selector always overrides a less specific one, regardless of order
- Equal specificity follows source order (last rule wins)
Specificity Calculation
Specificity is typically represented as four values: (a, b, c, d)
- a: Inline styles
- b: Number of ID selectors
- c: Number of class selectors, attribute selectors, and pseudo-classes
- d: Number of element selectors and pseudo-elements
Some developers visualize specificity as a four-digit number (e.g., 1,0,2,1), but remember it's not strictly decimal - 1,0,0,0 beats 0,9,9,9.
Examples:
Selector | Specificity (a,b,c,d) | As Number |
---|---|---|
* |
(0,0,0,0) | 0 |
li |
(0,0,0,1) | 1 |
li:first-line |
(0,0,0,2) | 2 |
ul li |
(0,0,0,2) | 2 |
.active |
(0,0,1,0) | 10 |
[type="text"] |
(0,0,1,0) | 10 |
:hover |
(0,0,1,0) | 10 |
.nav .active |
(0,0,2,0) | 20 |
#nav |
(0,1,0,0) | 100 |
style="color: red" |
(1,0,0,0) | 1000 |
Specificity Hierarchy
From lowest to highest specificity:
- Universal selector (
*
) - Element/type selectors (
h1
,div
) and pseudo-elements (::before
) - Class selectors (
.example
), attribute selectors ([type="radio"]
), and pseudo-classes (:hover
) - ID selectors (
#example
) - Inline styles (
style="color: red"
) - !important (overrides everything)
The !important Exception
The !important
declaration overrides normal specificity rules, making a property take precedence regardless of the selector's specificity.
p { color: red !important; } /* Will override even inline styles */
Warning: !important
breaks the natural cascading behavior of CSS and should be used sparingly.
Best Practices
For Maintainable Specificity:
- Avoid ID selectors when possible (they have high specificity and are not reusable)
- Don't use !important (except as a last resort or in utility classes)
- Use classes instead of elements for more specific styling
- Keep selectors as simple as possible
- Limit nesting to 2-3 levels max
- Use specificity deliberately - don't rely on accidental specificity
- Follow a naming convention like BEM to manage specificity through class names
- Consider a CSS methodology like SMACSS, ITCSS, or Atomic CSS to organize styles
- Use modern CSS features like custom properties and
:where()
to manage specificity - Document complex specificity decisions in comments
Debugging Specificity Issues
When styles aren't applying as expected:
- Inspect the element using browser developer tools
- Check the Styles panel to see which rules are being applied/overridden
- Look for crossed-out properties - these are being overridden
- Check the order of CSS files being loaded
- Use browser extensions like SelectorGadget or Specificity Visualizer
- Temporarily add !important to test if specificity is the issue
- Use more specific selectors strategically to override problematic rules
- Consider refactoring overly complex selectors
Practical Examples
Example 1: Specificity Override Chain
/* Specificity: 0,0,0,1 */
p {
color: black;
}
/* Specificity: 0,0,1,1 */
.content p {
color: blue;
}
/* Specificity: 0,0,2,1 */
.article .content p {
color: green;
}
/* Specificity: 0,1,1,1 */
#main .content p {
color: red;
}
A paragraph with all these selectors would be colored red.
Example 2: Equal Specificity - Last Rule Wins
.alert {
background-color: yellow;
}
.alert {
background-color: orange; /* This wins if same specificity */
}
Example 3: The Cascade and Inheritance
body {
font-family: Arial, sans-serif; /* Inherited by all elements */
color: #333; /* Inherited by all elements */
}
a {
color: blue; /* Overrides inherited color for links */
}
.button {
font-family: Roboto, sans-serif; /* Overrides inherited font */
}
Browser Support
Most CSS selectors are well-supported in modern browsers. Some considerations:
- CSS Selectors Level 4 (like
:has()
) have variable browser support - Older browsers (IE) may not support all pseudo-classes and combinators
- Mobile browsers generally have good selector support
- Always check caniuse.com for specific selector support
- Fallbacks may be needed for certain selectors in legacy projects