Introducing new features that touch the root of your development stack, such as the addition of themes, can introduce some unique challenges. This article aims to provide some tips to to make your life easier and shorten development time.

For existing projects this can be a guide towards refactoring part of the front-end stack.

Hierarchy

A web page is rendered in a template, which consists of multiple components (such as cards or a header). Each of these components contain reusable elements (such as buttons). All of these use basic shared utilities (icons, fonts, images etc.).

If we analyse these layers we find out that there are a lot of elements that are being reused, or at least, we could possibly reuse them somewhere in the future. Failing to keep this requirement in mind would generate a lot of what, in the world of software development are called DRY-issues . DRY stands for: Do not Repeat Yourself. We can avoid DRY-issues by working on our SCSS/SASS structure.

For example:

  • Page: Page-specific styling: home.scss
  • Component: A card containing a bit of text and a button: card.scss
  • Element: The button rendered within the card: button.scss
  • Shared utilities: things that we use everywhere: variables.scss and mixins.scss

Shared utilities

These can (re)used everywhere, Variables or Mixins for instance.

/generic/variables.scss

$header-height: 3rem;
$site-height: 100vh;
$sidebar-width: 20rem;
$navigation-width: 16rem;

$shadow-base: ("x": 0rem, "y": 0.4rem, "blur": 0.8rem,"spread": 0.4rem);
$theme-shadow-color: rgba(0,0,0, 0.2);
$theme-shadows: ("close": 0.5 ,"medium": 1, "far": 1.5);

$theme-colors: (
  "background": #ffffff,
  "primary": #865DF6,
  "secondary": #D490DA, 
);

/generic/mixins.scss

@mixin shadow($size) {
  $scaleFactor: map-get($theme-shadows, $size);
  $x: map-get($shadow-base, "x") * $scaleFactor;
  $y: map-get($shadow-base, "y") * $scaleFactor;
  $blur: map-get($shadow-base, "blur") * $scaleFactor;
  $spread: map-get($shadow-base, "spread") * $scaleFactor;
  
  box-shadow: $x $y $blur $spread $theme-shadow-color;
}

@function theme($key){
    @return map-get($theme-colors, $key);
}

Sharing the utilities

It would make sense to import these everywhere, create a /generic/ directory and then create an index.scss containing imports of all your atoms.

generic/index.scss

@import './variables';
@import './animations';
@import './mixins';
@import './themes/light' // Theme specific variables (colors) and icon imports
@import './themes/dark' // Theme specific variables (colors) and icon imports
@import './container'

Elements

Used by components that need them. They are only included when needed. A Button is a good example:

elements/button.scss

.button {
  transition: 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 2rem;
  text-decoration: none;
  padding: 0.4rem 0.8rem;
  text-align: center;
  font-weight: bold;
  color: #ffffff;
  min-width: 4rem;
  text-transform: uppercase;
  
  &:hover {
    transform: scale(0.99);
    transform: translatey(0.1rem);
  }
  
  &.secondary {
    background: theme('primary');
    &:hover {
      background: theme('secondary');
    }
  }
  
  &.secondary {
    background: theme('secondary');
    &:hover {
      background: theme('primary');
    }
  }
}

Components

Components are one factor larger than elements, they are modules that can be individually placed and repeated across different pages. A card containing bite-size information is a good example.

As you can see, the card component makes use of the button element, both make use of the shared utilities defined earlier in the article.

components/card.scss

.card {
    box-sizing: border-box;
    font-size: 16px;
    width: 10vw;
    padding: 1rem;
    background: theme('background');
    transition: 0.2s ease;
    @include shadow('far');
    
    &:hover {
      @include shadow('close');
    }
    
    p{
      padding-left:0.2rem;
    }
    
    h2:first-of-type {
      color: theme('primary');
      margin: 0;
    }
  }

Pages

A page usually contains a container of some sort and usually consists of one or more components.

pages/home.scss

@import './generic/index.scss'
@import './elements/button.scss'
@import './components/card.scss'

views/home/index.html

<div class='container light'>
   <div class='card'>
     <h2>Hallo</h2>
     <p>Hoi</p>
     <div class='button-box'>
       <a href='#' class='button secondary'>go</a>
     </div>
    </div>
</div>

Summary

Pages consist of multiple Components and Components can contain multiple elements. Both use shared utilities.

In this article we went through how you can separate parts of your templates into reusable parts. I hope you've been able to learn something new from this SCSS structure and in turn prevent a lot of headaches when your project matures.