Inheritance in SASS
This article explains inheritance in SASS.
We will explain inheritance in SASS with practical examples.
YouTube Video
Inheritance in SASS
Inheritance in SASS (@extend) is a mechanism that allows you to apply the styles of one selector to another without duplication. Since the same styles are 'combined' and output for multiple elements in the markup, the resulting CSS is less likely to be redundant; however, if used improperly, it can lead to unintended selector merges.
Basics: How to use @extend
Below is a basic example where .btn--primary inherits the styles of .btn. @extend is a directive that extends the target selector.
1// Base button styles
2.btn {
3 padding: 0.5rem 1rem;
4 border-radius: 4px;
5 border: 1px solid #ccc;
6 background: white;
7 color: #333;
8}
9
10/* Primary button extends the base .btn */
11.btn--primary {
12 @extend .btn;
13 background: #007bff;
14 color: white;
15}- By using
@extend,.btn--primaryinherits the base styles of.btnand overrides only the necessary parts.
Generated CSS
1.btn, .btn--primary {
2 padding: 0.5rem 1rem;
3 border-radius: 4px;
4 border: 1px solid #ccc;
5 background: white;
6 color: #333;
7}
8
9.btn--primary {
10 background: #007bff;
11 color: white;
12}Best Practice: Using Placeholders (%placeholder)
Placeholder selectors (%name) are selectors that are not output to CSS. They are widely used especially when you want to safely share common styles exclusively for inheritance across multiple components.
1// %placeholder will not be output directly
2%card-base {
3 padding: 1rem;
4 border-radius: 6px;
5 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
6 background: #fff;
7}
8
9/* Components extend the placeholder */
10.card {
11 @extend %card-base;
12 border: 1px solid #eee;
13}
14
15.panel {
16 @extend %card-base;
17 border: 1px solid #ddd;
18}.cardand.panelboth inherit from%card-base, allowing them to share common styles while adding differences as needed.
Generated CSS
1.card, .panel {
2 padding: 1rem;
3 border-radius: 6px;
4 box-shadow: 0 1px 3px rgba(0,0,0,0.08);
5 background: #fff;
6}
7
8.card {
9 border: 1px solid #eee;
10}
11
12.panel {
13 border: 1px solid #ddd;
14}Multiple Inheritance (Multiple @extend)
You can inherit multiple placeholders or classes at the same time. While style reusability is improved, it's important to keep track of which rules are combined with which selectors.
1%btn-base {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 border-radius: 3px;
5}
6
7%btn-large {
8 padding: 0.75rem 1.5rem;
9 font-size: 1.125rem;
10}
11
12/* Composite button that extends both placeholders */
13.btn--lg {
14 @extend %btn-base;
15 @extend %btn-large;
16 background: #222;
17 color: #fff;
18}- This is an example where a button inherits two placeholders, one for 'base' and one for 'size'.
.btn--lginherits both%btn-baseand%btn-large, combining the basic layout with larger sizing.
Generated CSS
1.btn--lg {
2 display: inline-block;
3 /* %btn-large overrides the padding from %btn-base */
4 padding: 0.75rem 1.5rem;
5 border-radius: 3px;
6 font-size: 1.125rem;
7 background: #222;
8 color: #fff;
9}@extend Behavior (Merge Mechanism) and Cautions About 'Selector Explosion'
@extend outputs all matching selectors merged together, which can sometimes result in unintended combinations of selectors.
The following example shows how the output can increase when the same base class is extended in multiple places.
1/* Many components extend .utility */
2/* A generic utility class */
3.utility {
4 margin-bottom: 1rem;
5}
6
7/* Nested selectors that extend .utility */
8.header {
9 @extend .utility;
10 .title {
11 font-weight: bold;
12 }
13}
14
15.footer {
16 @extend .utility;
17 .note {
18 color: #888;
19 }
20}
21
22.article {
23 @extend .utility;
24 .content {
25 line-height: 1.6;
26 }
27}
28
29.sidebar {
30 @extend .utility;
31 .section {
32 padding: 1rem;
33 }
34}- When multiple components inherit
.utility, selectors are merged into one and, in large-scale projects, this can cause CSS to bloat.
Generated CSS
1.utility,
2.header,
3.footer,
4.article,
5.sidebar {
6 margin-bottom: 1rem;
7}
8
9.header .title {
10 font-weight: bold;
11}
12
13.footer .note {
14 color: #888;
15}
16
17.article .content {
18 line-height: 1.6;
19}
20
21.sidebar .section {
22 padding: 1rem;
23}@extend and .class vs Element Selectors (Tags) — Priority and Side Effects
@extend can be applied not only to classes but also to element selectors. However, extending elements increases the affected scope, raising the risk of rules being unintentionally applied in unintended places.
Below is an example of extending an element selector and the effect it can have.
1/* Extending an element selector (not recommended) */
2h1 {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7/* If you extend h1, the resulting selector will include your class with h1 */
8.title {
9 @extend h1;
10 color: #333;
11}
12
13/* Output becomes:
14h1, .title { font-size: 2rem; margin-bottom: 0.5rem; }
15*/- In this example, inheriting the element selector
h1causes.titleto be merged with the same styles ash1. - While it may seem convenient in small-scale cases, as your project grows, rules for
h1could unexpectedly combine with.title, making styles more complex and reducing maintainability. Therefore, designing styles primarily around classes and placeholders makes them easier to maintain.
Generated CSS
1h1,
2.title {
3 font-size: 2rem;
4 margin-bottom: 0.5rem;
5}
6
7.title {
8 color: #333;
9}Use Cases for @extend and !optional
If you specify !optional with @extend, you can suppress errors when the inheritance target does not exist. This is particularly useful in library-like code or cases where placeholders are conditionally defined.
The following is an example of safely attempting to inherit a class that may not exist, using !optional.
1/* Try to extend a class that might not exist */
2.component {
3 @extend .maybe-existing !optional;
4 padding: 1rem;
5}- If
.maybe-existingdoes not exist, nothing happens and it is skipped. You can use this when you want to attempt an extension safely.
Generated CSS
1.component {
2 padding: 1rem;
3}Comparison of @extend and Mixins (@mixin / @include)
@extend and @mixin sometimes have overlapping purposes, but their outputs and use cases differ.
-
@extend- The generated CSS reduces redundancy by merging selectors.
- Because selectors are merged after generation, unintended combinations may occur.
- Parameters cannot be passed (though this can be compensated for by combining placeholders).
-
@mixin/@include- Each call duplicates the styles (causing redundant output).
- You can pass parameters and include logic such as conditionals or loops.
- Output is more predictable, but file size increases.
Below is a comparison using both @mixin and @extend to implement the same button styles.
1/* Mixin approach */
2@mixin btn-styles($bg, $color) {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 background: $bg;
6 color: $color;
7 border-radius: 4px;
8}
9
10/* Use mixin */
11.btn {
12 @include btn-styles(white, #333);
13}
14
15.btn--primary {
16 @include btn-styles(#007bff, white);
17}
18
19/* Extend approach (shared placeholder) */
20%btn-base {
21 display: inline-block;
22 padding: 0.5rem 1rem;
23 border-radius: 4px;
24}
25
26.btn2 {
27 @extend %btn-base;
28 background: white;
29 color: #333;
30}
31
32.btn2--primary {
33 @extend %btn-base;
34 background: #007bff;
35 color: white;
36}@mixinlets you flexibly insert styles, while@extendconsolidates output efficiently, so you can use each as appropriate depending on the use case.
Generated CSS
Output from @mixin
1.btn {
2 display: inline-block;
3 padding: 0.5rem 1rem;
4 background: white;
5 color: #333;
6 border-radius: 4px;
7}
8
9.btn--primary {
10 display: inline-block;
11 padding: 0.5rem 1rem;
12 background: #007bff;
13 color: white;
14 border-radius: 4px;
15}Output from @extend
1.btn2,
2.btn2--primary {
3 display: inline-block;
4 padding: 0.5rem 1rem;
5 border-radius: 4px;
6}
7
8.btn2 {
9 background: white;
10 color: #333;
11}
12
13.btn2--primary {
14 background: #007bff;
15 color: white;
16}Practical Guidelines
SASS inheritance is a powerful feature for increasing style reusability. However, incorrect use can make style merging complex and reduce maintainability. Below are some key points for using inheritance safely and efficiently.
- Use placeholders for purely common component styles such as structure and layout. Additionally, if dynamic parameterization is required, you can use
@mixin. - You should avoid directly inheriting HTML elements such as
h1. Unintended selector combinations may occur, potentially resulting in unexpected CSS being generated. - Using naming conventions like BEM or clear prefixes to show what each placeholder is for helps keep things safe.
- It is safer to use
@extendwithin the same file. Especially in large projects, it is advisable to design inheritance within the scope of each component to make it easier to track inheritance relationships.
Summary
The @extend feature in SASS is a convenient way to efficiently reuse common styles and ensure design consistency. However, as selector combinations can easily become complex, it is necessary to use this feature carefully and in a limited scope. By grouping shared styles with placeholder selectors (%placeholder) and using @mixin for parts that require dynamic parameters, you can maintain a simple and easily maintainable design.
You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.