[Coldbox 6.1.0][Coldbox-Elixir 3.1.6] Best Practices for SCSS and Image Files

I’m getting started with Coldbox Elixir and so far it’s amazing! I love being able to fire up npm run watch and it will automatically compile my SCSS and refresh the browser via browsersync. However, I’m a little confused as to how I should proceed with using images in my Elixir projects.

Here’s what my folder structure currently looks like (simplified for this example):
/includes/
/css/ // compiled/minified css goes here
/images/ // webpack likes to put images here (sometimes)
/js/ // compiled/minified js goes here
/resources
/js/ // my raw source javascript files
/sass/ // my raw source scss files
/static/
/img/ // my original image files

When I place a reference to an image in my scss file like this:
.header {

background-image: url( ‘…/…/static/img/header-gradient.png’ );

}

Webpack makes a copy of the image to /includes/images/ and rewrites the rendered css file to point to the proper image.

However, when I make a subsequent change to my scss file, webpack deletes the image in /includes/images/ during the cleanup process and never re-creates it thus breaking the compiled CSS link.

This behavior makes me think that I might be treating images wrong. I don’t see any reason why a duplicate image file needs to be generated. Is there a way to use the existing images in my /static/img/ folder and have webpack automatically change the image reference when compiling CSS without making a copy of the image? In other words, it would be great if Webpack would simply find the reference in the SCSS file:
url( ‘…/…/static/img/header-gradient.png’ )
… and change it to
url( ‘…/static/img/header-gradient.png’ )
… so it stays relative to the compiled css file.

The problem I see with making duplicate copies images used in CSS files is that some images will remain in my /static/img/ folder, and any image that is referenced in CSS gets duplicated to /includes/images/. This behavior feels like it would make keeping images organized more difficult and also wastes space.

Here is my webpack.config file for reference:

const elixir = require( “coldbox-elixir” );
const webpack = require( “webpack” );

elixir.config.mergeConfig({
plugins: [
// globally scoped items which need to be available in all templates
new webpack.ProvidePlugin({
$ : “jquery”,
jQuery : “jquery”,
“window.jQuery”: “jquery”,
“window.$” : “jquery”,
“Vue” : [“vue/dist/vue.esm.js”, “default”],
“window.Vue” : [“vue/dist/vue.esm.js”, “default”]
})
],
devtool: “source-map”
});

/*


Elixir Asset Management

Elixir provides a clean, fluent API for defining some basic Gulp tasks
for your ColdBox application. By default, we are compiling the Sass
file for our application, as well as publishing vendor resources.

*/

module.exports = elixir( mix => {

mix.browserSync({
proxy: “localhost:62351”,
notify: false
});

// Mix App styles
mix
.js(
“app.js”,
{
name: “app”,
entryDirectory: “resources/js/”
}
)
.sass(
“app.scss”,
{
name: “app”,
entryDirectory: “resources/sass/”
}
)
.js(
[
“node_modules/jquery/dist/jquery.min.js”,
“node_modules/bootstrap/dist/js/bootstrap.min.js”
],
{
name : “vendor.min”,
entryDirectory : “”
}
);

} );

David,

The reason this is done is because of a couple off factors:

  1. The /resources/assets directory is typically removed from production packages

  2. The relative paths to the /resources version of the image may be outside of the webroot, depending on the pack and the way you have it configured.

That said, I’ve never experienced the issue you are describing with CSS assets disappearing and then not being re-copied on a change, and I use Elixir day in/out. This may be a bug, or it may be a configuration issue with the relative paths. You may want to report that as an issue on the Github repo.

You have another option, as well, which is to normalize any images referenced in your CSS file in as base64 ( you can also do this with fonts ). You can see this in the Relax repo - https://github.com/coldbox-modules/relax/blob/development/webpack.config.js#L19 – where any font or image under 100000 bytes is automatically normalized as base64 in to the CSS file.

That’s not always ideal and makes your CSS much bigger, depending on how many images you use, but it is an option.

HTH,

Jon

Jon, thank you! Your input was very helpful. I can see I still have a lot to learn in regards to structuring my app with Elixir. I hadn’t considered keeping the resources/assets directory out of the production environment. I’ve been having a hard time understanding the Coldbox Elixir documentation so I’ve been looking at the Laraval docs to try and gain insight. One such page (Compiling Assets (Mix) - Laravel - The PHP Framework For Web Artisans) gave me an idea that I’ve put into place until I can better understand how to use Elixir in my workflow.

What I did to temporarily address the image issue was to add a slash “/” at the beginning of each URL() statement in my scss files. This tells Webpack to ignore those files and it won’t duplicate them in the /includes/images/ folder. For example:
background-image: URL( ‘./static/img/my-image.png’ );
becomes:
background-image: URL( ‘/static/img/my-image.png’ );
The only downside I see is that you have to always make your URLs absolute from the webroot.

Now I’m currently struggling with how to bring in page-specific .js files. I realized I can’t include them like I would have expected like this because none of the dependencies are available (I suspect due to runtime.js).

// won't work! Even though jquery is in my vendor.js file, it won't be available for some reason.

Do you happen to know any public repos or examples of code that use Elixir in a best-practices way that allows for different .js files for pages that need them? The docs don’t seem to cover it, and I don’t see anything on cfcasts either. I could probably figure it out if I had access to some working example code to sift through.