CSS Tutorial
20 Topics- CSS Tutorial Overview
- CSS Introduction
- CSS Fundamentals
- CSS Selectors
- CSS Colors & Backgrounds
- CSS Text & Fonts
- CSS Box Model
- CSS Display & Positioning
- CSS Flexbox
- CSS Grid
- CSS Sizing & Layout
- CSS Lists & Tables
- CSS Transitions & Animations
- CSS Responsive Design
- CSS Advanced Styling
- CSS Functions
- CSS for Accessibility & Print
- CSS Architecture & Methodologies
- CSS Preprocessors & Tools
- CSS Modern & Experimental CSS
CSS Selectors, Combinators & Specificity
From fundamental element targets to modern relational pseudo-classes (:has) and mathematical specificity calculations.
1. The 4-Column Specificity Calculation Algorithm
When multiple selectors apply to the exact same element and declare the same property, the browser determines which rule wins using Specificity. Specificity is calculated as a 4-component vector:
| Tier A: Inline Styles | Tier B: ID Selectors | Tier C: Classes, Attributes, Pseudo-classes | Tier D: Elements & Pseudo-elements |
|---|---|---|---|
(1, 0, 0, 0) | (0, 1, 0, 0) | (0, 0, 1, 0) | (0, 0, 0, 1) |
style="color:red" | #header, #sidebar | .btn, [type="text"], :hover | div, p, h1, ::before |
(0, 0, 10, 0) will never equal or surpass a single ID (0, 1, 0, 0). Tier B always wins over Tier C, regardless of how many classes are chained.CSS Example
/* Specificity comparison */
p { color: #64748b; } /* (0, 0, 0, 1) */
.desc { color: #2563eb; } /* (0, 0, 1, 0) - WINS over element */
#main-desc { color: #16a34a; } /* (0, 1, 0, 0) - WINS over class */2. CSS Combinators: Targeting Relationships
Combinators explain the relationship between two selectors in the DOM tree hierarchy:
| Combinator | Syntax | Description | Example |
|---|---|---|---|
| Descendant | A B (space) | Matches any element B that is inside A, regardless of nesting depth. | article p |
| Child | A > B | Matches only direct immediate children of A. | ul > li |
| Adjacent Sibling | A + B | Matches element B that is placed immediately after A sharing the same parent. | h2 + p |
| Subsequent Sibling | A ~ B | Matches all elements B that follow after A under the same parent. | h2 ~ p |
CSS Example
/* Child vs Descendant vs Adjacent Sibling */
.parent > .child {
border: 2px solid #3b82f6;
padding: 8px;
margin-bottom: 8px;
}
h4 + p {
font-weight: bold;
color: #d97706; /* Styles first paragraph immediately following h4 */
}3. Attribute Selectors & Pattern Matching
Target HTML elements based on their HTML attributes and values with regex-like operators:
[disabled]: Matches any element with the attribute present.[type="email"]: Exact string match.[href^="https"]: Starts with "https" (e.g. secure external links).[href$=".pdf"]: Ends with ".pdf" (e.g. document download links).[class*="btn-"]: Contains substring "btn-" anywhere in class attribute.[href*="github" i]: Case-insensitive matching with theiflag.
CSS Example
a[href^="https://"]::after {
content: " ↗";
font-size: 0.85em;
color: #2563eb;
}
a[href$=".pdf"]::after {
content: " [PDF]";
font-size: 0.8em;
color: #dc2626;
font-weight: bold;
}4. Modern Functional Pseudo-Classes: :is, :where, and :has
Modern CSS specifications introduced game-changing functional pseudo-classes:
A. The Parent / Relational Selector: :has()
Previously, CSS could never style a parent element based on its children without JavaScript. :has() checks if an element contains descendants matching a selector.
/* Style a card container when its internal checkbox is checked! */
.todo-card:has(input:checked) {
opacity: 0.6;
text-decoration: line-through;
background-color: #f1f5f9;
}B. :where() vs :is()
| Selector | Specificity Behavior | Primary Use Case |
|---|---|---|
:is(h1, h2, h3) | Adopts the specificity of its most specific argument | DRY selector grouping without repeating prefixes |
:where(h1, h2, h3) | Always zero specificity (0, 0, 0, 0) | CSS resets and design systems that callers can easily override |
CSS Example
/* Interactive :has() demonstration */
.card-item {
border: 2px solid #cbd5e1;
padding: 16px;
border-radius: 8px;
transition: all 0.2s ease;
}
/* Card reacts dynamically when child input is focused */
.card-item:has(input:focus) {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
.card-item input {
padding: 8px 12px;
border: 1px solid #94a3b8;
border-radius: 4px;
outline: none;
}5. Pseudo-Elements: Generating Virtual Content
While pseudo-classes target states (like :hover), pseudo-elements target virtual DOM fragments that do not exist in the source HTML:
::before&::after: Injects cosmetic content via thecontent: ""property.::marker: Styles the bullet or number indicator of list items (<li>) directly.::selection: Customizes the highlight color when users select text with their cursor.::first-letter: Creates classic editorial drop-caps.
::) to clearly distinguish pseudo-elements from pseudo-classes (single colon :). Browsers support single colon for legacy pseudo-elements (like :before), but double-colon is the modern W3C standard.6. Frequently Asked Questions (FAQ)
!important does not change the specificity weight of the selector itself; instead, it elevates the declaration to a completely different, higher tier of the Cascade Algorithm (Origin & Importance), bypassing standard specificity resolution entirely.:nth-child(n) counts every sibling regardless of tag type. If the 2nd child is a <div>, p:nth-child(2) will match nothing. In contrast, p:nth-of-type(2) filters specifically for <p> tags and matches the second paragraph among its siblings.:where(.btn, .card) applies styles with zero specificity (0,0,0,0), consumers can override any property using a simple single class selector without resorting to specificity wars.Explore Related Tools
Hand-picked utilities and calculators related to this tool.
