Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A proposal for Sass Modules

Since I'm unable to find an issue that has a clear module proposal, I'll make a proposal here for a significant reworking of dependencies in the future for Sass+.

Goals:

  • Allow the possiblity of concurrent builds
  • Create cleaner APIs for libraries
  • Allow for larger teams to work on the same codebase more reasonably
  • Allow for including testing as part of independent modules
  • Allow for "matrix testing" of components, mixed and matched! (CPAN-like)

Some rules:

  • module names are unique
  • module names are declared
  • function, variable, and mixin exports must be declared with @export
  • modules do not emit anything directly
  • @import continues functioning the same way, but disrupts faster compliation
  • mixins are the primary way to emit from a module
  • default function / mixin names are init
  • @export throws an error when mixins/functions

There are two primary different types of modules:

  1. Full - Will describe more in later sections, but it's basically a "full module", defining all the different things we might want to have modules declare, plus configuration, (optional: versioning, testing)
  2. Simple - When the module is a single, traditional Sass file. Think _var.scss files, only used in the compilation directory itself. We use sensible defaults here.

For "full-modules" there are three levels:

  1. Standard Library Modules - Functions and mixins global to Sass and implemented exactly the same way in every interpreter. Some of them might be definable in Sass directly as convenience methods, or they may be so low level as to require specific implementation on every platform.
  2. Native Extension Modules - Extensions to your local installation of Sass, defined by whatever platform you are using. This is where things like Eyeglass extensions would most likely fall.
  3. Local Directory Module - Within the compilation directory (or nearby), a folder that takes the structure of a module.

Full modules are structured this way:

-- my_framework/
  --lib/   (not required, but recommended)
    main.scss
    helpers.scss
    vars.scss
  -- tests/
    function_test.scss
    helpers.scss
  my_framework.scss

The Sass file with the same name as the module itself is the primary definition point for the whole module and is automatically loaded.

// my_framework/my_framework.scss
@import "lib/helpers";
@import "lib/vars";
@import "lib/main";

Note here that we are using @import... this is the same naive method we have now, that is a simple inliner. You can definitely use sub-modules, but in this example, we're just inlining everything.

Modules are different than Standard Sass files, because they don't emit. In fact, if the above code only included "standard sass", this would be a useless module. Instead, the implementation files need to explicitly call @export before any definitions, if they want to make them available.

In this example, helpers would define content that we can use anywhere in this module, but not outside of it.

// my_framework/lib/helpers.scss
@use "susy" as $susy;

@mixin grid($width) {
  @include $susy.grid($width);
}

I'm also showing here that we can include other modules that are available. If the module isn't available, we'd throw an error.

// my_framework/lib/vars.scss

$private: red;
@export $public: blue;

There isn't anything special about this vars file, just showing public vs private vars.

// my_framework/lib/main.scss

// the "default" mixin
@export @mixin init() {
  .defaults {
    //this is stuff we need to run this library at all... it WILL be emitted, only if we `@include $my_framework` in our Project!
  }
}

@export @mixin grid-one-hundred() {
  @import grid(100px);
}

@export @mixin grid-two-hundred() {
  @import grid(200px);
}

From within our project, we can use this module like so...

// project/application.scss
@use "my_framework" as $framework;

@include $framework; //bootstrapps it with an init! not required, but some libs would want this

@include $framework.grid-one-hundred();

body { 
  color: $framework.public; // defined in my_framework/lib/vars.scss
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment