惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
爱范儿
爱范儿
D
DataBreaches.Net
Simon Willison's Weblog
Simon Willison's Weblog
S
Secure Thoughts
S
SegmentFault 最新的问题
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
P
Proofpoint News Feed
The Hacker News
The Hacker News
T
ThreatConnect
N
News and Events Feed by Topic
T
Threatpost
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - Franky
Recorded Future
Recorded Future
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Project Zero
Project Zero
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
F
Future of Privacy Forum
F
Full Disclosure
Cyberwarzone
Cyberwarzone
J
Java Code Geeks
李成银的技术随笔
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
H
Hacker News: Front Page
人人都是产品经理
人人都是产品经理
博客园_首页
Scott Helme
Scott Helme
Google DeepMind News
Google DeepMind News
美团技术团队
Malwarebytes
Malwarebytes
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
T
The Exploit Database - CXSecurity.com
G
GRAHAM CLULEY
Recent Announcements
Recent Announcements
C
CXSECURITY Database RSS Feed - CXSecurity.com

CSS-Tricks

Revealing Text With CSS letter-spacing | CSS-Tricks Technical Writing in the AI Age | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks Cross-Document View Transitions: Scaling Across Hundreds of Elements | CSS-Tricks The State of CSS Centering in 2026 | CSS-Tricks Stack Overflow: When We Stop Asking | CSS-Tricks Cross-Document View Transitions: The Gotchas Nobody Mentions | CSS-Tricks What’s !important #11: 3D Voxel Scenes, Flying Focus, CSS Syntaxes, and More | CSS-Tricks Computing and Displaying Discounted Prices in CSS | CSS-Tricks rotateX() | CSS-Tricks rotateY() | CSS-Tricks rotateZ() | CSS-Tricks rotate() | CSS-Tricks Soon We Can Finally Banish JavaScript to the ShadowRealm | CSS-Tricks Using CSS corner-shape For Folded Corners | CSS-Tricks A Scrollytelling Gift for Mum on Mother’s Day 2026 | CSS-Tricks Google’s Prompt API | CSS-Tricks Making Zigzag CSS Layouts With a Grid + Transform Trick | CSS-Tricks Fixed-Height Cards: More Fragile Than They Look | CSS-Tricks What’s !important #10: HTML-in-Canvas, Hex Maps, E-ink Optimization, and More | CSS-Tricks The Importance of Native Randomness in CSS | CSS-Tricks contrast() | CSS-Tricks contrast-color() | CSS-Tricks Let’s Use the Nonexistent ::nth-letter Selector Now | CSS-Tricks Quick Hit #126 Recreating Apple’s Vision Pro Animation in CSS | CSS-Tricks Quick Hit #125 Enhancing Astro With a Markdown Component | CSS-Tricks Quick Hit #124 Markdown + Astro = ❤️ | CSS-Tricks Quick Hit #123 What’s !important #9: clip-path Jigsaws, View Transitions Toolkit, Name-only Containers, and More | CSS-Tricks A Well-Designed JavaScript Module System is Your First Architecture Decision | CSS-Tricks hypot() | CSS-Tricks The Radio State Machine | CSS-Tricks 7 View Transitions Recipes to Try | CSS-Tricks Quick Hit #122 Quick Hit #121 Selecting a Date Range in CSS | CSS-Tricks saturate() | CSS-Tricks justify-self | CSS-Tricks Quick Hit #120 Alternatives to the !important Keyword | CSS-Tricks Quick Hit #119 New CSS Multi-Column Layout Features in Chrome | CSS-Tricks Quick Hit #118 Making Complex CSS Shapes Using shape() | CSS-Tricks Quick Hit #117 Front-End Fools: Top 10 April Fools’ UI Pranks of All Time | CSS-Tricks Sniffing Out the CSS Olfactive API | CSS-Tricks What’s !important #8: Light/Dark Favicons, @mixin, object-view-box, and More | CSS-Tricks Quick Hit #116 Form Automation Tips for Happier User and Clients | CSS-Tricks Quick Hit #115 Generative UI Notes | CSS-Tricks Quick Hit #114 Quick Hit #113 Experimenting With Scroll-Driven corner-shape Animations | CSS-Tricks Quick Hit #112 JavaScript for Everyone: Destructuring | CSS-Tricks Quick Hit #111 Quick Hit #110 What’s !important #7: random(), Folded Corners, Anchored Container Queries, and More | CSS-Tricks 4 Reasons That Make Tailwind Great for Building Layouts | CSS-Tricks Quick Hit #109 Quick Hit #108 Abusing Customizable Selects | CSS-Tricks Quick Hit #107 The Value of z-index | CSS-Tricks Quick Hit #106 The Different Ways to Select <html> in CSS Quick Hit #105 Popover API or Dialog API: Which to Choose? Quick Hit #104 What’s !important #6: :heading, border-shape, Truncating Text From the Middle, and More Yet Another Way to Center an (Absolute) Element An Exploit ... in CSS?! Quick Hit #103 A Complete Guide to Bookmarklets Quick Hit #102 Loading Smarter: SVG vs. Raster Loaders in Modern Web Design Potentially Coming to a Browser :near() You Quick Hit #101 Distinguishing "Components" and "Utilities" in Tailwind Quick Hit #100 Spiral Scrollytelling in CSS With sibling-index() Interop 2026 Quick Hit #99 What’s !important #5: Lazy-loading iframes, Repeating corner-shape Backgrounds, and More Quick Hit #98 Making a Responsive Pyramidal Grid With Modern CSS Approximating contrast-color() With Other CSS Features Quick Hit #97 Trying to Make the Perfect Pie Chart in CSS Quick Hit #96 Quick Hit #95 CSS Bar Charts Using Modern Functions Quick Hit #94 No Hassle Visual Code Theming: Publishing an Extension Quick Hit #93
Sass Techniques from the Trenches
CSS-Tricks · 2019-01-08 · via CSS-Tricks

