6 tips to avoid headaches in SCSS

1. Never style elements

This is probably the biggest cause of headaches, at least in my experience. It seems innocent enough to style a button or a heading – they’re buttons and headings, of course they should look like buttons and headings.

// don't do this!
button {
  background-color: $color-btn;
  border-radius: 4px;
}

// nooooo!
h2 {
  font-size: 40px;
  font-weight: bold;
}

But!

What if you have an element that should look like a link, but is really a button? Or if you have a button which needs to look different from all the others, such as with hamburger/pancake menus (there’s a lot of debate on exactly which food item these look like, I think it’s a pancake).

What if your headings need a different size or style in a particular context?

In all these cases, you would have to manually un-style these elements in their own class. And because element styling wins specificity wars over classes, you would often need to use the !important rule – a huge no-no.

Instead, assign classes to your elements. This is much more expressive than styling raw elements as you can state the intention of the element through it’s class. You can also reuse the class on different elements, such as with buttons and links.

// better
.button {}
.heading-2 {}
// now you can reuse the classes!
<button class="button">I am a button</>
<a class="button">I am link, but I look like a button</>

If you need to style elements for a Rich Text Editor or WYSIWYG section, or anywhere else you might not be able to add classes directly, you can do so by nesting. By putting a .rte class on the container for these sections, you can scope your element styles to only target what you need to.

// _rte.scss

.rte {
  & h2 {
    font-size: 28px;
    font-family: $font-headings;
    font-weight: bold;
    line-height: 1.3;
  }

  & p {
    font-size: 16px;
    margin-bottom: 16px;
  }

  & ul {
    // etc...
  }
}
// article template

<article class="rte">
  <!-- content added by cms -->
</article>

2. Strive to never use !important

Your CSS should follow rules, and your classes shouldn’t overstep each other. The more tangled up in the web of specificity and nested selectors your code becomes, the harder your codebase will be to work with. You’ll end up adding !important to properties that really have no need to be, and the more of these rules you have in the first place the more you’ll have to use it again to override these rules. It’s a downward spiral of manually forcing specificity, and it has rippling consequences for your CSS.

There are a few ways to avoid using !important[]: For one, keeping your CSS structure as flat as possible can avoid any specificity wars that could crop up. Nesting CSS, and using complex selectors to target classes in specific situations, are things many developers do without realising they are lining up headaches and delays for themselves in future.

3. Use reset.css

Cross-browser consistency is one of the major problems surrounding not only CSS, but also JS and even accessibility with ARIA. In CSS, we can lay out a level playing field using a reset.css file. This will remove any browser-specified styles, such as margins, font sizes, button styles etc. so every browser will show the same thing.

reset.css is also great because we don’t need to waste time overwriting default styles: If your <button /> needs to have a transparent background and no borders, we would normally need to specify these in our own CSS. This way, we know any CSS we are writing is additive instead of being subtractive.

.button {
  // these properties wouldn't be necessary with reset.css
  background-color: transparent;
  border: none;
}

Subtractive CSS, where we write styles to overwrite other styles, is generally a big no-no as it adds a level of confusion to what a class is doing. CSS classes, as with anything in programming, should be concise and to the point. If you find yourself writing subtractive or overwriting CSS, it may be worth tracing back to find what it’s overwriting, and breaking down the original styles further using placeholders[link] or mixins[link].

You could also use normalize.css. This will maintain a set of default page margins and other element-level styles across browsers. Personally, I prefer reset.css due to the fact that I want to control everything on a page without being interrupted by default styles – I don’t want any page to have a margin on the body, for example, and writing code to undo these margins makes my code confusing. However, this might work better for you if you want some defaults.

4. Use variables to stay DRY

DRY, meaning Don’t Repeat Yourself, is a key concept to understand for any kind of programming. In SCSS, the best place to start with this is in using variables, placeholders and mixins. The most common of these you’ll be using is variables.

Let’s say you’re looking at a design you need to create, and it has many colours across it. You can make your life much easier by assigning these colours to variables, perhaps with a kind of hierarchy which relates to the brand you’re working with. There are lots of different ways to name variables, as outlined in this CSS Tricks article.

Another great use of SCSS variables is naming the top level of components. Here, I use $module to store the name of the component and am then free to reuse it in other places, without needing to worry if I want to change the name of that component for some reason.

// we can change $module at any time in one place
$module: 'nav';

.#{$module} {
  &__dropdown-link {
    position: relative;
  }

  &__dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    display: hidden;
    
    .#{$module}__dropdown-link:hover & {
      display: block;
    }
  }
// above css applied to html, for reference

<li class="nav__dropdown-link>
  <a>Link title</a>
  <ul class="nav__dropdown">
    ...
  </ul>
</li>

5. Use placeholders

Placeholders are a great way to save time and file size with SCSS. They are essentially groups of properties which you can apply in a similar way to variables, and further build on top of them.

I’ve used placeholders on my own site to help generate a set of paragraph styles using the Golden Ratio Typography Calculator (check it out!) which downsize appropriately at different resolutions:

// these styles...

%t-1\/2 {
  font-size: 14px;
  line-height: 1.786;
}

%t-1 {
  font-size: 18px;
  line-height: 1.722;
}

%t-2 {
  font-size: 23px;
  line-height: 1.652;
}
// ...can be easily reused for different breakpoints
.t {
  &-1 {
    @extend %t-1\/2;
    @screen lg { // @screen is a tailwind directive - these are just desktop-first breakpoints!
      @extend %t-1;
    }
  }
  &-2 {
    @extend %t-1;
    @screen lg {
      @extend %t-2;
    }
  }
}

As well as needing to write less code, we are also building a sensible design system with our CSS to ensure consistency between elements and enforce a single source of truth.

6. Don’t set a max-width on <main>

More often than not, any content on a webpage has a certain max-width which it cannot exceed. This helps with readability and keeps the page content flush all the way down.

If you’re a beginner, it may be instinctive to set this max-width on an element that’s quite high up in the hierarchy – the <main> element for instance, or even the <body>. On a page that has the same background color all the way down, this might be perfectly fine. Most webpages, however, are broken up by different sections that have a background color spanning the entire width of the page.

The issue you will run into by setting a max-width on the <main>, is that this background color will be limited by the max-width. Beyond this max-width, the background color will be white (or whatever color the <body> is set to).

// index.html

<main>
  <section class="section section--bg-primary">
    <div class="section__inner">
      <h2>Hello world!</h2>
      <p class="paragraph">
        Lorem ipsum dolor sit amet...
      </p>
    </div>
  </section>
  <section class="section section--bg-secondary">
    <div class="section__inner">
      <h2>Hello again world!</h2>
      <p class="paragraph">
        Lorem ipsum dolor sit amet...
      </p>
    </div>
  </section>
</main>
// layout/_section.scss

.section {
  // use the parent element to add padding
  padding: 48px 16px;

  &--bg-primary {
    background-color: $color-primary;
    color: white;
  }
  
  &--bg-secondary {
    background-color: $color-secondary;
    color: black;
  }

  &__inner {
    // set the max-width on the inner element
    max-width: $max-w-page;
    margin: 0 auto;
  }
}