Tags

, , , ,

Every client side web developer or even the occasional advanced user knows what a closure is suppose to do and why it is important to keep all variable within a private scope. Snippets like this are common when working with jQuery:

(function($){
    var myPrivateVariable = 'abc';
    // $ is private here

})(jQuery);

It allows you to code freely inside of the above IIFE (Immediately-Invoked Function Expression) knowing that anything properly declared (with a `var`) inside will not leak out to the global space as this possibly interfere with other logic running on the page.

What about Style Sheets?
Scoping stylesheets is easy if you are the author of that stylesheet and you know exactly what location of your user interface (UI) that style applies to. Stylesheet scoping means defining your selectors to a specific location on the page. That location is simply defined as an additional selector that is prepended to every other selector in the Stylesheet.
Example: let’s say I’m defining a Stylesheet like this:

h1 {
    font-size: 30px;
    color: red;
} 

This would affect all Heading 1 elements on the page. But I need it only to impact the h1 I define inside a `DIV` that has an ID of my_widget. To do that, simply prepend that selector to the rule sets:

#my_widget h1 {
    font-size: 30px;
    color: red;
} 

Ok. What about 3rd Part Libraries, Like jQuery UI and Bootstrap?

What if you’re trying to use a 3rd party UI stylesheet framework and are trying to have those styles only apply to elements inside a specific selector. I came across that need recently when wanting to use a jQuery UI theme inspired by Bootstrap. The app I was creating was going to be inserted into another application (Mingle) and was going to have to play nice with that application’s existing look. I certainly did not want it to interfere with that application’s User Experience (UI). The only answer is to scope all of the styles from the above theme to the container that holds my app.

HTML5 to the Rescue (almost)

HTML5 does have a feature called ‘scoped’ which allows you to use definitions inside HTML elements and have its rules apply only to the children of that element. But unless you are currently (as of this writing) building your code to run on Firefox only, it’s just not an option. There is a jQuery plugin that attempts to pollyfill this feature. But in reading the information on the site, it sounds like it may not be reliable when using the @import directive, which I may need in some occasions.

Grunt Task to the Rescue
Doing this manually is just not efficient so what I needed was a build time grunt task that would parse the stylesheet and insert a prefix to every selector. After trying a few plugins and getting near the point of ‘I’ll just create one myself‘, I found grunt-css-selectors (by Eric Ferraiuolo @ericf) which does the job.

The setup is just like any Grunt plugin.

– install the task

npm install grunt-css-selectors --save -dev

– configure the task (Gruntfile.js)


grunt.initConfig({
    css_selectors: {

        options: {
            mutations: [
                {prefix: ".my-app"}
            ]
        },

        build: {
            files: {
                "build/css/jquery-ui.custom.scoped.css": [
                    'src/css/jquery-ui/jquery-ui.custom.css'
                ]
            }
        }

    }
});

grunt.loadNpmTasks('grunt-css-selectors');

– Run grunt

The configuration above would take the stylesheet located in `src/css/jquery-ui/jquery-ui.custom.css`, prepend the value `.my-app` to every selector and save the new CSS file as `build/css/jquery-ui.custom.scoped.css`. The style rules selectors will look like the following in this new file:

.my-app .ui-button:active,
.my-app .ui-button.ui-state-active {
    background-image: none;
}

Now when, I include the new CSS file on the targeted (Mingle) page, I’m assured that it’s style rules will apply only to the content inside of elements having the `my-app` class and thus not interfering with the remainder of the UI.