A parser for generating dynamic theme stylesheets from Sass.
- Workflows
- Installation, Upgrade, and API
- Full API options
- Webpack integration
- Gulp integration
- Credits
The Problem:
We're building a site that gets themed with customizable colors, fonts, sizes, etc. So, we set up a base stylesheet for the site, and then maintain a separate theme stylesheet for custom style overrides.
This works, but makes updates difficult. All changes in the base stylesheet must be mirrored in the theme-specific overrides. Keeping these stylesheets synchronized is tedious and error-prone. It would be great if we could just automate the generation of these theme overrides from the base source... or, just generate a CSS template to be rendered by our application with theme variables at runtime.
This is SassThematic.
Workflows
SassThematic accomodates two unique workflows for generating CSS themes – each takes a different approach to the problem. A process overview for each workflow is available on the wiki:
Install
Install the NPM package:
npm install sass-thematic --save-dev
Upgrading to v2.x
The v2.x API has changed significantly to better support selecting a workflow. Breaking changes:
- Smaller API, tailored via options. Tree pruning no longer happens by default.
- Webpack integration removed. Webpack plugins should independently wrap this module.
API
SassThematic provides the following API. All methods take similar options, which are fully outlined below.
parseAST
- thematic.parseAST( options, callback )
- thematic.parseASTSync( options )
Parses and returns a raw abstract syntax tree of your deeply-nested Sass source. The returned object is a gonzales-pe node tree with all @import
statements replaced by the imported stylesheet nodes. Use this complete source tree to make your own modifications.
var thematic = require('sass-thematic');
// Async
thematic.parseAST({
file: './styles/main.scss',
includePaths: ['./lib/']
}, function(err, ast) {
console.log(ast);
});
// Sync
var ast = thematic.parseASTSync({ ...options... });
parseSass
- thematic.parseSass( options, callback )
- thematic.parseSassSync( options )
Parses and returns a raw Sass string of your deeply-nested Sass source with optional transformations applied. This raw Sass may be run through the Sass compiler. Options:
varsFile
orvarsData
: required to identify relevant theme variables.treeRemoval
: optionally removes Sass rules that do not implement theme variables.varsRemoval
: optionally removes theme variable imports.template
: optionally transforms theme variables into template identifiers.
var thematic = require('sass-thematic');
// Async
thematic.parseSass({
file: './styles/main.scss',
varsFile: './styles/_theme.scss',
includePaths: ['./lib/'],
treeRemoval: true,
varsRemoval: true,
template: true
}, function(err, sassString) {
console.log(sassString);
});
// Sync
var sassString = thematic.parseSassSync({ ...options... });
renderCSS
- thematic.renderCSS( options, callback )
- thematic.renderCSSSync( options )
Renders a CSS string from your Sass source. Sass is parsed with optional transformations applied, then custom theme variables are prepended, and lastly this custom themed Sass is run through the Sass compiler. Options:
varsFile
orvarsData
: required to identify relevant theme variables.themeFile
orthemeData
: required to provide variables for the themed CSS rendering.treeRemoval
: optionally removes Sass rules that do not implement theme variables.varsRemoval
: optionally removes theme variable imports.sassOptions
: options object passed to the Node-Sass compiler.
var thematic = require('sass-thematic');
// Async
thematic.renderCSS({
file: './styles/main.scss',
varsFile: './styles/_theme.scss',
themeData: '$color1: red; $color2: green;',
includePaths: ['./lib/']
}, function(err, cssString) {
console.log(cssString);
});
// Sync
var cssString = thematic.renderCSSSync({ ...options... });
renderTemplate
- thematic.renderTemplate( options, callback )
- thematic.renderTemplateSync( options )
Renders a CSS template string from your Sass source. Sass is parsed with theme variables preserved as identifiers (and other optional transformations applied), then CSS is compiled from the transformed source, and lastly field identifiers are filled back in with template interpolation fields. Options:
varsFile
orvarsData
: required to identify relevant theme variables.treeRemoval
: optionally removes Sass rules that do not implement theme variables.varsRemoval
: optionally removes theme variable imports.templateOpen
: token used to open template interpolation fields (ie:<%=
).templateClose
: token used to close template interpolation fields (ie:%>
).templateSnakeCase
: formats all variable names assnake_case
(lowercase with underscores).sassOptions
: options object passed to the Node-Sass compiler.
Note: theme variable names must pass through the Sass compiler as literal string identifiers, therefore restrictions apply on how theme variables may be used in pre-rendered Sass contexts.
var thematic = require('sass-thematic');
// Async
thematic.renderTemplate({
file: './styles/main.scss',
varsFile: './styles/_theme.scss',
includePaths: ['./lib/'],
templateOpen: '<%=',
templateClose: '%>'
}, function(err, templateString) {
console.log(templateString);
});
// Sync
var templateString = thematic.renderTemplateSync({ ...options... });
Parser API
The primary API above is designed to accomodate common use-cases. However, custom build tools may want to take advantage of the underlying parser API, documented on the wiki.
Full API Options
Required for all methods, one or both:
-
file
: String. Path to the main Sass file to load and parse. This may be an absolute path, or else a relative path fromcwd
. -
data
: String. A raw Sass string to parse. You may still provide afile
option as filepath context for mapping imports.
Required for Sass parsing methods, one of:
-
varsFile
: String. Path to a file containing all theme variables. This may be an absolute path, or else a relative path fromcwd
. This file must contain all theme variable definitions, and nothing else. Variables may be formatted as Sass or JSON. -
varsData
: String. Data containing variable definitions for all theme variables. Should be formatted as Sass ($color1: red; $color2: black;
) or JSON ({"color1": "red", "color2": "black"}
).
Required for CSS rendering methods, one of:
-
themeFile
: String. Path to a file containing all theme variables to render CSS with. This may be an absolute path, or else a relative path fromcwd
. -
themeData
: String. Data containing Sass variable definitions for all theme variables rendered into CSS. Should be formatted as Sass ($color1: red; $color2: black;
) or JSON ({"color1": "red", "color2": "black"}
).
Optional options:
-
includePaths
: Array. List of base paths to search while performing file lookups. These should be absolute directory paths, or else relative tocwd
. This operates just like thenode-sass
option of the same name. -
cwd
: String. Path of the directory to resolvefile
,varsFile
,themeFile
, andincludePaths
references from. Usesprocess.cwd()
by default. -
treeRemoval
: Boolean. Enables the removal of tree nodes that do not implement theme variables. -
varsRemoval
: Boolean. Enables the removal of theme variable imports. Be sure to use the Sass!default
flag when leaving theme variable imports in the source tree. -
templateOpen
: String. The opening token for template interpolation fields. Uses ERB-style<%=
by default. -
templateClose
: String. The closing token for template interpolation fields. Uses ERB-style%>
by default. -
templateSnakeCase
: Boolean. Enables the transformation of template variable names intosnake_case
(lowercase with underscores). -
fieldOpen
: String. The opening token wrapping field literals that get sent through the Sass compiler. Uses____
(four underscores) by default. -
fieldClose
: String. The closing token wrapping field literals that get sent through the Sass compiler. Uses____
(four underscores) by default. -
sassOptions
: Object. For rendering methods, this options object is passed through to the Sass compiler. See node-sass docs for possible values.
Webpack Builders
As of v2.x, Webpack integration has been broken out into wrapper modules. Build objectives vary, therefore SassThematic remains unopinionated about how it hooks into a build pipeline. The following Webpack wrappers exist:
- sass-theme-template-loader: compiles CSS templates with theme variables as interpolation fields.
- sass-thematic-override-plugin: a live-compiler for theme overrides included in SassThematic v1.3. No longer supported, but available if anyone wants to spin it into a community project.
Gulp Pipe
It's pretty simple to setup a Gulp pipe that hooks multiple Sass entry point files into SassThematic. Use the following as a basic template:
var gulp = require('gulp');
var vinyl = require('vinyl');
var through2 = require('through2');
var thematic = require('sass-thematic');
// SassThematic Gulp pipe:
function sassTheme(opts) {
var output = '';
return through2.obj(function(file, enc, done) {
opts.file = file.path;
opts.data = file.contents.toString('utf-8');
thematic.parseSass(opts, function(err, result) {
output += result;
done();
});
},
function(done) {
this.push(new vinyl({
path: 'theme.scss',
contents: new Buffer(output)
}));
done();
});
}
// Then use it...
gulp.task('theme', function() {
return gulp.src('components/**/index.scss')
.pipe(sassTheme({ ... opts ...}))
.pipe(gulp.dest('/path/to/output/dir'));
});
Credit
This toolkit would be impossible without the hard work of @tonyganch on the gonzales-pe lexer, which provides the framework for intelligently dismantling Sass. Serious kudos.
Brought to you by Vox Media.