Nucleum is an opinionated, performance oriented web development starter kit. It can be used as-is as a static site builder, or can be configured and integrated into many different web development environments and sites or apps structures.
The extras folder contains configuration details for WordPress projects. Check the WordPress with Nucleum documentation to learn more about how to set up Nucleum for WordPress based projects.
- Front-end stack
Nucleum is published as an npm package which allows us to start a new project in a only a few steps:
yarn init
yarn add nucleum
yarn run nucleum init
This will create default src
and config
files in your project directory.
The init
command also updates your package.json
file to include start
and build
scripts for Nucleum:
// package.json
"scripts": {
"start": "yarn run nucleum",
"build": "yarn run nucleum build"
}
which you can then use on the command line:
# command line
yarn start
yarn build
and also adds a browserslist
configuration that you can customize based on your project needs.
// package.json
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
Your project's CSS and JavaScript files will be compiled for production to include the necessary prefixes or syntax for the browsers you need to support.
You can find more details about browserslist and the type of queries its configuration requires by visiting the browserslist repo.
You can generate basic config files with:
yarn run nucleum init-config
Then edit the configs to match the needs of your project.
Nucleum requires at least Node 8.10.0. While you can install Node a variety of ways, we highly recommend using nvm to install and manage Node versions.
We recommend yarn
over npm
mainly for its yarn run
command which allows us to run package.json
scripts
and node_modules/.bin
executables in a nice convenience.
All commands should be run through yarn run
.
yarn start
This is where the magic happens. The perfect workflow. This runs the development task, which starts compiling, watching, and live updating all our files as we change them. BrowserSync will start a server on port 3000, or do whatever you've configured it to do. You'll be able to see live changes in all connected browsers. Don't forget about the additional BrowserSync tools available on port 3001!
yarn build
Compiles files for production to your destination directory. JS files are built using Webpack with standard production optimisations (Uglify, etc.). CSS is run through CSSNano and PurgeCSS. If rev
is set to true
in your task-config.js
file, filenames will be hashed (file.css -> file-a8908d9io20.css) so your server may cache them indefinitely.
You may override the default configuration by creating a config
folder with the following two files in it: path-config.json
and task-config.js
. These files will be created by any of the init
tasks, or you can generate only the config files with the following command:
yarn run nucleum init-config
By default, Nucleum expects these files to live in a ./config
at the root of your project. You may specify an alternative relative location by setting an environment variable:
// package.json
"scripts": {
"nucleum": "NUCLEUM_CONFIG_PATH='./some/location' nucleum"
}
# command line
yarn run nucleum
The files must be named path-config.json
and task-config.js
.
path-config.json
This file specifies the src
and dest
root directories, and src
and dest
for each task, relative to the configured root. For example, if your source files live in a folder called app
, and your compiled files should be output to a folder called static
, you'd update the src
and dest
properties here to reflect that.
task-config.js
This file exposes per-task configuration and overrides. At minimum, you just need to set the task to true
to enable the task with its default configuration. If you wish to configure a task, provide a configuration object instead.
- Any task may be disabled by setting the value to
false
. For example, if your project has its own handling HTML and template engine (WordPress, Craft, etc), you'll want to sethtml
tofalse
in your task-config. - All asset tasks have an
extensions
option that can be used to overwrite the ones that are processed and watched.
See task config defaults for a closer look. All configuration objects will be merged with these defaults. Note that array
options are replaced rather than merged or concatenated.
Options to pass to browserSync.
If you're using Pug (built in) to compile a static site, you'll want to use the server
and tell it which server to serve up via the baseDir
option.
browserSync: {
server: {
baseDir: "public";
}
}
If you're running another server (Docker for example), you'll want to use the proxy
option, along with files
to tell browserSync to watch additional files (like your templates).
browserSync: {
proxy: {
target: "localhost"
},
files: ["public/wp-content/themes/nucleum/**/*.php"]
}
If you need to turn on polling within webpack-dev-middleware, specify watchOptions
within this section, too.
browserSync: {
watchOptions: {
poll: true,
aggregateTimeout: 300
}
}
If you need to add extra middlewares, specify extraMiddlewares
within the server
subsection of this section.
browserSync: {
server: {
extraMiddlewares: [historyApiFallbackMiddleware],
},
},
If you need to override completely all server's middleware, specify middleware
within the server
subsection of this section.
browserSync: {
server: {
middleware: [
/* On your own! Note that default 'webpack-dev-middleware' will not be enabled using this option */
],
},
}
Under the hood, JS is compiled with Webpack with a heavily customized Webpack file to get you up and running with little to no configuration. An API for configuring some of the most commonly accessed options are exposed, along with some other helpers for scoping to environment. Additionally, you can get full access to modify Nucleum's webpackConfig
via the customizeWebpackConfig
option.
Discrete js bundle entry points. A js file will be bundled for each item. Paths are relative to the js
folder. This maps directly to webpackConfig.entry
.
The public path to your assets on your server. Only needed if this differs from the result of path.join(PATH_CONFIG.dest, PATH_CONFIG.javascripts.dest)
. Maps directly to webpackConfig.publicPath
Sets the webpack devtool option in development mode. Defaults to eval-cheap-module-source-map
, one of the fastest source map options. To enable sourcemaps in production builds, use customizeWebpackConfig
.
Object to overwrite the default Babel loader config object. This defaults to { presets: [["@babel/preset-env", { "modules": false }] }
. Same format as a .babelrc
file.
Object to extend the default config for entire Babel loader object. See Webpack loader documentation for details.
Key value list of variables that should be provided for modules to resolve dependencies on import using webpack.ProvidePlugin. A common example is making jQuery available to all modules (jQuery plugins need this). In that scenario, with jquery
installed via yarn
, add this to your javascripts config:
provide: {
$: "jquery",
jQuery: "jquery"
}
Under the hood, this gets passed directly to webpack.ProvidePlugin in the webpack config.
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
];
Define additional webpack plugins that should be used in all environments.
Define additional webpack loaders that should be used in all environments. Adds to webpackConfig.module.rules
Specify additional environment specific configuration to be merged in with Nucleum's defaults
Production Only:
Example:
production: {
devtool: "hidden-source-map",
definePlugin: {
SOME_API_KEY: "abcdefg"
},
plugins: (webpack) => { return [ new webpack.IgnorePlugin(/jsdom$/) ] },
loaders: [] // Adds to `webpackConfig.module.rules`
}
By default, the env
will be "development"
when running yarn run nucleum
, and "production"
when running yarn run nucleum build
.
By default, webpack HMR will simply do a full browser refresh when your js files change. If your code takes advantage of hot module replacement methods, modules will be hot loaded.
Defaults to:
hot: {
enabled: true,
reload: true,
quiet: true,
react: false
}
If you're using React yarn add react-hot-loader
and set react: true
to enable react-hot-loader. Follow the docs and update your React app to take advantage. Also install @babel/preset-react
and add it to the babel presets configuration.
In the event that an option you need is not exposed, you may access, modify and return a further customized webpackConfig by providing this option as a function. The function will receive the Nucleum webpackConfig
, env
and webpack
as params. The env
value will be either development
(yarn run nucleum
) or production
(yarn run nucleum build
).
customizeWebpackConfig: function (webpackConfig, env, webpack) {
if(env === "production") {
webpackConfig.devtool = "nosources-source-map"
}
return webpackConfig
}
CAUTION! Avoid overwriting webpackConfig.entry
or webpackConfig.plugins
via this function, and rely on the entry
and plugins
options above to avoid breaking Nucleum's hot-loading and file revisioning setup (view source).
PostCSS Preset Env lets you convert modern CSS into something most browsers can understand, determining the polyfills you need based on your targeted browsers or runtime environments.
Be default we set the stage of postcssPresetEnv to 0 which will enable experimental feature of CSS.
Within this object you can also override the browserslist
configuration and/or autoprefixer
settings. Please read more about postcssPresetEnv configuration on their repo page https://github.com/csstools/postcss-preset-env#options.
stylesheets: {
presetEnv: {
stage: 3,
browsers: "last 2 versions",
autoprefixer: { grid: true } // passing `autoprefixer: false` disables autoprefixer
}
}
PostCSS Normalize lets you use the parts of normalize.css you need from your browserslist
. Please read more about postcssNormalize configuration option on their repo page https://github.com/csstools/postcss-normalize#options. By default the forceImport
option is set to true
in Nucleum, so we don't have to specifically include the library at the beginning of our sass file.
cssnano takes your nicely formatted CSS and runs it through many focused optimisations, to ensure that the final result is as small as possible for a production environment. This optimisation is only run on the build task.
purgecss is a tool to remove unused CSS. Purgecss has a list of options that allow you to customize its behavior. Customization can improve the performance and efficiency of Purgecss. You can customize its configuration with the options found on their documentation page https://www.purgecss.com/configuration. This optimisation (when enabled) will only run on the build task.
stylesheets: {
purgecss: {
content: ["../views/**/*.pug"], // the path should be relative to your src stylesheet file
extensions: ["pug"] // this will be assigned to the built-in extractor extensions object
}
}
IMPORTANT All of the above stylesheets options are included as PostCSS plugins.
Allows passing extra postcss plugins into the pipeline.
stylesheets: {
postCssPlugins: [
pluginName(pluginConfig)
]
}
Options to pass to node-sass.
Defaults to { includePaths: ["./node_modules"] }
so you can @import
files installed to node_modules
.
We use critical to extract and inline critical-path (above-the-fold) CSS from HTML.
IMPORTANT Use the path-config.json
file (criticalCss
property) to set critical src
and dest
. Setting these properties inside criticalCss.config
will not work.
There are two ways of generating critical CSS.
- Running the gulp task through all the templates generated at the root of the public folder.
// path-config.json
"criticalCss": {
"siteUrl": "",
"src": "./**/*.html",
"dest": "./"
}
// task-config.js
criticalCss: {
config: {
// ...critical options
base: "./public",
width: 1200,
height: 1200
}
}
- Creating a
pages
array where each page object sets anurl
and atemplate
property. This is useful for generating critical CSS when the templates are generated by CMSs or other tools.
// path-config.json
"criticalCss": {
"siteUrl": "http:https://localhost",
"src": "", // this should be empty because the src is set per page in task-config.js
"dest": "style/critical"
}
// task-config.js
criticalCss: {
config: {
// ...critical options
base: "./public",
width: 1200,
height: 1200
},
pages: [
{
url: "index",
template: "index"
}
]
}
IMPORTANT If pages.url
is not set to a .html
file or to a file system path make sure your website is running on a local server for the criticalCss task to work.
Setting the config
object, you can create the necessary configuration for critical.
Note: If you are on a platform that's already handling html (WordPress), set html: false
or delete the configuration object completely from task-config.js
. If that's the case, don't forget to use the BrowserSync files
option in the browserSync
config object to start watching your templates folder.
Robust templating with Pug.
gulp-data dataFunction
is used to provide data to templates. Defaults to reading in a global JSON, specified by the dataFile
option.
A path to a JSON file containing data to use in your templates via gulp-data
.
Options to pass to [gulp-htmlmin
](https://github.com/jonschlinkert/gulp-htmlmin.
You'll want to exclude some folders from being compiled directly. This defaults to: ["components", "data", "includes", "layout", "mixins"]
.
There are some files that belong in your root destination directory that you won't want to process or revision in production. Things like favicons, app icons, etc., should go in src/static
, and will get copied over to public
as a last step (after revisioning in production). Nothing should ever go directly in public
, since it gets completely trashed and re-build when running the default
or production
tasks.
Options passed to gulp.src
. See gulp documentation for details. Defaults to:
static: {
srcOptions: {
dot: true; // include dotfiles
}
}
These tasks simply copy files from src
to dest
configured in path-config.json
. Nothing to configure here other that specifying extensions or disabling the task.
Generates an SVG Sprite from .svg
files in src/icons
. You can either include the created SVG directly on the page and reference the icon by id like this:
<svg viewBox="0 0 1 1"><use xlink:href="#my-icon"></use></svg>
or reference the image remotely:
<svg viewBox="0 0 1 1"><use xlink:href="img/icons.svg#my-icon"></use></svg>
If you reference the sprite remotely, be sure to include something like inline-svg-sprite to ensure external loading works on Internet Explorer.
Nucleum includes a mixin inside src/views/mixins/_mixins.pug
which generates the required svg markup for your icons, so you can just do:
+icon("my-icon")
Which outputs:
<svg class="c-icon"><use xlink:href="img/icons.svg#my-icon"></use></svg>
This particular setup allows styling 2 different colors from your CSS. You can have unlimited colors hard coded into your svg.
In the following example, the first path will be red
, the second will be white
, and the third will be blue
. Paths without a fill attribute will inherit the fill
property from CSS. Paths with fill="currentColor"
will inherit the current CSS color
value, and hard-coded fills will not be overwritten, since inline styles trump CSS values.
.c-icon {
fill: red;
color: white;
}
<svg xmlns="http:https://www.w3.org/2000/svg">
<path d="..."/>
<path fill="currentColor" d="..."/>
<path fill="blue" d="..."/>
</svg>
Make sure you draw your SVGs on a square (500 x 500) canvas, center your artwork, and expanding/combining any shapes of the same color. This last step is important.
We also include postcss-svg which allows us to inline SVGs (encoded) in out CSS code.
To use this option make sure the <path/>
styles are not set with individual attributes (like fill="currentColor"
or fill="red"
) but within a single style
attribute, like so:
<svg xmlns="http:https://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path style="fill: currentColor; fill: var(--icon-fill-color, currentColor)" d="M7 7h2v5H7V7zm1-1a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm0 8A6 6 0 1 0 8 2a6 6 0 0 0 0 12zm0 2A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</svg>
The first fill
property is a fallback for Internet Explorer while the second one is picked up by browsers that support CSS Custom Properties (CSS Variables).
In our SCSS file, when we need an inline icon we can just reference the symbol id from the SVG sprite, like this:
.icon {
background-image: url("icons#icon-info" param(--icon-fill-color color("success")));
}
clean: {
patterns: [
path.resolve(process.env.PWD, "dist/assets"),
path.resolve(process.env.PWD, "dist/templates")
];
}
By default, the entire dest
directory is deleted before each build. By setting the clean.patterns
option, you can specify which directory or directories (using globbing syntax) should be deleted instead. Use this if you have subdirectories within the dest
directory that should be left alone (media uploaded through a CMS, say).
Filenames can be revisioned when running the production build
task. If you want to enable this behaviour, you can set rev
to true.
production: {
rev: true;
}
If you wish to define additional gulp tasks, and have them run at a certain point in the build process, you may use this configuration to do so via the following config object:
additionalTasks: {
initialize(gulp, PATH_CONFIG, TASK_CONFIG) {
// Add gulp tasks here
},
development: {
prebuild: null,
postbuild: null
},
production: {
prebuild: null,
postbuild: null
}
}
Nucleum will call initialize
, passing in gulp
, along with the path and task configs. Use this method to define or require
additional gulp tasks. You can specify when and in what order your custom tasks should run in the production
and development
prebuild
and postbuild
options.
For example, say you had a sprite task you wanted to run before your css compiled, and in production, you wanted to run an image compression task you had after all assets had been compiled. Your configuration might look something like this:
additionalTasks: {
initialize(gulp, PATH_CONFIG, TASK_CONFIG) {
gulp.task("createPngSprite", function() {
// do stuff
})
gulp.task("compressImages", function() {
// compress all the things
})
},
development: {
prebuild: ["createPngSprite"],
postbuild: null
},
production: {
prebuild: ["createPngSprite"],
postbuild: ["compressImages"]
}
}
Yep! See additionalTasks.
You can also clone this repo, copy over the gulpfile.js folder and package.json dependencies and run gulp
instead of installing it as a modules directly, or you could fork and maintain your own custom setup.
JS files are compiled and live-update via BrowserSync + WebpackDevMiddleware + WebpackHotMiddleware. That means, that you won't actually see .js
files output to your destination directory during development, but they will be available to your browser running on the BrowserSync port.
Gulp tasks! Built combining the following:
Feature | Packages Used |
---|---|
CSS | Sass (Libsass via node-sass), PostCSS with postcss-preset-env, purgecss, postcss-normalize, cssnano, postcssSVG, Source Maps |
JavaScript | Babel, babel-preset-env, Webpack |
HTML | Pug, gulp-data |
Icons | Auto-generated SVG Sprites |
Live Updating | BrowserSync, |
Webpack Dev Middleware, | |
Webpack Hot Middleware | |
Production Builds | CSS is minified and purged, JS is compressed and optimized with various Webpack plugins, filename md5 hashing (reving), file size reporting. |
Extras:
Feature | Packages Used |
---|---|
WordPress | Docker, docker-compose, WordPress quick start |
Sass Libraries | Bourbon, Adaptable |