In recent years, CSS has transformed from writing simple rulesets to a powerful engine for creating beautiful user experiences. But as projects grow more complicated, as does the CSS that is being written, it gets harder to manage and even harder to make changes. Native CSS has implemented some new tools to help with this, like Grid and variables, but CSS preprocessors take writing and managing CSS to the next level.
Aside from writing flexible, reusable rulesets, CSS preprocessors like Sass bring powerful program logic to CSS. That’s right – using Sass, you can perform arithmetic operations, write functions (@mixins
), and even use control structures like if/else statements and loops. In this article, we’ll take a look at all the ways you can write programmable logic in CSS. The syntax being used is SCSS.
TIP: If you’ve never used SCSS before, I recommend checking out this guide to get started. But fret not! If you just want to see what SCSS has to offer as far a program logic goes, you can do so in CodePen.io
The easiest way to get started with programming in SCSS is by doing math. There are five arithmetic operations you can perform:
+
-
*
/
%
This allows you to use math in your font sizing, or even create nice gradients in styles. Here’s an example:
$scale: 1.5; $heading-base: 65px; h1 { font-size: $heading-base; } h2 { font-size: $heading-base / $scale; }
Thanks to a simple divisor, your heading font sizes are now scaled, relative to each other. And later, you’ll see how to make that even better.
One thing to note if you can only use these operators on similar units. So you can’t multiply px
and em
for example. You can use operators on hex colors though to make some interesting colors! As a general rule, I try to use only numbers, without units, for math in SCSS when I can.
But using math in CSS is only the start. Let’s take a look at even more powerful programming features of SCSS.
Repeatable code is an important aspect of any programming language, and SCSS is no different. Using @mixin
, you can create a set of code that’s reusable. Here’s a simple example:
@mixin button() { background: #880000; color: #FFFFFF; border-radius: 10px; padding: 15px; text-align: center; } a.button { @include button; }
In this mixin, we have a set of common rules for all buttons. Then, as we declare something that should have those rules, we can use the @include
directive to import that ruleset. But it gets better. You can pass variables to mixins as well. Let’s say, for example, you want to control the color of the button:
@mixin button($color) { background: $color; color: #FFFFFF; border-radius: 10px; padding: 15px; text-align: center; } a.button { @include button(#008800); }
Finally, if you want a default changeable color, you can set an optional variable:
@mixin button($color: #880000) {...}
Then if you send a color, that’s used. And if not, the default, #880000
is used.
You can imagine this is pretty powerful for having, controlling, and modifying a set of common styles. If the general style for all buttons ever changes, you’d only need to change it in the @mixin
.
With the ability to generalize code and make it repeatable, there’s another thing you might want to add to your toolset: decision making.
Some would argue that programming is pretty much making specific decisions based on some condition. And while I’ll save the philosophical discussion for a different article, it is true that decision making unlocks a lot of power in code to control the flow of a program.
In SCSS, this is less about reacting to real-time interactions (that’s what media queries are for), and more about being able to consolidate and better manage large blocks of rulesets. Let’s look at an example:
@mixin shape($w, $h, $circle: false) { width: $w; height: $h; background: #FF0000; @if $circle { border-radius: 50%; } } .square { @include shape(100px, 100px); } .circle { @include shape(100px, 100px, true); }
In this mixin, we create a general shape (well, really a square) but thanks to the @if
, we can use the same mixin to create a circle as well. In a more practical example, and one that uses @else
to, you might have a mixin to manage the base styles for a “dark mode”:
@mixin theme-styles($dark-mode: false) { @if $dark-mode { //Set styles based on dark mode variables } @else { //Set styles based on light mode variables } }
So math lets us make better calculations for our measurements, mixins let us generalize rulesets to make them easier to manage from a high level, and if/else statements let us take that management one step further by conditionally adding rulesets and properties as needed.
But perhaps the biggest time saver in SCSS program logic is loops.
Loops let you repeat a task as long as some condition is true. There are three types of loops in SCSS:
@for
@while
@each
The @for
loop is a simple counting loop. You can perform a task a specific amount of times. A great example of this is easily creating those scaled heading sizes we looked at earlier:
$heading-size: 65px; @for $i from 1 through 6 { h#{$i} { font-size: $base-size; } $heading-size: $heading-size / 1.25; }
Here, we have a variable we’ve defined as $i
(commonly used in loops; you can think of it as “index” or “interator”), which starts at 1 and increments up to 6. Then we have a little bit of clever syntax in SCSS that lets us use variable names in selectors. First, we use the hash sign (#
) to indicate we’re starting a variable name, then we wrap the variable in curly braces ({ }
).
So here we’re using $i
to define each of the 6 heading tags. Then we’re setting a font size based on the $heading-size
variable. At the end of the loop, we divide that variable by 1.25, making it smaller, then doing the same thing for the next heading tag. So the rendered CSS looks like this:
h1 { font-size: 65px; } h2 { font-size: 52px; } h3 { font-size: 41.6px; } h4 { font-size: 33.28px; } h5 { font-size: 26.624px; } h6 { font-size: 21.2992px; }
Nice, mathematically scaled heading sizes!
@while
loops are similar, but we’re not relegated to a single incrementor. We can do something like create a scaled gradient, based on some multiplier:
$gradient: 1; $i: 1; @while $gradient >= 0.1 { main p:nth-child(#{$i}) { background: rgba(0,0,240, $gradient); min-height: 20px; margin: 0; } $gradient: $gradient - 0.1; $i: $i + 1; }
In this example, we’re targeting <p>
inside a <main>
element, and stepping down a blue gradient for each child. We have 2 counters here: the $gradient
, which controls the opacity, and $i
, which is counting for nth-child
.
This can also be used to create a set of gradient classes, or create a nice visual effect for long content.
Perhaps the most interesting loop in SCSS is the @each
loop. This let’s you step through variable lists(SCSS’s version of arrays). You can do some pretty nifty stuff with the right combination of info. Let’s say you want a series of alerts. You can loop through an array of colors like this:
$alerts: red, blue, green, yellow; @each $alert in $alerts { .alert-#{$alert} { background: $alert; color: #FFFFFF; padding: 10px; text-align: center; } }
This says, for each entry in the $alerts
list, create a class with the entry’s name and set the entry as the background color.
You can take this one step further with maps – key → value pairs in lists. If you wanted more descriptive names, you could do this:
$alerts: ("error" : red, "neutral" : blue, "good" : green, "warning" : yellow ); @each $name, $color in $alerts { .alert-#{$name} { background: $color; color: #FFFFFF; padding: 10px; text-align: center; } }
This is an effective away to quickly work through large sets of repetitive CSS. You can imagine a custom icon font you want to use, where you have a map of the icon name, and the unicode value for the glyph.
With that, you’re ready to start coding your own SCSS. You now have the syntax you need to do math, if statements, and loops for your styles. What are you going to use this new-found power for? Let us know in the comments!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "A beginner’s guide to programming for CSS with Sass"
Hey Joe,
First off, thanks for all of the tips here on how to make our CSS more powerful and modular.
I just felt like it is worth noting that in the example of the for loop that the line at the end “$heading-size: $heading-size / 1.25;” will actually make it so that $heading-size remains at the final value before the loop is done. I think this is important because if you have another style right after it something like:
h1.double-size { font-size: $heading-size * 2; }
Then it would not be double the size you are expecting most likely. Do you know if there is a way to have the value reset to it’s original after the loop is finished without making the code too clunky?
Again, this is a great piece and thank you for sharing it!