charlotte fed - css inheritance and specificity
TRANSCRIPT
CSS Inheritance &Specificity
Sarah Hudson
What is specificity?
Answer:1)How browsers determine which CSS properties to apply to an element when multiple, conflicting rules are given.
2)The weight given to each ruleset, determined by the selectors given.
Is specificity different from inheritance?
Short answer, yes.
Inheritance refers to how child nodes inherit their parent nodes’ styling, how styling cascades.
Specificity refers to how the browser determines which styles to apply to an element.
Specificity can come into play in overriding inherited styles, but child nodes don’t technically inherit the parents’ specificity rules
In other words -- You can use inheritance rules as guidelines to write selectors, but don’t rely on the parents’ specificity to style child elements.
But why does this matter? Anyone can learn CSS in an hourAnyone can learn & write CSS quickly but understanding specificity matters if:
You work on a large team with many developers
You are pulled onto a legacy project
You need to update a theme (i.e., Shopify, Wordpress, Squarespace)
You’re working on an open source project
You can’t touch the HTML
How do browsers decide what styles apply?
How browsers
work● Networking layer opens XHR requests to server for docs and assets
● UI backend contains default styles
● HTML and CSS are parsed by the Rendering Engine
How browsers work, cont
● Rendering Engine: Will fetch files in 8KB chunks
● Content Tree: HTML and content parsed first
● Render Tree: Style attributes and CSS parsed, determines the order of content
● Layout Tree: Node coordinates are calculated
● Paint: The browser’s UI Backend processes the information from the trees and displays it on the screen
How browsers parse CSS
Browsers download CSS files as they’re linked and create StyleSheet objects which contain rules
The browser traverses the DOM tree, and then traverses all rulesets in CSS to match the values in class attributes to selectors in rulesets
Browsers read selectors right to left; the last selector is called the key selector
Making the decision, overview
Browsers take three things into account before applying CSS:
1.Source Order
a.Whichever ruleset comes last will be applied
2.Specificity
a.Whichever ruleset carries greater weight / more specific selectors will be applied
3.Importance
a.Whichever ruleset is marked as being more important will be applied
Making the decision via specificity
The most specific selector: If more than one declaration applies to the same element, then the most specific selector wins
I.e., h2.headline will apply over .headline
This also works in overriding inherited styles, i.e., p > .red will override p.
The most weighted selector: The selector that has the highest specificity weight will win
I.e., #sidebar { … } weighs more than aside { … }
Equal weight: The last ruleset downloaded will be applied
I.e., .sidebar and .side-navigation
So how does specificity specifically work?
Specificity Weights
Built in rules assign weights to different selectors:
Tag selectors, pseudo-elements: 1 point
I.e., a { … }
Class selectors, attributes, pseudo-classes: 10 points
I.e., .headline { … }
ID selectors: 100 points
I.e., #sidebar
Inline CSS: 1000 points
!important: ∞
While technically not part of specificity, this overrides all rules of specificity EXCEPT when two conflicting rulesets both employ it.
Two visuals of calculating specificity
1.Add the weights by 10s 2. Fill in the number of each selector in its respective 1s, 10s, etc space to determine the weight
Specificity Is Additive
The longer your selector is, the higher the specificity weight is. Examples:
.sidebar li { … } = 10 + 1 = 11
.sidebar li a { … } = 10 + 1 + 1 = 12
#main-content .sidebar { … } = 100 + 10 = 110
#main-content .sidebar li { … } = 100 + 10 + 1 = 111
And so on...
Quick Exercise: How much weight is given?1.div {}
a.Answer: 1 (1 tag element)
2.ul li {}a. Answer: 2 (2 tag elements)
3.ul.special-links li a {}a. Answer: 13 (3 tag elements and 1 class)
4.ul.special-links li a:hover {}a. Answer: 23 (3 tag elements, 1 class, 1 pseudo-class)
5.ul.special-links li:not(a) {}a. Answer: 13 (3 tag elements, 1 class, negation is 0)
6.aside[id=“navbar”] header h2 {}a. Answer: 13 (3 tags, 1 attribute)
7.h1 + *[rel=up] {}a. Answer: 11 (1 tag and 1 attribute)
That’s cool & all but can you show me some code?
What ruleset will win? Example #1
<footer class=“main-footer”>
<a href=“#about-us” class=“navigation--link site-map--link”>About Us</a>
</footer>
.main-footer a { color: white }
a.site-map--link { color: darkblue }
a { color: blue; }
a.navigation--link { color: skyblue }
Answer: It’ll be sky blue because all the selectors have the same weight, except for the tag selector, and the navigation--link is the last one
What ruleset will win? Example #2
<ul>
<li><a href=“#”>Home</a></li>
<li><a href=“#”>Contact</a></li>
</ul>
li > a { color: green; }
li a { color: brown; }
Answer: It will be brown because the selectors are equal weight; it chooses the last one. It’s important to note that combinators such as > and ~ have no weight on specificity. They’re a part of inheritance instead, ensuring we only grab the a tags directly under our li tags, but we don’t apply the declarations to a tags which are in divs in li elms
What ruleset will win? Example #3
What color will the links be give this structure?
<aside id="sidebar">
<section id="salads">
<ul>
<li><a href="#">Cobb Salad</a></li>
</ul>
</section>
</aside>
aside#sidebar a {background-color: yellow; }
aside#sidebar a[href="#"] {background-color: lightgreen;}
section#salads li a {background-color: aliceblue;}
Answer:The background will be light green. This is because attribute selectors have a specificity weight of 10, thus making it 1 + 100 + 1 + 10 = 112
What ruleset will win? Example #4
What color will the text be given this structure?
<section id=“intro-text”>
<p>Lorem ipsum…</p>
</section>
section#intro-text {color: red !important;}
p {color: darkgrey;}
Answer: Specificity is not inherited. Since we’re using the parent element as a selector, it will apply to the parent element, but selecting “p” is more specific to the “p” tag than the parent’s selector. Thus the paragraph will be grey.
What ruleset will win? Example #5
What color will the text be given this structure, where we apply .orange-text on the last span?
Hint: We have 11 element selectors in the first ruleset
html body main section header h2 div span span span span {color: purple;}
.orange-text {color: orange;}
Answer: 11 beats 10, right? Well...if this were simply math, but classes will always beat out elements. That’s the tricky thing about specificity. Each step up will always beat out the step beneath it.
What ruleset will win? Example #6 <section>
<header>
<h2 style="color: red;" class="test-important-text">Test</h2>
</header>
</section>
style="color: red;"
.test-important-text {color: purple !important;}
Answer: Inline styles are closer to the DOM, so those should apply, right? In this case, remember that !important breaks all specificity rules. It is the only way to override inline styles. So it will be purple.
What are some best practices?
Simplicity is best
Keep selectors as short and simple as possible to ensure that stylesheets are:
Easily maintainable, readable, reusable, & intuitive
Easy to onboard (for new developers on the team)
Easy to come back to after months
Not chock full of convoluted rules tripping over each other
*** Use highly specific selectors as the exception, not the rule, whenever possible
Use classes whenever possible
● Element selectors should mainly be used for resets, and resets should mainly be done to level the browser playing field
● Element selectors are too general and end up with adding more rulesets for different styling/conditions
● Multiple resets mean that style rules were applied too early and are usually the cause of specificity battles
● Classes are more semantic and provide specific intent
○ I.e., the selector .article-title instead of article > header > h2
● Separation of concerns -- CSS semantics are different from/more granular than HTML semantics
More best CSS practices
● Rulesets should do one thing
○ I.e., .align-left should only float to the left; .slider should only describe necessary items for slider component (no page layout, etc)
○ This allows you to reuse, mix and match
○ Follows modular approach the web is moving towards
○ Less issues with overwriting rules down the road
● Give classes semantic, intuitive names
○ I.e., .primary-brand-color is better than .dark-blue or using the color throughout selectors
○ Prevents brute force overriding when dev can’t find relevant classes
What are some anti-patterns?
Antipatterns, Pt. I!important
Reasons:
Breaks specificity rules and is nearly impossible to override
Can result in bugs
Makes it difficult for developers / designers new to the project
IDs
Reasons:
IDs have a specificity weight of 100, making them difficult to override and impossible to override with classes
Can lead to bugs / complicated CSS
Style objects can’t be shared among siblings when IDs are present
Note: IDs are meant to be landmarks
Descendents / child selectors
Reason: It is an expensive call and slows processing/paint time
Antipatterns, Pt. IILong / unnecessarily specific selectors
Example: body .container .sidebar .links
Reasons:
Adds weight and causes slower processing / paint times as it has to traverse the DOM to check for an exact match
Adds confusion in terms of readability / maintainability
Violates DRY
Qualified Selectors
Example: div.container aside.sidebar a.links
Reasons:
Unnecessary, as the browser can find the classes without the elem
Adds weight and causes slower processing / paint times
Violates DRY
Not readable, reusable, or maintainable
FIN
Resources
Smashing Magazine -- CSS Specificity: Things You Should Know
Mozilla’s Specificity Docs
Smashing Magazine -- CSS Specificity & Inheritance
How browsers work: Behind the scenes of modern web browsers
Specificity calculator
CSS Wizardry
This presentation’s specificity examples
Contact meEmail: [email protected]:@SarahHudson2008Website:www.sarah-hudson.com