Nesting: Selectors & Properties
Nesting: Selectors & Properties
Nesting is one of the most beloved features of SASS. It allows you to write selectors that mirror the HTML structure, making your code more readable and maintainable. However, with great power comes great responsibility - improper nesting can lead to bloated, overly-specific CSS. In this comprehensive lesson, we'll master both selector and property nesting, along with best practices to keep your code clean.
Why Nesting Matters
In plain CSS, you have to repeat parent selectors over and over:
Repetitive Plain CSS
.navigation {
background: #333;
padding: 20px;
}
.navigation ul {
list-style: none;
margin: 0;
}
.navigation ul li {
display: inline-block;
margin-right: 10px;
}
.navigation ul li a {
color: white;
text-decoration: none;
padding: 5px 10px;
}
.navigation ul li a:hover {
background: #555;
}
.navigation ul li a:active {
background: #777;
}
This is verbose, error-prone, and hard to maintain. With SASS nesting, you can write the same styles in a structure that mirrors your HTML:
Clean SCSS with Nesting
.navigation {
background: #333;
padding: 20px;
ul {
list-style: none;
margin: 0;
li {
display: inline-block;
margin-right: 10px;
a {
color: white;
text-decoration: none;
padding: 5px 10px;
&:hover {
background: #555;
}
&:active {
background: #777;
}
}
}
}
}
Much cleaner! The structure visually represents the HTML hierarchy, making it easier to understand and modify.
How Nesting Compiles to CSS
Understanding how nesting compiles helps you predict the output and avoid mistakes. SASS converts nested selectors into descendant selectors in CSS:
SCSS Input
.card {
padding: 20px;
.card-header {
font-size: 24px;
}
}
CSS Output
.card {
padding: 20px;
}
.card .card-header {
font-size: 24px;
}
The nested .card-header becomes .card .card-header (a descendant selector).
Multiple Levels of Nesting
Deep Nesting SCSS
.article {
.article-header {
.article-title {
font-size: 32px;
}
}
}
CSS Output
.article .article-header .article-title {
font-size: 32px;
}
Each level of nesting adds another selector to the chain.
The Parent Selector (&): Your Best Friend
The ampersand (&) is a special character that references the parent selector. It's essential for pseudo-classes, pseudo-elements, and modifier classes.
Pseudo-Classes with &
SCSS with Pseudo-Classes
.button {
background: #3498db;
color: white;
padding: 10px 20px;
&:hover {
background: #2980b9;
}
&:focus {
outline: 2px solid #3498db;
outline-offset: 2px;
}
&:active {
transform: scale(0.98);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
CSS Output
.button {
background: #3498db;
color: white;
padding: 10px 20px;
}
.button:hover {
background: #2980b9;
}
.button:focus {
outline: 2px solid #3498db;
outline-offset: 2px;
}
.button:active {
transform: scale(0.98);
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
Pseudo-Elements with &
SCSS with Pseudo-Elements
.button {
position: relative;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.1);
opacity: 0;
transition: opacity 0.3s;
}
&:hover::before {
opacity: 1;
}
&::after {
content: "→";
margin-left: 5px;
}
}
:: (double colon) for pseudo-elements like ::before and ::after, and : (single colon) for pseudo-classes like :hover and :focus. This is the modern CSS standard.
Modifier Classes with &
The & is perfect for creating modifier classes (like BEM methodology):
SCSS with Modifier Classes
.button {
padding: 10px 20px;
background: #3498db;
color: white;
// Class modifier: .button.primary
&.primary {
background: #3498db;
}
// Class modifier: .button.secondary
&.secondary {
background: #95a5a6;
}
// Class modifier: .button.danger
&.danger {
background: #e74c3c;
}
// BEM-style modifier: .button--large
&--large {
padding: 15px 30px;
font-size: 18px;
}
// BEM-style modifier: .button--small
&--small {
padding: 5px 10px;
font-size: 12px;
}
}
CSS Output
.button {
padding: 10px 20px;
background: #3498db;
color: white;
}
.button.primary {
background: #3498db;
}
.button.secondary {
background: #95a5a6;
}
.button.danger {
background: #e74c3c;
}
.button--large {
padding: 15px 30px;
font-size: 18px;
}
.button--small {
padding: 5px 10px;
font-size: 12px;
}
&.secondary (no space) creates .button.secondary (both classes on same element), while & .secondary (with space) would create .button .secondary (descendant selector).
Context-Based Styling with &
You can place & anywhere in the selector, even after another selector:
Parent Context Styling
.button {
background: blue;
color: white;
// When button is inside .dark-theme
.dark-theme & {
background: black;
border: 1px solid white;
}
// When button is inside a nav
nav & {
margin-right: 10px;
}
// When button is a direct child of .toolbar
.toolbar > & {
margin: 5px;
}
}
CSS Output
.button {
background: blue;
color: white;
}
.dark-theme .button {
background: black;
border: 1px solid white;
}
nav .button {
margin-right: 10px;
}
.toolbar > .button {
margin: 5px;
}
Property Nesting
SASS also allows you to nest properties that share the same prefix (like font-, border-, background-). This is less commonly used but can reduce repetition:
Property Nesting Example
.text {
font: {
family: Arial, sans-serif;
size: 16px;
weight: bold;
style: italic;
}
border: {
width: 1px;
style: solid;
color: #ccc;
radius: 4px;
}
background: {
color: white;
image: url('pattern.png');
repeat: no-repeat;
position: center;
}
}
CSS Output
.text {
font-family: Arial, sans-serif;
font-size: 16px;
font-weight: bold;
font-style: italic;
border-width: 1px;
border-style: solid;
border-color: #ccc;
border-radius: 4px;
background-color: white;
background-image: url("pattern.png");
background-repeat: no-repeat;
background-position: center;
}
Property Nesting with Shorthand
You can combine property nesting with shorthand values:
Mixed Property Nesting
.element {
border: 1px solid #ccc {
radius: 4px;
top-color: blue;
}
margin: 0 auto {
top: 20px;
bottom: 20px;
}
}
CSS Output
.element {
border: 1px solid #ccc;
border-radius: 4px;
border-top-color: blue;
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
}
Best Practice: Limit Nesting Depth (Max 3-4 Levels)
While nesting is powerful, over-nesting creates overly specific selectors that are hard to override and can hurt performance.
Over-Nesting (BAD)
// DON'T DO THIS
.page {
.container {
.sidebar {
.widget {
.widget-header {
.widget-title {
color: blue; // Specificity nightmare!
}
}
}
}
}
}
Compiled CSS (Too Specific)
/* This selector is way too specific! */
.page .container .sidebar .widget .widget-header .widget-title {
color: blue;
}
.widget-title differently on a specific page, you'll need an even more specific selector. This leads to specificity wars!
Better Approach (GOOD)
Shallow Nesting with BEM
// Much better!
.widget {
background: white;
padding: 20px;
&__header {
margin-bottom: 15px;
}
&__title {
color: blue;
font-size: 18px;
}
&__content {
line-height: 1.6;
}
&--featured {
border: 2px solid gold;
}
}
Compiled CSS (Perfect Specificity)
.widget {
background: white;
padding: 20px;
}
.widget__header {
margin-bottom: 15px;
}
.widget__title {
color: blue;
font-size: 18px;
}
.widget__content {
line-height: 1.6;
}
.widget--featured {
border: 2px solid gold;
}
&__element for child elements and &--modifier for variations. This keeps specificity low while maintaining clear relationships.
The Danger of Over-Nesting: Specificity Bloat
Let's see a concrete example of why over-nesting is problematic:
Problem: Over-Nested SCSS
.header {
.navigation {
ul {
li {
a {
color: blue;
&:hover {
color: darkblue;
}
}
}
}
}
}
Compiled: Highly Specific Selectors
/* Specificity: (0, 0, 5, 0) - very high! */
.header .navigation ul li a {
color: blue;
}
/* Specificity: (0, 1, 5, 0) - even higher! */
.header .navigation ul li a:hover {
color: darkblue;
}
Now imagine you want to style navigation links differently on a specific page:
Trying to Override (Won't Work)
/* This won't work - not specific enough! */
.special-page .navigation a {
color: red;
}
You'd need an even more specific selector to override it, leading to an endless specificity war.
Solution: Shallow Nesting
Better Approach
.navigation {
ul {
list-style: none;
}
&__item {
display: inline-block;
}
&__link {
color: blue;
&:hover {
color: darkblue;
}
}
}
// Now easy to override when needed
.special-page .navigation__link {
color: red;
}
Real-World Example: Styled Card Component
Let's put everything together with a comprehensive, real-world example:
Complete Card Component SCSS
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.3s, box-shadow 0.3s;
// Hover state for entire card
&:hover {
transform: translateY(-4px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
// Card image
&__image {
width: 100%;
height: 200px;
object-fit: cover;
}
// Card content wrapper
&__content {
padding: 20px;
}
// Card title
&__title {
font-size: 24px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
// Nested anchor inside title
a {
color: inherit;
text-decoration: none;
&:hover {
color: #3498db;
}
}
}
// Card description
&__description {
color: #666;
line-height: 1.6;
margin-bottom: 15px;
}
// Card footer
&__footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 15px;
border-top: 1px solid #eee;
}
// Card button
&__button {
padding: 8px 16px;
background: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
&:hover {
background: #2980b9;
}
}
// Modifier: Featured card
&--featured {
border: 2px solid gold;
.card__title {
color: #f39c12;
}
}
// Modifier: Compact card
&--compact {
.card__image {
height: 120px;
}
.card__content {
padding: 15px;
}
.card__title {
font-size: 18px;
}
}
// Context: Card inside a grid
.grid & {
margin-bottom: 20px;
}
}
& for modifiers and states, and context-based styling. Study this pattern!
Nesting vs Flat Selectors: When to Use Each
Use Nesting When:
- Styles are tightly coupled to a parent component
- You need pseudo-classes or pseudo-elements (
&:hover) - Creating modifier classes (
&--large) - Context-based styling (
.dark-theme &) - Related elements share a namespace (
.card__title)
Use Flat Selectors When:
- Elements can be used independently
- Nesting would exceed 3 levels
- You need low specificity for easy overriding
- Creating utility classes (
.text-center)
Performance Considerations
While nesting doesn't directly impact CSS file size (it compiles to the same output), overly specific selectors can slow down browser rendering slightly. Modern browsers are fast, but deeply nested selectors like .a .b .c .d .e .f take longer to match than .f.
Conclusion
Nesting is a powerful SASS feature that makes stylesheets more readable and maintainable. You've learned how to nest selectors, use the parent selector (&), nest properties, and most importantly, how to avoid the common pitfalls of over-nesting. Keep nesting shallow (3-4 levels max), embrace BEM methodology, and your SASS will be clean, performant, and maintainable!
In the next lesson, we'll explore partials and imports - how to organize your SASS into modular files for better project structure.
Exercise 1: Convert Flat CSS to Nested SCSS
Take this flat CSS and convert it to well-nested SCSS:
.product { padding: 20px; }
.product .product-image { width: 100%; }
.product .product-title { font-size: 24px; }
.product .product-title a { color: blue; }
.product .product-title a:hover { color: darkblue; }
.product .product-price { font-weight: bold; color: green; }
.product .product-button { padding: 10px; background: blue; }
.product .product-button:hover { background: darkblue; }
.product.featured { border: 2px solid gold; }
Convert to BEM-style nested SCSS with proper & usage.
Exercise 2: Build a Navigation Component
Create a fully-styled navigation bar using nested SCSS:
- Create
.navwith nested.nav__list,.nav__item,.nav__link - Add hover and active states using
& - Create a
.nav--darkmodifier for dark theme - Add responsive styles using media queries inside the nesting
- Compile and test with HTML
Exercise 3: Identify Over-Nesting
Review this over-nested SCSS and refactor it:
.page {
.container {
.content {
.article {
.article-header {
.article-title {
h1 {
color: blue;
}
}
}
}
}
}
}
Refactor to a maximum of 2-3 nesting levels while maintaining clear relationships. Use BEM naming where appropriate. Document why your refactored version is better.