Cross Browser CSS Injection

Published by Guest Author

The following is a guest post by Shane Osbourne (@shaneOsbourne) from England. Besides being a musician, Shane is also an open source developer. Shane has developed a tool called Browser-sync for making CSS injection work across a wider swath of browsers than you might assume possible. And it does more than that, I'll let Shane tell you about it.

The ability to inject newly-modified CSS on every file change (without reloading the page) is the type of workflow-enhancement that you never truly appreciate... until you lose it.

This is exactly what happened to me recently.

For at least the last 12 months I had been using Grunt and the LiveReload browser extension to inject CSS when my files changed. It felt like a really nice workflow until I had to work on a project that required IE7 & 8 support. The LiveReload plugin I was using simply didn't support older versions of IE. For the first time in over a year I had to resort to manual page-refreshes every time I made a change to my CSS. It was painful. It's also why I built Browser-Sync

Introducing Browser-Sync

Browser-sync is a tool that gives you cross-browser CSS injecting. It will watch over your files and the moment you make a change, it will inform all connected browsers to reload (inject) the new CSS. This all happens without reloading the page. Most importantly, it works in every browser I've been able to test it on.

How Browser-Sync came to be

I knew from the outset that simply inserting new CSS into browsers is easy enough to do. The issue I had to solve was how to communicate a file-system change back to the browsers. The Grunt + LiveReload combination I was using previously relied on Web Sockets. This is why there was no support for old IE.

Say hello to Socket.io

I had my lightbulb moment when using Karma for running unit-tests. Karma allows you to run tests in multiple browsers. While testing in IE8 one day, I realized that Karma was automatically re-running the tests on each file save.

Bingo. I had found something that was able to watch the file system for changes and inform browsers all about it. I started to dig into the Karma source code. Eventually I discovered they were using Socket.io to achieve this.

Starting to come together

Everything was in place. With the pain endured from manual page refreshing as my driving force and the discovery of Socket.io as my inspiration, I was well set-up to build a tool that I would use myself on a daily basis.

Getting set up

This guide assumes you already have Grunt installed inside a project and know how to use it. If you don't, then you can learn about it on the Grunt site.

Browser-sync can be installed as a stand-alone command-line tool, or as a Grunt plugin. Because of the current popularity of Grunt I'll focus here on how to set it up as as Grunt Task. If you would prefer the command-line only approach, please checkout these instructions.

From the same file directory as your Gruntfile.js:

npm install grunt-browser-sync

This will install Browser-sync locally into your project. Now you can make sure it's loaded by Grunt by placing this near the end of your Gruntfile.js:

grunt.loadNpmTasks('grunt-browser-sync');

Finally, like all Grunt plugins, you'll need to provide some configuration. At a minimum, you'll need the following:

// Inside the Gruntfile.js config object
browser_sync: {
    files: {
        src : 'assets/css/style.css'
    }
}
Reader Jan Skovgaard writes in:

I got a bit confused when I tried to follow along the steps in the article. It simply did not work - So I went to the official website and read. Seems the steps have changed a bit and instead of naming the task "browser_sync" it should say "browserSync" otherwise grunt won't run the task.

With your config set up, you can run the plugin with this command:

grunt browser_sync

Now you should receive an HTML snippet that you can paste into the footer of your website. Place that there. Now whenever you change that CSS file (or it's changed by another build process), it will be automatically injected into all browsers.

Note: there are ways to specify multiple files and you can use wildcards (*) too. Please see the Readme on Github for more information.

A more automated approach

The example above is great for those situations where you already have a server. Building a site with WordPress or Rails, for example. But the downside is that you have that manual step of copying & pasting a snippet into a file. If you're working with static files (HTML, CSS, & JS) then you can use the built-in server instead. The built-in server will automatically insert any HTML snippets needed so you don't have to.

To use the built-in server, just set your config like this.

// Inside the Gruntfile.js config object
browser_sync: {
    files: {
        src : 'app/css/style.css'
    }, 
    options: {
      server: {
        baseDir: "app"
    }
  }
}

Files will now be served from the app directory and all the magic needed to inject CSS will be done for you.

Going even further

If you use the server option above, did you notice the IP address and port that is used? Normally something like 192.168.0.8:3001? Browser-sync tries to figure out a good external IP address to use for the server. That means you should be able to connect to it from any device/computer on the same WiFi network. The cool thing here is that the CSS injecting will work across all mobile devices and tablets too. When you see this type of automation in action, you're going to find it hard to live without it.

If it doesn't work for you

In my tests, I've found that on typical WiFi networks, Browser-sync does a fantastic job of getting the correct IP address. However, in a large office setup, you may find that it chooses an IP that is not accessible to other devices/computers. If this is the case, and you know what the correct one is, just provide it as an option like this:

// Inside the Gruntfile.js config object
browser_sync: {
    files: {
        src : 'app/css/style.css'
    }, 
    options: {
      server: {
        host: "192.168.1.1",
        baseDir: "app"
    }
  }
}

Beyond CSS injecting

Being a guest post on CSS-Tricks, I've focused mostly on setting up the CSS-injecting part of my tool. But Browser-sync does a lot more than that! It has something awesome called Ghost-mode.

Ghost mode will keep track of all connected browsers and keep the following in sync:

  1. Scroll Position - extremely helpful when developing with multi-monitor
  2. Form Fields - fill out a form in one browser and all others will be populated with that data.
  3. Links - Reviewing a few pages of a newly built site? Have it open on all your devices and when you navigate from page to page, all the browsers with follow you around!

Conclusion

I hope this all sounds pretty cool to you. I loved building Browser-sync. I welcome feedback and issue reporting so that we can improve it as a community.

Remember Browser-sync can be used two ways: