On this page
CSS becomes much easier to debug when you can answer three questions:
- Which elements does this selector match?
- If several declarations set the same property, which one wins?
- If an element has no declaration for a property, can it inherit the value from its parent?
Selectors answer the first question. The cascade answers the second. Inheritance helps answer the third. This lesson uses one learning-notes page to keep those ideas connected.
The anatomy of a CSS rule
Start with one small rule:
.note {
border: 1px solid #d7dceb;
background: white;
}
.note is the selector. It identifies elements whose class attribute includes note.
The braces contain a declaration block. Each declaration has a property on the left and a value on the right. In background: white, background is the property and white is the value.
The semicolon ends a declaration. Browsers can sometimes recover from a missing final semicolon, but writing every semicolon makes additions safer and mistakes easier to spot.
Type selectors target element names
A type selector matches every element of that type:
body {
color: #24304d;
}
h1 {
line-height: 1.15;
}
Use a type selector for a broad default that should apply consistently. h1 is appropriate when every main heading needs the same base line height. It is too broad when only one particular heading should change.
Class selectors are reusable styling hooks
A class selector begins with a full stop:
.note {
padding: 1.25rem;
}
Several elements can carry the same class:
<article class="note">...</article>
<article class="note">...</article>
This makes classes useful for reusable presentation. One element can also have several classes, such as class="note featured-note", allowing a base style and a variation to work together.
ID selectors target one document identifier
An ID selector begins with #:
#css-lesson {
border-top: 0.4rem solid #3946d6;
}
An id must be unique within the document. IDs are useful for fragment destinations, labels, and scripting references. CSS can select them, but classes are usually more reusable for repeated styling and are easier to override without escalating selector specificity.
Use an ID because the element needs a stable unique identity, not merely because you want a stronger CSS selector.
Group selectors that share declarations
Separate selectors with commas when they genuinely need the same declarations:
h1,
h2,
h3 {
color: #17203a;
line-height: 1.15;
}
This is a selector list. The rule matches all three heading types. If one selector in a list is invalid, the browser can discard the whole rule, so keep unfamiliar or experimental selectors separate until you understand their behavior.
Use a simple descendant selector for context
A space between selectors means “an element matching the second selector somewhere inside an element matching the first”:
.note p {
line-height: 1.65;
}
This targets paragraphs inside .note elements. It does not target the introductory paragraph outside those notes.
Descendant selectors are useful in moderation. A long chain such as .page main section article div p is fragile because a harmless HTML wrapper can change whether it matches. Prefer a clear class when the styling represents a reusable component.
The cascade resolves competing declarations
Several CSS rules can match the same element and set the same property. The cascade is the browser’s full process for choosing one value.
For a beginner example, assume all rules are normal author styles in the same unlayered stylesheet. In that limited situation, you can usually debug a conflict by checking:
- Does each selector actually match?
- Does one selector have greater specificity?
- If specificity is equal, which declaration comes later?
The complete cascade also considers origins, importance, cascade layers, and other context. You do not need every branch of that algorithm to understand this page, but remember that specificity is not the first step in every possible CSS conflict.
Source order breaks an equal tie
These two selectors are identical:
.status {
color: #65708a;
}
.status {
color: #17616c;
}
Both match the same elements with equal specificity. The second declaration appears later, so the status text is teal.
Source order does not mean “the last rule always wins.” A later rule with a weaker selector can still lose to an earlier, more specific rule.
Specificity at a useful beginner level
Specificity is the selector weight used when competing declarations have already reached the relevant stage of the cascade. For the simple selectors in this lesson:
- an ID selector is more specific than a class selector;
- a class selector is more specific than a type selector;
- combining selectors can add to their specificity;
- source order decides only after the competing selectors have equal specificity.
Consider this HTML:
<article class="note featured-note">...</article>
The .featured-note rule can override a property from .note because both are class selectors with equal specificity and the featured rule comes later. A combined .note.featured-note selector would be more specific than either class alone, but do not make selectors stronger unless the relationship is useful.
Inheritance supplies some missing values
Inheritance applies when an element has no winning declaration for an inheritable property. For example:
body {
color: #24304d;
font-family: system-ui, sans-serif;
line-height: 1.6;
}
Text inside the body commonly inherits color, font-family, and line-height. That lets you set readable defaults once instead of repeating them on every paragraph and list item.
Properties such as background, border, margin, padding, and width generally do not inherit. If every child inherited its parent’s width and border, ordinary layouts would become difficult to control.
Do not guess when a property matters. MDN’s property reference includes an “Inherited” field in its formal definition.
An element can still receive a direct value that replaces an inherited one. In this rule, headings use a darker color than the body text:
h1,
h2,
h3 {
color: #17203a;
}
Build the complete learning-notes example
Create two files:
css-learning-notes/
├── index.html
└── styles.css
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CSS learning notes</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main class="lesson-card" id="css-lesson">
<p class="eyebrow">CSS practice</p>
<h1>Learning notes</h1>
<p class="summary">These notes share inherited text styles and reusable classes.</p>
<section class="note-list" aria-labelledby="notes-title">
<h2 id="notes-title">Today's notes</h2>
<article class="note featured-note">
<h3>Selectors find elements</h3>
<p>A class can style several notes without repeating the same rule.</p>
<span class="status">Ready to practice</span>
</article>
<article class="note">
<h3>The cascade resolves conflicts</h3>
<p>When equally specific rules compete, the later declaration wins.</p>
<span class="status">Read next</span>
</article>
</section>
</main>
</body>
</html>
The first rule below is supplied responsive-sizing scaffolding. * is the universal selector, so it selects every element. box-sizing is explained properly in the next box-model lesson; for now, it keeps padding inside the declared card width.
* {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
display: grid;
place-items: center;
padding: 1rem;
font-family: system-ui, sans-serif;
background: #edf0ff;
color: #24304d;
}
#css-lesson {
border-top: 0.4rem solid #3946d6;
}
.lesson-card {
width: min(42rem, 100%);
padding: clamp(1.5rem, 5vw, 2.5rem);
border-radius: 1rem;
background: white;
}
h1,
h2,
h3 {
color: #17203a;
line-height: 1.15;
}
.eyebrow {
color: #3946d6;
font-size: 0.8rem;
font-weight: 800;
text-transform: uppercase;
}
.note-list {
margin-top: 2rem;
}
.note {
margin-top: 1rem;
padding: 1.25rem;
border: 1px solid #d7dceb;
border-radius: 0.75rem;
}
.note p {
line-height: 1.65;
}
.featured-note {
border-color: #3946d6;
background: #f4f5ff;
}
.status {
color: #65708a;
font-weight: 700;
}
.status {
color: #17616c;
}
Inspect matched and overridden rules in DevTools
Open the example and inspect a .status element. In the Styles panel, you should see both color declarations. The losing value is usually crossed out, while the later declaration is active.
Then inspect a paragraph inside .note:
- Confirm that
.note psupplies its line height. - Find the inherited
font-familyand basecolorfrombodyin the Computed or Inherited view. - Temporarily disable a declaration with its checkbox.
- Read the selector shown beside each rule instead of changing random values.
DevTools changes are temporary. Once you understand the cause, edit styles.css and reload.
Why !important should not be the first fix
!important changes how a declaration participates in the cascade. It can override ordinary declarations in its origin and layer, but it often turns one unclear conflict into a harder future conflict.
Before adding it, check for a misspelled class, a selector that does not match, an unexpectedly specific rule, or incorrect source order. If a project genuinely needs !important—for example, a carefully designed utility or an accessibility override—document the reason and keep the scope narrow.
Common mistakes
Forgetting the . or #. note selects an element named <note>; .note selects a class; #note selects an ID.
Writing a selector that does not match the HTML. Compare spelling and capitalization, then inspect the element instead of assuming the stylesheet failed to load.
Expecting every property to inherit. A parent’s text color may pass down; its margin and border usually will not.
Assuming the later rule always wins. Source order breaks an equal tie after other relevant cascade decisions.
Using an ID to make a rule “strong enough.” Fix the selector relationship. Do not increase specificity merely to win a contest.
Practice
Practice: predict before you run
Add a class named .quiet to the second note’s status. Write .quiet { color: #65708a; } above
the final .status rule and predict the result. Then move it below that rule. Both selectors have
equal specificity, so source order changes the winner. Finally try .status.quiet and explain why
its position no longer produces the same tie.
What to learn next
You can now work out why a style applies. Next, learn how the CSS box model turns width, padding, borders, and margins into an element’s rendered size. That sizing model will make layout problems much less mysterious.