Having been in the web development industry for more than 14 years, I’ve seen and written my fair share of good and bad CSS. When I began at Ramsey Solutions five years ago, I was introduced to Sass. It blew my mind how useful it was! I dove right in and wanted to learn everything I could about it. Over the past five years, I’ve utilized a number of different Sass techniques and patterns and fell in love with some that, to steal Apple’s phrase, just work.

In this article, I’ll explore a wide range of topics:

In my experience, finding the balance between simple and complex is the crucial component to making great software. Software should not only be easy for people to use, but for you and other developers to maintain in the future. I’d consider these techniques to be advanced, but not necessarily clever or complex, on purpose!

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?”

—The Elements of Programming and Style (2nd Edition), Chapter 2

With that in mind, let’s first look at Sass’ ampersand.


The power of the ampersand

There are many different naming conventions you can use to organize your CSS. The one I enjoy using the most is SUIT, a variation of BEM (which is short for Block, Element, Modifier). If you’re unfamiliar with SUIT or BEM, I’d recommend taking a peek at one or both of them before moving on. I’ll be using the SUIT convention throughout the rest of this article.

Whatever naming convention you choose, the base idea is that every styled element gets its own class name, prepended with the component name. This idea is important for how some of the following organization works. Also, this article is descriptive, not prescriptive. Every project is different. You need to do what works best for your project and your team.

The ampersand is the main reason I like to use SUIT, BEM, and conventions like them. It allows me to use nesting and scoping without either biting back with specificity. Here’s an example. Without using the ampersand, I would need to create separate selectors to create -title and -content elements.

.MyComponent {
  .MyComponent-title {}
}

.MyComponent-content {}

// Compiles to
.MyComponent .MyComponent-title {} // Not what we want. Unnecessary specificity!
.MyComponent-content {} // Desired result

When using SUIT, I want the second result for -content to be how I write all my selectors. To do so, I would need to repeat the name of the component throughout. This increases my chance to mistype the name of the component as I write new styles. It’s also very noisy as it ends up ignoring the beginning of many selectors which can lead to glossing over obvious errors.

.MyComponent {}
.MyComponent-title {}
.MyComponent-content {}
.MyComponent-author {}
// Etc.

If this were normal CSS, we’d be stuck writing the above. Since we’re using Sass, there’s a much better approach using the ampersand. The ampersand is amazing because it contains a reference to the current selector along with any parents.

.A {
  // & = '.A'
  .B {
    // & = '.A .B'
    .C {
      // & = '.A .B .C'
    }
  }
}

You can see in the above example how the ampersand references each selector in the chain as it goes deeper into the nested code. By utilizing this feature, we can create new selectors without having to rewrite the name of the component each and every time.

.MyComponent {
  &-title {}
  
  &-content {}
}

// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent-content {}

This is great because we can take advantage of the ampersand to write the name of the component one time and simply reference the component name throughout. This decreases the chance that the component name is mistyped. Plus, the document as a whole becomes easier to read without .MyComponent repeated all over the code.


