• est. reading time: 3 min

Module Configs in Stylesheets (Sass)

Techniques to create structured and reusable stylesheets.

Since working with Sass one of the most important parts of my stylesheets is the module config.

This lays the groundwork for

  • configurable code
  • design consistency
  • reusability
  • theming
  • non-bloated output

OK - enough with the buzzwords and let's get to it. The concepts are certainly not new and do not only apply to Sass. They can also be applied when using other pre-processors or CSS Custom Properties.

When creating stylesheets I follow this structure:

// TITLE
// Documentation (High-Level)

// Module Config
// Documentation (Low-Level)

// Styles
// MODULE
// HIGH-LEVEL documentation on the general purpose, variations, usage information, etc.

$module-variable: value !default;

// [1] LOW-LEVEL documentation about the declaration(s).
.module {
declaration: value; // [1]
}

#Terminology

So what is a module?

By module I mean a single stylesheet or partial. This is a generic term for objects, components, blocks, tools, etc. - depending on the used methodology and naming conventions like BEM, ITCSS or SMACSS.

For example when using ITCSS:

  • grid: object
  • modal: component
  • text-color: utility

Despite each partial being on different layers, they can all be referred to as modules.

  • Plugin or vendor code? Module.
  • Settings file? Module.
  • Awesome mixin? Module.

The module config consists of every setting and variable a module can have.

To make this module configurable and reusable.

#Variable Extraction

A static value that needs to be potentially configured or is subject to change, is extracted to a variable into the module config.

// STATIC (within declaration)
.module {
padding: 1rem;
background-color: #fff;
}

.module__title {
margin-bottom: 0.5rem;
font-size: 1.2rem;
}
// DYNAMIC (with module config)
$module-spacing: 1rem;
$module-background: #fff;
$module-title-font-size: 1.5rem;
$module-title-spacing: 1rem;


.module {
padding: $module-spacing;
background-color: $module-background;
}

.module__title {
margin-bottom: $module-title-spacing;
font-size: $module-title-font-size;
}

The module's styles can now be configured by changing the variables at the top without the need to go into the actual styles and modify the declarations. This is especially useful when the same variable is used in multiple declarations.

Values that are connected can now build on other variables. By creating extra variables instead of using and modifying the existing variable in the declaration, they can be dependent or independent.

// dependent
$module-spacing: 1rem;
$module-title-spacing: $module-spacing / 2; // halve the inner-spacing
$module-title-font-size: 1.2rem;
$module-title-icon-size: $module-title-font-size * 2; // double the title-size

If the module-spacing changes, the title-spacing changes accordingly - if that's what you want.

// independent
$module-spacing: 1rem;
$module-title-spacing: 0.5rem;
$module-title-font-size: 24px;
$module-title-icon-size: 32px;

This creates a config for the module's styles.

Convention: I highly recommend to come up with a schema or convention on how to name variables. I mostly use a schema like namespace-element-property-state@screen-size, e.g.

$button-padding
$button-padding@small
$button-background-color
$button-background-color-hover
$button-background-color-disabled

$button-text-size
$button-text-size-small

#Global Variables

In order to create a consistent and maintainable UI, fundamentals like color-palette, spacings and typography need to be globally defined.

I like to refer to those fundamentals as design tokens. A great term first tossed by @jina. There is W3C Community Group for it, go check out @DesignTokens.

Let's take a look at an example of different modules. The configs have multiple values that should be defined on a global level.

// _objects.grid.scss

// GRID
$grid-gap: 1rem; //global spacing
$grid-items-per-row: 3; //module specific
$grid-items-per-row@small: 1; //module specific
// _components.modal.scss

// MODAL
$modal-spacing: 1rem; //global spacing
$modal-height: 80vh; //module specific
$modal-height@small: 100vh; //module specific
$modal-background: #fff; //color in global palette
$modal-title-font-size: 1.5rem; //global typography-size
$modal-title-spacing: 0.5rem; //global spacing (variant)
$modal-backdrop-color: rgba(#000, 0.5); //color in global palette, global opacity value
// _components.tooltip.scss

// TOOLTIP
$tooltip-padding: 0.5rem; //global spacing (variant)
$tooltip-background-color: #0f0; //color in global palette
$tooltip-text-size: 0.8rem; //global typography-size
$tooltip-text-color: #fff; //color in global palette
$tooltip-indicator-size: 10px; //module specific

By using settings files the values can be used globally.

// _settings.spacings.scss
$SPACING: 1rem;
$SPACING-SMALL: $SPACING / 2;
$SPACING-LARGE: $SPACING * 2;


// _settings.typography.scss
$FONT-SIZE: 16px;
$FONT-SIZE-XSMALL: 10px;
$FONT-SIZE-SMALL: 12px;
$FONT-SIZE-LARGE: 24px;
$FONT-SIZE-XLARGE: 32px;


// _settings.colors.scss
$COLOR-PRIMARY: #0f0;

$COLOR-WHITE: #fff;
$COLOR-BLACK: #000;

// _settings.opacity.scss
$OPACITY-FULL: 1;
$OPACITY-FADE: 0.2;
$OPACITY-DARK: 0.8;

Applying the settings to the module configs:

// _objects.grid.scss

// GRID
$grid-gap: $SPACING;
$grid-items-per-row: 3;
$grid-items-per-row@small: 1;
// _components.modal.scss

// MODAL
$modal-spacing: $SPACING;
$modal-height: 80vh;
$modal-height@small: 100vh;
$modal-background: $COLOR-WHITE;
$modal-title-font-size: $FONT-SIZE-LARGE;
$modal-title-spacing: $SPACING-SMALL;
$modal-backdrop-color: rgba($COLOR-BLACK, $OPACITY-DARK);
// _components.tooltip.scss

// TOOLTIP
$tooltip-padding: $SPACING-SMALL;
$tooltip-background-color: $COLOR-PRIMARY;
$tooltip-text-size: $FONT-SIZE-SMALL;
$tooltip-text-color: $COLOR-WHITE;
$tooltip-indicator-size: 10px;

When the global font-size needs to change, this can be configured in settings.typograhpy.scss and the modules using these settings inherit from it.

We now have a single source of truth for design tokens in the respective settings files.

Convention: Why are the variables written in uppercase? This is convention I follow for global variables. The idea is borrowed from other programming languages where global variables or constants are written in uppercase. Sass variables are case-sensitive so it has both an effect on the functionality (see demo) and readability.

Global variables should not be used directly in style declarations as it decreases the maintainability and by writing them in uppercase, spotting this "misuse" is easier.

Instead, extract values into module variables and check if those values are global or module specific.


// DON'T
.module {
display: block;
width: 100%;
padding: $SPACING; 👎🏼
background-color: $COLOR-PRIMARY; 👎🏼
}



// DO
$module-padding: $SPACING;
$module-background: $COLOR-PRIMARY;

.module {
display: block;
width: 100%;
padding: $module-padding;
background-color: $module-background;
}

#Conclusion

  • extract module variables to module config
  • use only module variables in declarations
  • conventions:
    • mark global variables, e.g. write them in uppercase

    • use variable naming schema, e.g.

      namespace-element-property-state@screen-size