# 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
, andli
are nested within the.container
rule. - 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
&:hover
applies the background-color only when theli
element is hovered.
This results inli:hover
where the&
is replaced by the parent selector. - Without the
&
selector, the:hover
pseudo-class would not work in the nested context, as it would not be associated with the parentli
selector. This would result in an invalid rule. This would result inli :hover
(with a space betweenli
and: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:
&:hover
applies the hover style to the base.btn
class.&.btn-primary
applies styles when the element has both classes:.btn
and.btn-primary
.&:hover
in the.btn-primary
selector applies hover effect just for the.btn-primary
variant.
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-primary
appends the nested rule.btn-primary
to the parent.btn
selector. (You must nest&.btn-primary
and not just the class name without the prefix&-primary
like 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
.container
selector. - This means that the styles inside the media query will only be applied to the
.container
element 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.html
Copied!
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)