There are times when the component needs a variant or modifier, as they’re called in SUIT and BEM. Using the ampersand pattern makes it easier to create modifiers.

<div class="MyComponent MyComponent--xmasTheme"></div>
.MyComponent {
  &--xmasTheme {}
}

// Compiles to
.MyComponent {}
.MyComponent--xmasTheme {}

“But, what about modifying the child elements?” you might ask. “How are those selectors created? The modifier isn’t needed on every element, right?”

This is where variables can help!

Variables and scoping

In the past, I’ve created modifiers a few different ways. Most of the time, I’d rewrite the special theme name I want to apply when modifying the element.

.MyComponent {
  &-title {
    .MyComponent--xmasTheme & {
    }
  }
  
  &-content {
    .MyComponent--xmasTheme & {
    }
  }
}

// Compiles to
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
.MyComponent-content {}
.MyComponent--xmasTheme .MyComponent-content {}

This gets the job done, but I’m back to rewriting the component name in multiple places, not to mention the modifier name. There’s definitely a better way to do this. Enter Sass variables.

Before we explore Sass variables with selectors, we need to understand how they’re scoped. Sass variables have scope, just like they would in JavaScript, Ruby, or any other programming language. If declared outside of a selector, the variable is available to every selector in the document after its declaration.

$fontSize: 1.4rem;

.a { font-size: $fontSize; }
.b { font-size: $fontSize; }

Variables declared inside a selector are scoped only to that selector and its children.

$fontSize: 1.4rem;

.MyComponent { 
  $fontWeight: 600;
  font-size: $fontSize; 
  
  &-title {
    font-weight: $fontWeight; // Works!
  }
}

.MyComponent2 { 
  font-size: $fontSize; 
  
  &-title {
    font-weight: $fontWeight; // produces an "undefined variable" error
  }
}

We know variables can store font names, integers, colors, etc. Did you know it can also store selectors? Using string interpolation, we can create new selectors with the variable.

// Sass string interpolation syntax is #{VARIABLE} 
$block: ".MyComponent";

#{$block} {
  &-title {
    #{$block}--xmasTheme & {
    }
  }
}

// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}

That’s cool, but the variable is globally scoped. We can fix that by creating the $block variable inside the component declaration, which would scope it to that component. Then we can re-use the $block variable in other components. This helps DRY up the theme modifier.

.MyComponent {
  $block: '.MyComponent';
  
  &-title {
    #{$block}--xmasTheme & {
    }
  }
  
  &-content {
    #{$block}--xmasTheme & {
    }
  }
}

// Compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}
.MyComponent-content {}
.MyComponent--xmasTheme .MyComponent-content {}

This is closer, but again, we have to write the theme name over and over. Let’s store that in a variable too!

.MyComponent {
  $block: '.MyComponent';
  $xmasTheme: '.MyComponent--xmasTheme';
  
  &-title {
    #{$xmasTheme} & {
    }
  }
}

This is much better! However, we can improve this even further. Variables can also store the value of the ampersand!

.MyComponent {
  $block: &;
  $xmasTheme: #{&}--xmasTheme;
  
  &-title {
    #{$xmasTheme} & {
    }
  }
}

// Still compiles to
.MyComponent {}
.MyComponent-title {}
.MyComponent--xmasTheme .MyComponent-title {}

Now that’s what I’m talking about! “Caching” the selector with ampersand allows us to create our modifiers at the top and keep the theme modifications with the element it’s modifying.

“Sure, that works at the top level,” you say. “But what if you are nested really deep, like eight levels in?” You ask great questions.

No matter how deep the nest, this pattern always works because the main component name is never attached to any of the children, thanks to the SUIT naming convention and ampersand combo.

.MyComponent { 
  $block: &;
  $xmasTheme: #{&}--xmasTheme;
  
  &-content {
    font-size: 1.5rem;
    color: blue;
    
    ul {
      li {
        strong {
          span {
            &::before {
              background-color: blue;
              
              #{$xmasTheme} & {
                background-color: red;
              }
            }
          }
        }
      }
    }
  }
}

// Compiles to 
.MyComponent-content {
  font-size: 1.5rem;
  color: blue;
}

.MyComponent-content ul li strong span::before {
  background-color: blue;
}

/*
* The theme is still appended to the beginning of the selector!
* Now, we never need to write deeply nested Sass that's hard to maintain and 
* extremely brittle: https://css-tricks.com/sass-selector-combining/
*/
.MyComponent--xmasTheme .MyComponent-content ul li strong span::before {
  background-color: red;
}

