# CSS Nesting
CSS Nesting is a feature that allows you to nest CSS selectors within other selectors, making your stylesheets more organized and readable. It mirrors the structure of your HTML, and it can help avoid repetitive selector declarations. This chapter will explore the fundamentals of CSS nesting and how to use it effectively to enhance your CSS development workflow.
# Basic Nesting
CSS nesting lets you write nested rules that follow the structure of your HTML, by creating a parent/child relationship between selectors. This is especially useful in complex layouts and for organizing CSS rules that target specific elements inside other elements.
# Un-nested CSS
Let's begin with a small example of un-nested CSS. Suppose you have the following HTML structure:
<div class="container"> <p class="text">This is some text inside the container.</p> <ul> <li>Item 1</li> <li>Item 2</li> </ul> </div>Copied!
2
3
4
5
6
7
8
The un-nested CSS for this structure might look like this:
.container { background-color: lightgray; padding: 10px; } .container .text { color: darkblue; font-size: 1.2rem; } .container ul { list-style-type: none; padding-left: 0; } .container ul li { padding: 5px; border-bottom: 1px solid gray; }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Here, each rule is written as a separate declaration, with the parent-child relationships defined by descendant
selectors (e.g., .container .text).
# Nested CSS
The nested version of the above code would look like this:
.container { background-color: lightgray; padding: 10px; .text { color: darkblue; font-size: 1.2rem; } ul { list-style-type: none; padding-left: 0; li { padding: 5px; border-bottom: 1px solid gray; } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In this nested version:
- The rules for
.text,ul, andliare nested within the.containerrule. - This ensures that these rules are intended to apply to elements inside the
.container.
The result is the same as the un-nested version but the structure is more readable and maintainable.
# The & Selector
The & selector is a special character used in CSS nesting to represent the parent selector. It provides greater
control over the relationship between nested rules and is useful in many cases. Using the & selector is not always
necessary, especially when using descendant selectors, but there are several situations where the & is required, such
as when using pseudo-classes, pseudo-elements, and custom classes.
# Pseudo-classes
When using pseudo-classes with nested rules, the & selector is necessary to ensure that the pseudo-class applies to
the correct element. Consider this example where we want to style the :hover state of the <li> elements inside the
.container ul:
.container { background-color: lightgray; padding: 10px; ul { list-style-type: none; padding-left: 0; li { padding: 5px; border-bottom: 1px solid gray; &:hover { /* Using & to refer to the parent li (= concatenate with the li selector) */ background-color: #f0f0f0; } } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- In this case, the
&:hoverapplies the background-color only when thelielement is hovered.
This results inli:hoverwhere the&is replaced by the parent selector. - Without the
&selector, the:hoverpseudo-class would not work in the nested context, as it would not be associated with the parentliselector. This would result in an invalid rule. This would result inli :hover(with a space betweenliand:hover) and not the desiredli:hover!
# Pseudo-elements
Similarly to pseudo-classes, when using pseudo-elements within nested rules, the & selector is needed to correctly
target specific parts of the parent element. Let's see how to style the ::before pseudo-element of the li elements:
.container { background-color: lightgray; padding: 10px; ul { list-style-type: none; padding-left: 0; li { padding: 5px; border-bottom: 1px solid gray; &::before { /* Using & to refer to the parent li */ content: "*** "; color: greenyellow; } &::after { /* Using & to refer to the parent li */ content: " ***"; color: greenyellow; } } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Here, the &::before and &::after selectors add some stars before and after each li element, as intended.
# Attribute Selectors
The & selector can also be used with attribute selectors to target elements with specific attributes.
.container { &[data-active="true"] { background-color: green; } }Copied!
2
3
4
5
# Custom Classes
The & selector is also useful when nesting styles for custom classes that modify the appearance of a component.
For example, let's see how to style .btn and .btn-primary elements:
Let's examine the CSS code in the example above:
.btn { padding: 10px 20px; border: none; border-radius: 5px; background-color: #f0f0f0; color: #333; cursor: pointer; &:hover { /* Using & to refer to the parent .btn */ background-color: #e0e0e0; } &.btn-primary { /* Using & to refer to the parent .btn when it has the class .btn-primary */ background-color: #3498db; color: white; &:hover { /* Using & to refer to the parent .btn.btn-primary */ background-color: #2980b9; } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In this example, the & selector is used in three different ways:
&:hoverapplies the hover style to the base.btnclass.&.btn-primaryapplies styles when the element has both classes:.btnand.btn-primary.&:hoverin the.btn-primaryselector applies hover effect just for the.btn-primaryvariant.
IMPORTANT
- When using the
&selector, make sure to place it at the beginning of the nested rule. - For pseudo-classes, pseudo-elements and attribute selectors, the
&selector should be followed by the pseudo-class, pseudo-element or attribute.
(e.g.,&:hover,&::before,&::after,&[data-active="true"], ...). - For custom classes, the
&selector appends the nested rule to the parent selector.
For example,&.btn-primaryappends the nested rule.btn-primaryto the parent.btnselector. (You must nest&.btn-primaryand not just the class name without the prefix&-primarylike we do for pseudo-classes and pseudo-elements).
# Nested Media Queries
You can also nest media queries within a selector to apply styles for specific viewport sizes. This is a great way to organize your responsive code. Let's see an example:
.container { background-color: lightgray; padding: 10px; width: 100%; @media (min-width: 768px) { /* Applying styles for tablets and above */ width: 80%; max-width: 800px; margin: 0 auto; } }Copied!
2
3
4
5
6
7
8
9
10
11
In this example:
- The media query is nested within the
.containerselector. - This means that the styles inside the media query will only be applied to the
.containerelement when the viewport is at least 768px wide.
NOTE
The & selector is not required for media queries, as they are scoped to the parent selector by default.
# Component-based Nesting
CSS Nesting can be used in component-based architectures to organize the CSS for each component.
Let's see an example of a card and a button component using nested CSS. We will have four CSS files: base.css that contains the basic
styling, and card.css and button.css, that contain component-specific styles. The styles.css file will import all
the other CSS files and will be used as the main stylesheet for the project.
With this approach, each component’s CSS is isolated in its own file, and you have to link only the styles.css file in your HTML files.
NOTE
Component-based architecture organizes styles into separate files for each component, making it easier to manage and maintain styles in large projects.
This same approach we're going to use in your front-end group project.
📂css/ ├── 📄styles.css ├── 📄base.css ├── 📂components/ │ ├── 📄card.css │ └── 📄button.css 📄index.htmlCopied!
2
3
4
5
6
7
# css/base.css:
* { padding: 0; margin: 0; box-sizing: border-box; } html { font-family: Arial, sans-serif; font-size: 16px; } body { margin: 1rem; }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
# css/components/card.css:
.card { background-color: lightblue; border-radius: 5px; padding: 10px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); .card-title { font-size: 1.2rem; margin-bottom: 5px; } .card-text { color: #333; } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# css/components/button.css:
.btn { padding: 10px 20px; border: none; border-radius: 5px; background-color: #f0f0f0; color: #333; cursor: pointer; &:hover { background-color: #e0e0e0; } &.btn-primary { background-color: #3498db; color: white; &:hover { background-color: #2980b9; } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# css/styles.css:
@import url('base.css'); @import url('components/card.css'); @import url('components/button.css');Copied!
2
3
NOTE
The @import rule can also be a string with the URL of the CSS file to import.
For example: @import url('base.css'); is equivalent to @import "base.css";.
# index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Component-based CSS</title> <link rel="stylesheet" href="css/styles.css"> </head> <body> ... </body> </html>Copied!
2
3
4
5
6
7
8
9
10
# CSS Variables
Now, let's use CSS variables to define the values used for the styles in the component example:
# css/base.css:
:root { --base-font: sans-serif; --base-margin: 20px; } body { font-family: var(--base-font); margin: var(--base-margin); }Copied!
2
3
4
5
6
7
8
9
# css/components/card.css:
:root { --card-bg-color: lightblue; --card-text-color: #333; --card-border-radius: 5px; --card-padding: 10px; --card-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); --card-title-font-size: 1.2rem; --card-title-margin-bottom: 5px; } .card { background-color: var(--card-bg-color); border-radius: var(--card-border-radius); padding: var(--card-padding); box-shadow: var(--card-shadow); .card-title { font-size: var(--card-title-font-size); margin-bottom: var(--card-title-margin-bottom); } .card-text { color: var(--card-text-color); } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# css/components/button.css:
:root { --btn-padding-y: 10px; --btn-padding-x: 20px; --btn-border-radius: 5px; --btn-bg-color: #f0f0f0; --btn-text-color: #333; --btn-hover-bg-color: #e0e0e0; --btn-primary-bg-color: #3498db; --btn-primary-text-color: white; --btn-primary-hover-bg-color: #2980b9; } .btn { padding: var(--btn-padding-y) var(--btn-padding-x); border: none; border-radius: var(--btn-border-radius); background-color: var(--btn-bg-color); color: var(--btn-text-color); cursor: pointer; &:hover { background-color: var(--btn-hover-bg-color); } &.btn-primary { background-color: var(--btn-primary-bg-color); color: var(--btn-primary-text-color); &:hover { background-color: var(--btn-primary-hover-bg-color); } } }Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Un-nested vs. Nested CSS
CSS Nesting provides a powerful way to organize and structure your stylesheets, enabling you to write more readable,
maintainable, and scalable CSS code. By understanding how to nest selectors, use the & selector, nest media queries,
and apply nesting to component-based architectures, you can effectively leverage nesting to enhance your CSS workflows
and create more organized projects. This approach will help you write more efficient code and will be useful when
creating more complex applications.
| Feature | Un-nested CSS | Nested CSS |
|---|---|---|
| Readability | Can be less readable for complex structures; parent-child relationships may be unclear. | Generally more readable; parent-child relationships are clearly defined by nesting. |
| Maintainability | Can be harder to maintain, especially when refactoring; requires more repetition. | Easier to maintain, as modifications to parent selectors are localized. Reduces repetition. |
| Organization | Rules are separate and can be scattered, which makes it harder to follow a component structure. | Rules are grouped by selector, making it easier to understand the structure. Improves organization of related styles. |
| Specificity | Usually involves long descendant selectors that may lead to high specificity and conflicts if you are not careful. | Can lead to lower specificity in many cases, because direct descendant selectors are not necessary. |
| Code Repetition | Higher code repetition, especially when targeting nested elements. | Reduces code repetition, as you don't need to repeat parent selectors. |
| CSS Size | Can result in larger CSS files due to repetition of selectors. | Can result in smaller CSS files, as nesting avoids repetitive selectors. |
| Learning Curve | Easier to start with for beginners, as there is no nesting concept. | Requires a basic understanding of nesting, and the & selector, but it's very easy to learn with practice. |
| Debugging | May require more effort to locate the relevant style rules for deeply nested elements. | Easier to find related styles, specially when using the browser dev tools. |
| Performance | Has no performance overhead in comparison with nested CSS. | Has no performance overhead in comparison with un-nested CSS. |
| Workflow | May require more manual work to organize the CSS file. | Better workflow with component based styling. Makes it easier to manage styles in complex projects. |
In summary, while un-nested CSS is straightforward for simple projects, nested CSS offers significant advantages in terms of readability, maintainability, organization, and code reduction, making it a better choice for more complex applications. Both, un-nested and nested CSS, offer the same performance.
NOTE
- CSS nesting can lead to overly complex stylesheets if overused, and recommend keeping nesting levels shallow (e.g., no more than 3 to 4 levels deep).
- When working on a project, consider using a combination of un-nested and nested CSS based on the project's complexity
- CSS nesting is supported in all modern browsers.
# Practice
Practice CSS Nesting by executing the steps in the following repository: CSS Nesting Exercise (opens new window)
