Day 5: Advanced Selectors, Specificity & Cascade
✅ Today you will learn
- Combinators: descendant, child, sibling
- Attribute selectors and :not(), :is(), :has()
- Pseudo-classes: :hover, :focus-visible, :nth-child, :checked
- Pseudo-elements: ::before, ::after, ::placeholder, ::marker
- Cascade, specificity, inheritance – debugging “my CSS doesn’t work”
🏢 In the real world
The most common message a CSS developer gets: “I changed the colour in style.css but nothing happens.” Nine times out of ten the rule is fine – another, more specific rule wins. Once you can read specificity, you fix these bugs in 30 seconds instead of adding !important everywhere.
Combinators
<style>
.menu a { color: #264de4; } /* descendant: any <a> inside .menu */
.menu > li { border-bottom: 1px solid #e5e7eb; } /* child: only direct <li> */
h3 + p { font-weight: 600; } /* adjacent sibling: the p right after h3 */
h3 ~ small { color: #9ca3af; } /* general sibling: any later small */
</style>
<ul class="menu">
<li><a href="#">Home</a></li>
<li><a href="#">Sweets</a>
<ul><li><a href="#">Dry sweets</a></li></ul>
</li>
</ul>
<h3>Kaju Katli</h3>
<p>This first paragraph is bold (h3 + p).</p>
<p>The second is normal.</p>
<small>Prices include GST (h3 ~ small)</small>Attribute selectors and logical pseudo-classes
<style>
a[href^="https"]::after { content: " ↗"; } /* starts with */
a[href$=".pdf"] { color: #b91c1c; } /* ends with */
a[href^="tel:"] { font-weight: 700; color: #15803d; }
input[type="email"] { border: 2px solid #264de4; }
li:not(:last-child) { margin-bottom: 6px; }
:is(h4, h5) span { color: #c2410c; }
.card:has(img) { border: 2px solid #f59e0b; padding: 6px; }
</style>
<p><a href="https://maps.google.com">Find us on Maps</a> · <a href="menu.pdf">Download menu</a> · <a href="tel:+919829012345">Call now</a></p>
<input type="email" placeholder="you@example.com">
<ul><li>One</li><li>Two</li><li>Three (no margin)</li></ul>
<h4>Today’s <span>special</span></h4>
<div class="card"><img src="images/ladoo-400.webp" alt="" width="80"> card with an image gets a gold border (:has)</div>Pseudo-classes: states and positions
<style>
.btn { background: #264de4; color: #fff; border: 0; padding: .6rem 1rem; border-radius: 8px; cursor: pointer; }
.btn:hover { background: #1e3a8a; }
.btn:active { transform: scale(.97); }
.btn:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
.btn:disabled { background: #9ca3af; cursor: not-allowed; }
tr:nth-child(even) { background: #f1f5f9; }
tr:first-child { font-weight: 700; background: #264de4; color: #fff; }
input:checked + label { color: #15803d; font-weight: 700; text-decoration: line-through; }
</style>
<button class="btn">Hover / Tab to me</button> <button class="btn" disabled>Sold out</button>
<table style="margin-top:10px;border-collapse:collapse;width:100%">
<tr><td>Sweet</td><td>Price</td></tr><tr><td>Kaju katli</td><td>₹900</td></tr><tr><td>Ghewar</td><td>₹650</td></tr><tr><td>Ladoo</td><td>₹500</td></tr>
</table>
<p><input type="checkbox" id="t1"><label for="t1"> Pack 20 Diwali boxes</label></p>| Pseudo-class | Matches |
|---|---|
| :hover / :active | Mouse over / being pressed |
| :focus-visible ⭐ | Keyboard focus (better than :focus for outlines) |
| :nth-child(2n) / (odd) / (3) | Position among siblings |
| :first-child / :last-child | First / last sibling |
| :checked, :disabled, :required, :invalid | Form states (Day 15) |
| :has(x) | Parent that contains x – the “parent selector” |
Pseudo-elements: ::before and ::after
<style>
.req::after { content: " *"; color: #dc2626; }
.new::before { content: "NEW"; background: #16a34a; color: #fff; font-size: 10px; font-weight: 700; padding: 2px 6px; border-radius: 4px; margin-right: 6px; }
.quote::before { content: "“"; font-size: 3rem; color: #f59e0b; line-height: 0; vertical-align: -0.4em; margin-right: 4px; }
ul.tick { list-style: none; padding: 0; }
ul.tick li::before { content: "✔"; color: #16a34a; margin-right: 8px; }
p::first-letter { font-size: 1.4em; font-weight: 700; color: #264de4; }
::selection { background: #fde68a; }
</style>
<p><label class="req">Mobile number</label></p>
<p class="new">Sugar-free kaju katli launched</p>
<p class="quote">Best ghewar in Jaipur!</p>
<ul class="tick"><li>Pure desi ghee</li><li>No preservatives</li><li>Made today</li></ul>Text from ::before/::after is not selectable and is read inconsistently by screen readers. Never put important information (prices, instructions) there.
The cascade: who wins?
- 1Importance: !important beats normal declarations (avoid it)
- 2Specificity: the more specific selector wins
- 3Order: if specificity is equal, the rule written LAST wins
- 4Inheritance: if no rule targets the element, some properties (color, font) come from the parent
| Selector | Score (ID, class, element) | Wins against |
|---|---|---|
| p | 0, 0, 1 | — |
| .price | 0, 1, 0 | any number of elements |
| .card .price | 0, 2, 0 | .price |
| nav ul li a:hover | 0, 1, 4 | .price? No – 1 class = 1 class, then 4 elements beat 0 |
| #special | 1, 0, 0 | any number of classes |
| style="…" (inline) | beats all selectors | only !important beats it |
<style>
.card .price { color: #16a34a; } /* 0,2,0 – wins */
.price { color: #dc2626; } /* 0,1,0 – written later, but loses */
p { color: #374151; }
.card { color: #264de4; font-family: Georgia, serif; } /* inherited by children */
</style>
<div class="card">
<p>This text inherits the card’s font, but p sets its own colour.</p>
<span class="price">₹900 – green, because .card .price is more specific</span>
</div>Real debugging: “my colour doesn’t apply”
- Inspect the element. Is your rule listed at all? If not: wrong selector, typo, or the CSS file is not linked/cached (Ctrl+F5).
- Your rule is crossed out? Look at the rule above it in the Styles pane – that one wins. Compare specificity.
- Fix by matching the existing pattern (e.g. write .card .price), not by adding !important.
- Prefer flat, single-class selectors in your own CSS (BEM, Day 17) so these fights never start.
It works today, but the next fix needs another !important to beat it, and soon nothing can be overridden. Acceptable uses: utility classes like .hidden { display: none !important } and overriding third-party CSS you cannot edit.
🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
#nav .item a, ul li.active, .btn.primary:hover.Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
How is specificity calculated?
As a tuple (IDs, classes/attributes/pseudo-classes, elements/pseudo-elements) compared left to right. Inline styles beat selectors; !important beats normal declarations; :where() adds zero and :is()/:not() take the specificity of their most specific argument.
Pseudo-class vs pseudo-element?
A pseudo-class (:hover) selects an element in a particular state or position; a pseudo-element (::before) styles a part of an element or creates a virtual child that is not in the HTML.
Summary
- Combinators: space, >, +, ~; attribute selectors like [href^="https"].
- Pseudo-classes = states/positions; pseudo-elements = parts/decorations.
- Cascade order: importance → specificity → source order → inheritance.
- Debug in DevTools; avoid !important and deep selectors.
Day 6: display, position (relative, absolute, fixed, sticky), z-index and overflow – badges, sticky headers and WhatsApp buttons.