Code organization is the main reason I like to use this pattern.

  • It’s relatively DRY
  • It supports the “opt-in” approach, which keeps modifiers with the elements they modify
  • Naming stuff is hard but this enables us to reuse common element names like “title” and “content”
  • It’s low-lift to add a modifier to a component by placing the modifier class on the parent component

“Hhhmmmmm… doesn’t that get hard to read though after you create a bunch of different components? How do you know where you’re at when everything is named &-title and &-content?”

You continue to ask great questions. Who said the source Sass had to be in one file? We can import those components, so let’s turn to that topic!

The importance of imports

Credit: @Julien_He

One of Sass’ best features is @import. We can create separate Sass files (partials) and import them into other Sass files that compile together with the imported file located at the spot it’s imported. This makes it easy to package up related styles for components, utilities, etc. and pull them into a single file. Without @import, we’d need to link to separate CSS files (creating numerous network requests, which is badong) or write everything in a single stylesheet (which is tough to navigate and maintain).

.Component1 {
  &-title {}
  &-content {}
  &-author {}
}

.Component2 {
  &-title {}
  &-content {}
  &-author {}
}

.Component3 {
  &-title {}
  &-content {}
  &-author {}
}

.Component4 {
  &-title {}
  &-content {}
  &-author {}
}

.Component5 {
  &-title {}
  &-content {}
  &-author {}
}

// A couple of hundred lines later...

.Component7384 {
  &-title {}
  &-content {}
  &-author {}
}

// WHERE AM I?

One of the more popular methodologies for organizing Sass files is the 7-1 Pattern. That’s seven distinct folders containing Sass files that are imported into a single Sass file.

Those folders are:

  • abstracts
  • base
  • components
  • layout
  • pages
  • themes
  • vendor

Use @import to pull each Sass file in those folder into a main Sass file. We want to import them in the following order to maintain good scope and avoid conflicts during compilation:

  1. abstracts
  2. vendor
  3. base
  4. layout
  5. components
  6. pages
  7. themes
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';

@import 'vendors/some-third-party-component';

@import 'base/normalize';

@import 'layout/navigation';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';

@import 'components/buttons';
@import 'components/hero';
@import 'components/pull-quote';

@import 'pages/home';
@import 'pages/contact';

@import 'themes/default';
@import 'themes/admin';

You may or may not want to use all of these folders (I personally don’t use the theme folder since I keep themes with their components), but the idea of separating all of styles into distinct files makes it easier to maintain and find code.

More of the benefits of using this approach:

  • Small components are easier to read and understand
  • Debugging becomes simpler
  • It’s clearer to determine when a new component should be created — like when a single component file gets to be too long, or the selector chain is too complex
  • This emphasizes re-usage — for example, it might make sense to generalize three component files that essentially do the same thing into one component

Speaking of re-usage, there are eventually patterns that get used often. That’s when we can reach for mixins.

Mixin’ it up

Mixins are a great way to reuse styles throughout a project. Let’s walk through creating a simple mixin and then give it a little bit of intelligence.

The designer I work with on a regular basis always sets font-size, font-weight, and line-height to specific values. I found myself typing all three out every time I needed to adjust the fonts for a component or element, so I created a mixin to quickly set those values. It’s like a little function I can use to define those properties without having to write them in full.

@mixin text($size, $lineHeight, $weight) {
  font-size: $size;
  line-height: $lineHeight;
  font-weight: $weight;
}

At this point, the mixin is pretty simple—it resembles something like a function in JavaScript. There’s the name of the mixin (text) and it takes in three arguments. Each argument is tied to a CSS property. When the mixin is called, Sass will copy the properties and the pass in the argument values.

.MyComponent {
  @include text(18px, 27px, 500);
}

// Compiles to
.MyComponent {
  font-size: 18px;
  line-height: 27px;
  font-weight: 500;
}

While it’s a good demonstration, this particular mixin is a little limited. It assumes we always want to use the font-size, line-height, and font-weight properties when it’s called. So let’s use Sass’ if statement to help control the output.

@mixin text($size, $lineHeight, $weight) {
  // If the $size argument is not empty, then output the argument
  @if $size != null {
    font-size: $size;
  }
  
  // If the $lineHeight argument is not empty, then output the argument
  @if $lineHeight != null {
    line-height: $lineHeight;
  }
  
  // If the $weight argument is not empty, then output the argument
  @if $weight != null {
    font-weight: $weight;
  }
}

