Interpolation
A variable can normally go only where a CSS value goes. But sometimes you want a value inside a selector (.btn-primary), a property name (margin-left), a URL or a custom property. Interpolation is the tool for this: wrap any Sass expression in #{ } and Sass inserts its result as text, right there.
In this lesson you will use interpolation in selectors, property names, media queries, URLs, strings and custom properties, and learn when you do not need it.
The #{} syntax
#{expression} evaluates the expression and pastes the result into the surrounding code. The expression can be a variable, maths, or a function call:
$prefix: "ec";
$side: "left";
$img: "hero";
.#{$prefix}-btn {
padding: 1rem;
}
.box {
margin-#{$side}: 2rem;
border-#{$side}-width: 3px;
}
.hero {
background-image: url("/img/#{$img}@2x.webp");
}
.gate::after {
content: "Gate #{1 + 2}";
}.ec-btn {
padding: 1rem;
}
.box {
margin-left: 2rem;
border-left-width: 3px;
}
.hero {
background-image: url("/img/hero@2x.webp");
}
.gate::after {
content: "Gate 3";
}- In the selector,
.#{$prefix}-btnbecame.ec-btn. Notice that the quotes around"ec"disappeared: interpolation removes the quotes from quoted strings, which is what you want in a selector. - In the property names,
#{$side}builtmargin-leftandborder-left-width. - Inside the quoted URL and the
contentstring, the values were inserted into the text, and1 + 2was calculated first.
Generating classes in a loop
Interpolation becomes powerful together with loops. One @each rule can write a whole set of classes. Here is a set of colour classes for a Kerala tourism site's tags:
$tags: (backwaters: #0e7490, hills: #15803d, beaches: #d97706, temples: #b91c1c);
@each $name, $color in $tags {
.tag-#{$name} {
background: $color;
color: white;
}
}.tag-backwaters {
background: #0e7490;
color: white;
}
.tag-hills {
background: #15803d;
color: white;
}
.tag-beaches {
background: #d97706;
color: white;
}
.tag-temples {
background: #b91c1c;
color: white;
}Here is the result on a page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Tag colours</title>
<style>
body { font-family: system-ui, sans-serif; padding: 1rem; }
span { display: inline-block; padding: 0.25rem 0.75rem; border-radius: 999px; margin: 0.25rem; }
/* compiled from the SCSS above */
.tag-backwaters { background: #0e7490; color: white; }
.tag-hills { background: #15803d; color: white; }
.tag-beaches { background: #d97706; color: white; }
.tag-temples { background: #b91c1c; color: white; }
</style>
</head>
<body>
<h1>Explore Kerala</h1>
<span class="tag-backwaters">Backwaters</span>
<span class="tag-hills">Hills</span>
<span class="tag-beaches">Beaches</span>
<span class="tag-temples">Temples</span>
</body>
</html>Add a fifth entry to the map and Sass writes a fifth class. You will learn loops fully in Control Flow.
Custom properties and comments
Two places need interpolation even though they look like normal values:
- CSS custom properties (
--name: value). Sass copies their values as plain text, so--brand: $brand;would output the text$brand. Write--brand: #{$brand};. - Loud comments (
/* */). Write/* Built for #{$client} */to stamp a value into a comment.
$brand: #cc6699;
$space: 8px;
$client: "Rohtak Sweets";
/* Theme for #{$client} */
:root {
--brand: #{$brand};
--space-2: #{$space * 2};
--wrong: $brand;
}/* Theme for Rohtak Sweets */
:root {
--brand: #cc6699;
--space-2: 16px;
--wrong: $brand;
}--wrong shows the bug: the browser receives the meaningless text $brand. --space-2 shows that the expression inside #{ } is calculated first.
When you do not need interpolation
Beginners often wrap everything in #{ }. In normal values it is unnecessary, and it can hurt, because interpolation always produces an unquoted string, not a number. You cannot do maths with the result:
$size: 16px;
.ok {
font-size: $size * 1.25;
}
.not-needed {
font-size: #{$size};
}
.broken {
font-size: #{$size} * 1.25;
}Undefined operation "16px * 1.25".
╷
12 │ font-size: #{$size} * 1.25;
│ ^^^^^^^^^^^^^^^
╵
- 12:14 root stylesheetThe whole file failed because of .broken: #{$size} turned the number into the unquoted text 16px, and Sass cannot multiply text by a number, so it stops with "Undefined operation". Delete the .broken rule and run again: .ok calculates 20px, and .not-needed works but adds nothing.
Also, you do not need interpolation in media queries or in calc() any more. Current Dart Sass understands variables there directly: @media (width >= $tablet) and calc(100% - $sidebar) both work. You will still see @media (min-width: #{$tablet}) and calc(100% - #{$sidebar}) in older code; they produce the same result.
Common mistakes
--brand: $brand;outputs the literal text. Custom properties always need#{$brand}.- Interpolating and then doing maths.
#{$size} * 2multiplies text by a number, which is an error. Do the maths inside,#{$size * 2}, or better, skip interpolation. - Interpolating whole selectors from user data. Keep generated selectors simple (letters, digits, hyphens) so the output stays valid CSS.
- Using interpolation everywhere. Only use it where a plain variable cannot go: selectors, property names, strings, custom properties and comments.
Key points
#{expression}evaluates the expression and inserts the result as text.- Use it in selectors (
.btn-#{$name}), property names (margin-#{$side}), URLs, strings, loud comments and custom property values. - Interpolation removes quotes from quoted strings and always returns an unquoted string, so you cannot do maths on its result.
- Plain variables work in normal values, media queries and
calc()without interpolation. - Loops plus interpolation generate families of classes from a map.
Exercise
A Lucknow coaching centre needs spacing helper classes. Using a map (1: 0.25rem, 2: 0.5rem, 3: 1rem) and a list of sides top, bottom, generate classes like .mt-1 (margin-top: 0.25rem) and .mb-3 (margin-bottom: 1rem). Hint: nest one @each inside another and use string.slice($side, 1, 1) from sass:string to get the first letter of the side.
Show solution
@use "sass:string";
$space: (1: 0.25rem, 2: 0.5rem, 3: 1rem);
$sides: top, bottom;
@each $side in $sides {
@each $step, $size in $space {
.m#{string.slice($side, 1, 1)}-#{$step} {
margin-#{$side}: $size;
}
}
}The outer loop picks a side, the inner loop a size. Interpolation builds both the class name (.mt-1) and the property name (margin-top), giving six classes in total.
Quick Quiz
1. What does .icon-#{$name} compile to when $name: "star"?
Interpolation inserts the value and removes the quotes from a quoted string.
2. Why does --brand: $brand; not work?
Custom property values are not evaluated as SassScript unless you interpolate.
3. What happens with width: #{$size} * 2; when $size: 10px?
Interpolation always returns an unquoted string, so maths on its result fails with "Undefined operation".
4. Where is interpolation needed?
Plain variables work in values and calc(); property names and selectors need #{ }.
Learn Sass with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
