If you spend the majority of your time writing a lot of front-end code, specifically CSS or Sass, you understand the importance of organization. While CSS and more specifically Sass, are powerful languages, they can easily become bloated, hard to maintain, and simply put, a hot mess.

But it doesn't have to be this way. With a little preplanning and attention to detail, building and maintaining a highly intuitive framework is simple.


Where do you begin?

Start small and build out. As a front-end developer or designer it is easy to get ahead of yourself. But every website or application starts with core style features. A few examples of typical core styles:

  • Basic structural layout
  • Core and secondary colors palettes
  • Typography (font families, font sizes, etc.)

Define these features before doing anything else. Answer questions such as, what are your typical font sizes? What are your core, secondary and neutral colors? What are the common characteristics of each page?

By defining these core styles at the start of your project you can use these as pillars to build the rest of your site's styles.


Keep your folders clean

Next, make sure to follow a pattern when creating and defining new styles. At this point in time, your project is small and finding styles that need an update is fairly simple. But what happens when your application begins to grow or a new team member starts to push code?

Here is a our typical directory structure at Made By Munsters:


Main.scss

This file sits just inside the root stylesheets directory. It is used to pull in all files and serves as an index of sorts. Not sure where a file resides, the main.scss will tell you.

The makeup of this file should look like this:

  • Vendor Files (Bourbon, Neat, any third party import)
  • Settings Files
  • Components Files
  • Base files
  • Module or view files
// Style Guide
// ========================================

// Imports
@import "bourbon";
@import "neat";

// Import All Settings Files
@import "settings/settings";

// Import All Component Styles
@import "components/components";

// Import All Base Styles
@import "base/base";

// Import All Module Styles
@import "module/module";

// Note: Each section has a parent file that
// inherits the name of its directory. These files
// import all of the children in the directory.

As you can see, this structure places the global or less specific styles at the top of the cascade and gets gradually more specific.


Settings Directory

The settings directory is the core of our structural system. It houses all of our variables, mixins, functions and our global reset.

Additionally, to keep this directory even more organized, we separate each group of styles (variables, mixins, functions, etc.) in subdirectories. This allows us to quickly search our styles without sifting through unrelated files.

Furthermore, this allows us to keep our naming convention simple. If a file resides in the variables directory and deals with typography we name it accordingly, _typography.scss. Then, in our mixins directory we can again use the name _typography.scss to define type or font related mixins.

This also allows us to map our files together. Not in a programmatic sense. But in an organization sense. If variables define styles and mixins build styles, then it makes sense that we reference our variables in our mixins, when applicable. This type of directory structure and naming convention makes that easy.

// Settings Directory
// ========================================

// Reset
@import "reset";

// Functions
@import "functions/misc";

// Variables
@import "variables/colors";
@import "variables/alert";
@import "variables/buttons";
@import "variables/links";
@import "variables/tables";
@import "variables/typography";
@import "variables/spacers-resets";
@import "variables/status-bars";

// Mixins
@import "mixins/layout";
@import "mixins/misc";
@import "mixins/typography";


Components

Components are singular objects that serve as Lego pieces for your websites or applications. We place all of our components in one directory. Then, we create a corresponding variables file to help define this feature.

Let's look at an alert component as an example.

First we create a variables file called _alert.scss and place that in the settings/variables directory. It might look like this:

// Alert Variables
// ========================================

$alerts: (
  success   $green-base  $green-dark,
  danger    $yellow-base $yellow-dark,
  warning   $red-base    $red-dark,
  default   $grey-base   $grey-dark
);

As you can see here we are referencing various color variables. These were defined early on in our _colors.scss file and placed in settings/variables directory.

Next, we create a component file and name it _alert.scss. We place that file in the components directory. That file would look something like this:

// Alerts Component
// ========================================

// Variables
$alert-border-radius:      3px;
$alert-box-shadow:         rgba(0, 0, 0, 0.25);
$alert-box-spread:         0 0 10px;
$alert-margin:             0 auto $spacer-delta;
$alert-padding:            $spacer-delta;
$alert-position:           -9999px 0 null 0;
$alert-transition:         all 0.8s ease;
$alert-width:              100%;
$alert-z-index:            1001;

$alert-title-color:        $white-base;

$alert-icon-position-type: absolute;
$alert-icon-position:      5px 5px null null;
$alert-icon-width:         15px;


// Structure
@each $alert in $alerts {
  $name:     nth($alert, 1);
  $color-1:  nth($alert, 2);
  $color-2:  nth($alert, 3);

  .alert--#{$name} {
    @include box-shadow($alert-box-spread, $alert-box-shadow);
    @include font-type($primary-sans-serif, normal, 600);
    @include position(relative);
    @include transition($alert-transition);
    background: $color-1;
    border: 1px solid $color-2;
    border-radius: $alert-border-radius;
    margin: $alert-margin;
    padding: $alert-padding;
    text-align: center;
    width: $alert-width;
    z-index: $alert-z-index;

    .alert__icon-trigger {
      @include transition(opacity 0.5s ease);

      &:hover {
        opacity: 0.5;
      }
    }

    #app-icon{
      fill: $color-2;
      display: block;
    }

    .alert__title {
      @include s-foxtrot;
      color: $alert-title-color;
      margin: 0;
    }
  }

  .alert__icon {
    @include position($alert-icon-position-type, $alert-icon-position);
    color: $color-2;
    width: $alert-icon-width;
  }
};