.MyComponent {
  @include text(12px, null, 300);
}

// Compiles to
.MyComponent {
  font-size: 12px;
  font-weight: 300;
}

That’s better, but not quite there. If I try to use the mixin without using null as a parameter on the values I don’t want to use or provide, Sass will generate an error:

.MyComponent {
  @include text(12px, null); // left off $weight
}

// Compiles to an error:
// "Mixin text is missing argument $weight."

To get around this, we can add default values to the parameters, allowing us to leave them off the function call. All optional parameters have to be declared after any required parameters.

// We define `null` as the default value for each argument
@mixin text($size: null, $lineHeight: null, $weight: null) {
  @if $size != null {
    font-size: $size;
  }
  
  @if $lineHeight != null {
    line-height: $lineHeight;
  }
  
  @if $weight != null {
    font-weight: $weight;
  }
}

.MyComponent {
  &-title {
    @include text(16px, 19px, 600);
  }
  
  &-author {
    @include text($weight: 800, $size: 12px);
  }
}

// Compiles to
.MyComponent-title {
  font-size: 16px;
  line-height: 19px;
  font-weight: 600;
}

.MyComponent-author {
  font-size: 12px;
  font-weight: 800;
}

Not only do default argument values make the mixin easier to use, but we also gain the ability to name parameters and give them values that may be commonly used. On Line 21 above, the mixin is being called with the arguments out of order, but since the values are being called out as well, the mixin knows how to apply them.


There’s a particular mixin that I use on a daily basis: min-width. I prefer to create all my sites mobile first, or basically with the smallest viewport in mind. As the viewport grows wider, I define breakpoints to adjust the layout and the code for it. This is where I reach for the min-width mixin.

// Let's name this "min-width" and take a single argument we can
// use to define the viewport width in a media query.
@mixin min-width($threshold) {
  // We're calling another function (scut-rem) to convert pixels to rem units.
  // We'll cover that in the next section.
  @media screen and (min-width: scut-rem($threshold)) {
    @content;
  }
}

.MyComponent {
  display: block;
  
  // Call the min-width mixin and pass 768 as the argument.
  // min-width passes 768 and scut-rem converts the unit.
  @include min-width(768) {
    display: flex;
  }
}

// Compiles to 
.MyComponent {
  display: block;
}

@media screen and (min-width: 48rem) {
  .MyComponent {
    display: flex;
  }
}

There are a couple of new ideas here. The mixin has a nested function called @content. So, in the .MyComponent class, we’re no longer calling the mixin alone, but also a block of code that gets output inside the media query that’s generated. The resulting code will compile where @content is called. This allows the mixin to take care of the @media declaration and still accept custom code for that particular breakpoint.

I also am including the mixin within the .MyComponent declaration. Some people advocate keeping all responsive calls in a separate stylesheet to reduce the amount of times @media is written out in a stylesheet. Personally, I prefer to keep all variations and changes that a component can go through with that component’s declaration. It tends to make it easier to keep track of what’s going on and help debug the component if something doesn’t go right, rather than sifting through multiple files.

Did you notice the scut-rem function in there? That is a Sass function taken from a Sass library called Scut, created by David The Clark. Let’s take a look at how that works.

Getting functional

A function differs from a mixin in that mixins are meant to output common groups of properties, while a function modifies properties based on arguments that return a new result. In this case, scut-rem takes a pixel value and converts it to a rem value. This allows us to think in pixels, while working with rem units behind the scenes to avoid all that math.

I’ve simplified scut-rem in this example because it has a few extra features that utilize loops and lists, which are out of the scope of what we’re covering here. Let’s look at the function in its entirety, then break it down step-by-step.

// Simplified from the original source
$scut-rem-base: 16 !default;

@function scut-strip-unit ($num) {
  @return $num / ($num * 0 + 1);
}

@function scut-rem ($pixels) {
  @return scut-strip-unit($pixels) / $scut-rem-base * 1rem;
}

.MyComponent {
  font-size: scut-rem(18px);  
}

// Compiles to
.MyComponent {
  font-size: 1.125rem;
}

The first thing to note is the declaration on Line 2. It’s using !default when declaring a variable, which tells Sass to set the value to 16 unless this variable is already defined. So if a variable is declared earlier in the stylesheet with a different value, it won’t be overridden here.

$fontSize: 16px;
$fontSize: 12px !default;

.MyComponent {
  font-size: $fontSize;
}

