# 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!
1
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!
1
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!
1
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, and li 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!
1
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 the li element is hovered.
    This results in li: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 parent li selector. This would result in an invalid rule. This would result in li :hover (with a space between li and :hover) and not the desired li: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!
1
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!
1
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!
1
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:

  1. &:hover applies the hover style to the base .btn class.
  2. &.btn-primary applies styles when the element has both classes: .btn and .btn-primary.
  3. &: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!
1
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!
1
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!
1
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!
1
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!
1
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!
1
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!
1
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!
1
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!
1
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!
1
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.

    https://caniuse.com/#feat=css-nesting

# Practice

Practice CSS Nesting by executing the steps in the following repository: CSS Nesting Exercise (opens new window)

Last Updated: 3/4/2025, 1:08:34 PM