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:
- The power of the ampersand
- Variables and scoping
- The importance of imports
- Mixin’ it up
- Getting functional
- The selector order that placeholders mess up
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

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:
- abstracts
- vendor
- base
- layout
- components
- pages
- 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

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
- Sass Style Guide – Thoughts and recommendations for keeping Sass clean and maintainable.
- What a CSS Code Review Might Look Like – Predominantly looks at CSS, but includes tips to code review Sass files.
- Defining and Dealing with Technical Debt – We talked a lot about DRY methods and efficiency in this article. This post talks about the various consequences of verbose or inefficient code.
- Loops in CSS Preprocessors – We didn’t cover loops here, but Miriam Suzanne goes in depth in this article.
- Sass Snippets – A collection of handy Sass mixins and other Sass patterns.
These posts are always helpful to re-learn some forgotten basics and remind ourselves of best practices. There’s so much to learn that you can lose sight of the simple things. Thanks for the recap, I am definitely going to revisit my ampersand usage and variable scoping.
Thanks! It’s always helpful to re-review the basics. The ampersand is one of my favorite tools.
Here’s my favorite SASS trick:
This allows you to import the same file multiple times, but only get one copy of the styles in the final build; a la webpack. This has the advantages that:
– You obviously get a smaller CSS file size, but you also get improved performance of your styles (no redundant rules) and much more readable Inspector content.
– You can freely @import the file everywhere it’s depended upon by any other styles, which is hugely beneficial for self-documentation and also for stability.
This is pretty cool! Do you use the 7-1 pattern? What does your main stylesheet file look like?
We don’t; our SCSS architecture parallels our React component structure – one SCSS file per one .react.js file. We do have a file with a handful of global styles at the top, but almost everything lives in those components. In addition to making it easy to find the styles relevant to the markup you’re looking at, this makes imports straightforward. If your JS file imports another component, the corresponding SCSS file needs to import that component’s SCSS file as well. The entry point just imports the “app” level component and everything else takes care of it’s own dependencies.
nice! React is something I’ve been actively learning through Wes Bos’ course.
LOVE IT! Terrific approach and execution of some great Sass techniques.
I have to respectfully completely disagree with part of the suggested use of ampersands in SCSS. I personally believe that using the ampersand in partial class names makes the code impossible to maintain – unless you’re the person who wrote it – and recently.
Using an example snippet from your post (because it shows using ampersands in 2 different ways)
If I see the class
.MyComponent--title
applied to an element in the DOM, I would expect that I can copy that, and then grep the code base to find the exact definition/s for that class. Using the ampersand breaks that ability, meaning developers have to already be familiar with the codebase in order to maintain/extend it (sourcemaps definitely could help, but lets assume we don’t have them). When I see an ampersand used this way in a CR, I always ask the developer to change it. A few extra characters typed now will save them, or more importantly a future dev, a lot of time when trying to find the source to edit. Find and replace exists in every IDE/text editor, so it’s not that much harder to refactor in the future, shouldMyComponent
becomeMyNewComponent
The second use of the ampersand in that snippet though,
.MyComponent--xmasTheme &
, is perfectly acceptable – although not something I personally want to be used all the time. The reason I’m okay with this is that it’s highly unlikely you’re ever going to do a search in the code base for the resulting compiled CSS,.MyComponent .MyComponent--xmasTheme
. I would hope that most people will just grab one class name and grep the codebase for that instead.Would love to hear your thoughts..
There’s definitely strong thoughts on both sides, particularly on the maintainability front: https://css-tricks.com/sass-selector-combining/
There are definitely considerations to take on both sides. For my team and I, we’ve never had any issue with it. If we go with an architecture like this one, we make sure that any component that is created has it’s own file. Instead of grepping for the class name, we just pull up the file for that particular component. We require the team to be diligent with the creation of 1 component -> 1 file and really haven’t had any issues.
Since we also strive to keep our files simpler and contain less code overall, we are able to open a file and quickly scan to find what we are looking for.
Again, there’s benefits and drawbacks to both approaches and you just need to pick what works best for you and your team.
This isn’t a problem if you use the ITCSS* way of organizing project files and following css namespacing conventions set out by harry roberts (CSS wizardry) plus his suggestions on BEM. If you see see “c-MyComponent–title” in the DOM you go to your CSS folders components folder i.e “sass>components/_MyComponent.scss” its easy. You should never need to search the project for this text as any reference to it should only ever be in that one file.
So here the “c-” denotes a “component”, ITCSS has a component layer which corresponds to a “component” folder (the original ITCSS recommendation is to use prepend filenames with the layer such as component-myComponent.scss and have all partials in one file but I in my company told them to stick the ITCSS layers into folders).
Our convention is to name the file the same as the “block” class in BEM. As I said it’s so easy to find what you want you don’t even need a source map to find your’re stuff is. The combined principles of ITCSS, BEM and namespacing largely remove the issues I think you’re talking about and cascade issues. you’ll know exactly where your css is held just by reading the DOM. Go check it out, it’ll change your life!
*Only works on a monolith, falls flat in a distributed component based environment where styles could being getting pulled in from all over. This is where CSS-in-JS is good.
A warning about writing things like “&-title”: it makes the code so difficult to maintain, because you can’t just search for things like “component1-title”.
If you use a source map, the problem doesn’t exist anymore, does it ?
SASS is realy great. But what I hate about the whole precompiler world is the build tools. Webpack, Gulp, blabla are so damn complicated to setup. It’s gotten so hard.
As a Designer I whish there would be some gui to setup all thes build tools. And every developer is like “What that’s easy, just read the doc”. But for this I’d have to do a study in egyptology first to understand all this stuff. And forget about debugging. Where did it fail? In webpack? In Gulp? in NPM? Nobody knows. And all I wanted to do was write Sass and minify it.
If Id wish for something on this site it would be a “Build Tools guide for idiots”.
One great thing about SASS is that you can do the whole thing – imports, transpilation, even minification – with just the standard command line tool and without a config file. No gulp, webpack, or anything else required. You’re definitely right, though, that the web build-tool space is too complex and fragile for its own good.
But there is such a tool :) … check out CodeKit!
I’ve used ampersands to nest BAMS style selectors in the past, but I’ve moved away from it lately. I’ve found it’s difficult for other developers to find selectors they search for when they find them in the DOM. For example, they search for
.Component1-title
, but can’t find it because it’s broken up in the SCSS file.Maybe this is only a problem for larger teams all working on the same framework? I’m curious if anyone else has run into this?
In my dev environment, there’s about 160 developers, ~40 of which are front-end devs if I remember correctly. We’re all broken up into teams of 2 – 5 covering various business units. Most of our front-end developers code this way and we haven’t had any issue on-boarding new devs into the process. One of the key factors is picking a style and sticking to it within the particular project. If you’re going to do ampersand nesting like I describe above, it should be done across the whole project. If you are going to write out the component names, then they should always be written out.
Great Article. Thanks :)
Great post. I use the
&__
and&--
tricks a lot for BEM and I like how organised things look.I have found a genuinely good use for placeholder and extends. I’ve recently moved from software development to agency web design/dev and here we do a lot of integration with third party systems (ticketing) where you don’t have control over the html but you still need to reskin to match the main design. And you might have two or three different 3PIs to do on the same project and a really fast turnaround. So I write the key branded elements as models during the main development and then @extend them into the 3PI css sheet/s like this:
etc. Then in our main it’s just
It means I make sure to write those styles independent of our specific html and I can drop that onto any element I need to dead easily and I don’t need to do loads of translations to whatever crazy html I am dealing with. And when stuff changes on main, which gets the bulk of design time, it just updates all those 3PI sheets automatically so we don’t get drift. So for example if it’s some version of Bootstrap, which it often is. It’s just:
(
#{$s}
is my specificity bump that I set in my themer because you can’t always control where your css is gonna show up. or what it’s fighting with.)And if you don’t have a framework or component system or anything even, and it’s just someone else’s undocumented front end full of the random accumulations of decades that they can’t update (because of all the integrations!), you can bring some order to it by defining models, like this is a call to action, this is secondary or page navigation, etc etc. Then you can go through page by page and roughly sort that random html into those groups with extends and quickly bring (some kind of) consistent look to bear without mega bloat (if no access to gzip for example).
So I think there really are good uses for extends out here in the trenches, though I never ever used them in the world of software and I agree when you’re building the whole thing there are just better ways to go.
“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.”
But searching in IDE is just destroyed. How can I search “.MyComponent-title” in whole project with code like below?
.MyComponent {
&-title {}
}
Please people, don’t do this in SCSS. I’m often working on other people’s codebases where this technique is applied, and I’m completely lost most of the time. Autocomplete and jump-to-source in my IDE is broken.
Just because you can do clever things in Sass doesn’t mean you should. Stay close to Css – what the browser (and people) understand. See and learn the cascade. Use Sass-variables for consistency. Shorter source code is hardly an argument, and this also doesn’t save you any time – it costs the next developer more time.
Great arguments from Harry Roberts here: https://csswizardry.com/2017/02/code-smells-in-css-revisited/: “I am far more likely to look for a class than I am to rename it”.
@Joris um… The article you linked advocates the use of BEM like syntax throughout. Which is what I’m advocating for in this article.
EDIT
Or are you advocating against the ampersand selector?
or are you advocating against the ampersand selector?
Yes, the ampersand selector.
didn’t know that it has so much use, I generally use it only for :hover and event related stuff to keep my code clean. But your article is an eye opener so much possibilities. Thanks for sharing