// Compiles to
.MyComponent {
  font-size: 16px;
}

The next piece of the puzzle is scut-strip-unit. This function takes a px, rem, percent or other suffixed value and removes the unit label. Calling scut-strip-unit(12px) returns 12 instead of 12px. How does that work? In Sass, a unit divided by another unit of the same type will strip the unit and return the digit.

12px / 1px = 12

Now that we know that, let’s look at the scut-strip-unit function again.

@function scut-strip-unit ($num) {
  @return $num / ($num * 0 + 1);
}

The function takes in a unit and divides it by 1 of the same unit. So if we pass in 12px, the function would look like: @return 12px / (12px * 0 + 1). Following the order of operations, Sass evaluates what’s in the parentheses first. Sass smartly ignores the px label, evaluates the expression, and tacks px back on once it’s done: 12 * 0 + 1 = 1px. The equation is now 12px / 1px which we know returns 12.

Why is this important to scut-rem? Looks look at it again.

$scut-rem-base: 16 !default;

@function scut-rem ($pixels) {
  @return scut-strip-unit($pixels) / $scut-rem-base * 1rem;
}

.MyComponent {
  font-size: scut-rem(18px);  
}

On Line 4, the scut-strip-unit function removes px from the argument and returns 18. The base variable is equal to 16 which turns the equation into: 18 / 16 * 1rem. Remember, Sass ignores any unit until the end of the equation, so 18 / 16 = 1.125. That result multiplied by 1rem gives us 1.125rem. Since Scut strips the unit off of the argument, we can call scut-rem with unit-less values, like scut-rem(18).

I don’t write that many functions because I try to keep the stuff I create as simple as possible. Being able to do some complex conversions using something like scut-rem is helpful though.

The selector order that placeholders mess up

End up where I think it did, that CSS?

Be careful what is extended

I tried writing out some examples to demonstrate why using @extend can be problematic, but I have used them so little that I can’t create any decent examples. When I first learned Sass, I was surrounded by teammates who’ve already gone through the trials and tribulations. My friend Jon Bebee wrote an extremely excellent article on how @extend can get you into trouble. It’s a quick read and worth the time, so I’ll wait.

About those placeholders…

Jon proposes using placeholders as a solution to the problem he outlines: Placeholders don’t output any code until they’re used with @extend.

// % denotes an extended block
%item {
  display: block;
  width: 50%;
  margin: 0 auto;
}

.MyComponent {
  @extend %item;
  color: blue;
}

// Compiles to
.MyComponent {
  display: block;
  width: 50%;
  margin: 0 auto;
}

.MyComponent {
  color: blue;
}

OK, wait. So it output .MyComponent twice? Why didn’t it simply combine the selectors?

These are the questions I had when I first started using placeholders (and then subsequently stopped). The clue is the name itself. Placeholders simply hold a reference to the place in the stylesheet they were declared. While a mixin copies the properties to the location it is used, placeholders copy the selector to the place where the placeholder was defined. As a result, it copies the .MyComponent selector and places it where %item is declared. Consider the following example:

%flexy {
  display: flex;
}

.A {
  color: blue;
}

.B {
  @extend %flexy;
  color: green;
}

.C {
  @extend %flexy;
  color: red;
}

// Compiles to
.B, .C {
  display: flex;
}

.A {
  color: blue;
}

.B {
  color: green;
}

.C {
  color: red;
}

Even though B and C are declared further down in the stylesheet, the placeholder places the extended properties tall the way up to where it was originally declared. That’s not a big deal in this example because it’s really close to the source where it’s used. However, if we’re adhering to something like the 7-1 Pattern we covered earlier, then placeholders would be defined in a partial in the abstracts folder, which is one of the first imported files. That puts a lot of style between where the extend is intended and where it’s actually used. That can be hard to maintain as well as hard to debug.

Sass Guidelines (of course) does a great job covering placeholders and extend and I would recommend reading it. It not only explains the extend feature, but at the end, advocates against using it:

Opinions seem to be extremely divided regarding the benefits and problems from @extend to the point where many developers including myself have been advocating against it, […]


There are many other features of Sass I didn’t cover here, like loops and lists, but I’ve honestly haven’t relied on those features as much as the ones we did cover in this article. Take a look through the Sass documentation, if for nothing else, to see what things do. You may not find a use for everything right away, but a situation may come up and having that knowledge in your back pocket is priceless.

Let me know if I missed something or got something wrong! I’m always open to new ideas and would love to discuss it with you!

Further Reading