In this file we define our basic alert component and scope specific local variables to it. These are defined at the top of the file. Why do this? Again, as your project grows your styles will need to adapt. Instead of searching the entire component to edit one value, you can scan the local variables list and make your change there.

Unlike the variables we define in our settings directory, local variables build the structure of our components or views. They are not reusable in more cases, thus do not belong in our settings directory.

After you have created your basic local variables you can define the components structure. This is where you will take advantage of the list or map you created. By looping over the list of alerts in this example we created four different alerts. A positive to developing the component this way is it allows us to create new alerts without having to edit the component file. We can simply append a new style to our list or map.

Note: Not all components need a variable's file. Take for instance a card. You may only have one type of card. If that is the case, don't waste your time creating a map for this component. If a time comes when you need more than one card type, refactor. Remember, start small and build out.


Modules

Since we covered base files at the start of this post, we will move on to modules or views.

These are specific styles that will not be reused. A site's navigation or footer are good examples of modules. These are used once on a site and if removed would cause issues with the site's experience.

Here is an example of a navigation module:

// Navigation Module Stylesheet
// ========================================

// Variables
$page-navogation-padding:           $spacer-delta;
$page-navigation-position:          0 null -10px 0;
$page-navigation-position-type:     absolute;
$page-navigation-spread:            -5px -6px 10px 0px;
$page-navigation-shadow:            rgba(34,23,78,0.25);
$page-navigation-width:             14rem;

$page-navigation-header-border:     1px solid #493A85;

$page-navbar-title-color:           $white-base;
$page-navbar-title-txt-transform:   uppercase;

$page-navbar-link-border:           3px solid transparent;
$page-navbar-link-color:            $white-base;
$page-navbar-link-txt-transform:    uppercase;

$page-navbar-link-background-hover: rgba(27, 15, 69, 0.15);
$page-navbar-link-border-hover:     3px solid $blue-base;

$navbar-link-icon-dimensions:       8px;
$navbar-link-icon-size:             100%;
$navbar-link-icon-transform:        -90deg;

$navbar-link-icon-transform-active: 0deg;


// Structure
.page__navigation {
  @include box-shadow(inset $page-navigation-spread, $page-navigation-shadow);
  @include position($page-navigation-position-type, $page-navigation-position);
  border-left: $page-navigation-header-border;
  overflow-y: scroll;
  width: $page-navigation-width;

  &.page__navigation--background {
    @include linear-gradient(-180deg, $purple-base 40%, $blue-base 100%);
  }
}

.page__navigation__header {
  border-bottom: $page-navigation-header-border;
  margin-bottom: $spacer-delta;
  padding: $page-navogation-padding;
  text-align: center;
}

.navbar__title {
  @include s-hotel;
  color: $page-navbar-title-color;
  padding: $page-navogation-padding $page-navogation-padding 0;
  text-transform: $page-navbar-title-txt-transform;
  text-indent: 3px;
}

.navbar__item {
  border-left: $page-navbar-link-border;
  @include position(relative);
  @include transition(background-color 0.5s ease);

  &:hover,
  &.navbar__item--active {
    border-left: $page-navbar-link-border-hover;
    background-color: $page-navbar-link-background-hover;
  }

  .navbar__link--toggle,
  .navbar__link {
    @include font-type($primary-sans-serif, normal, 700);
    color: $page-navbar-link-color;
    display: block;
    padding: $page-navogation-padding;
    text-transform: $page-navbar-link-txt-transform;
  }

  .navbar__link--toggle-icon::after {
    @include position(absolute, 20px 10px null null);
    @include transition(transform 0.3s ease);
    @include transform(rotate($navbar-link-icon-transform));
    background: image-url('icons/misc/navigation-arrow.svg') no-repeat;
    background-size: 100%;
    content: "";
    display: block;
    height: $navbar-link-icon-dimensions;
    width: $navbar-link-icon-dimensions;
  }

  &.navbar__item--active .navbar__link--toggle::after {
     @include transform(rotate($navbar-link-icon-transform-active));
  }
}

.navbar__submenu {
  height: 0;
  padding: 0;
  overflow: hidden;

  &.navbar__submenu--active {
    height: auto;
    padding: 0 $spacer-delta $spacer-delta;
  }
}

.submenu__item__link {
  @include transform(opacity 0.3s ease);
  color: $white-base;
  display: block;
  padding: $spacer-echo $spacer-charlie;

  &:hover {
    opacity: 0.75;
  }
}

Again, we contain the local variables to this file. They are structural variables that help to build the module, similar to a component's local variables.

Overall, we have found that this structure and file naming convention helps us create scalable systems. It's not without its flaws, but it beats a hot mess.

If you want to see this directory structure in action check out a style guide we recently put together for one of our clients. You can find the Github repository here.

More posts
  • Volunteering At RailsBridge Tulsa 2016

    This year Made By Munsters will be volunteering at the RailsBridge Tulsa Intro to Web Development for Women. RailsBridge offers free workshops that focus on increasing diversity in the technology industry.

    Read More
  • Mocking Global Configs with RSpec

    Using RSpec and need to mock a dynamic class that doesn't play well with introspection? Here's the simple way to get that mock working.

    Read More
  • Thoughts After Building Our First Ionic 2 App

    One of the most impressive aspects of developing the Ionic 2 app was just how mature and stable both Ionic 2 and Angular 2 were. It came as a surprise; I had expected to run into far more difficulty during this project.

    Read More