Grow your CSS skills. Land your dream job.

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:

Comments

  1. Great work Shane!

  2. Adam
    Permalink to comment#

    Thank you! Thats insanely helpful!

  3. JoeShmoe
    Permalink to comment#

    Whoa!! That’s a very cool tool!! I’m definitely gonna start using this!!!

  4. Ted
    Permalink to comment#

    This is a feature of the latest Visual Studio that I have definitely fallen in love with! Having the page reload my styles every time I save is such a small thing that makes my day so much better :)

  5. This is incredible cheers.

  6. As a front end dev I’ve known about Grunt.js before and after reading this article I’m now at their website reading how to make it work.

    Truth be told, as much as I want to make this work and try it out, I can’t help to feel the pain of doing so just like I felt when I tried to set up Sass, and FYI I am not intimidated by the CMD at all. Some things are just not front end devs-friendly.

    With this being said, here’s a totally dumb and n00b question(s) in the name of PC front end devs: Are these instructions for Mac only? Or do they apply for PC as well? Or are these questions too dumb? (Don’t answer this last one :p, your comment(s) will make it clear :]). Tnx!

    • I feel your pain re: setting up grunt.

      It’s one of those things that is frustrating at first, but if you stick with it and get these tools working – you won’t look back I promise.

      I work at WeAreJH in my day-job and we recently put aside some time for a grunt workshop that really helped our front-end devs/designers understand some of the the common mistakes/problems you can encounter. Now they are completely sold on it.

      Have a look at this config here & see if you can get it working.
      Gruntfile.js example

      To answer your question about PC/Mac: using Browser-sync is identical across Windows & Mac. I’ve tested (and even changed some code) to ensure that you get the same tool on either platform. (I do confess to not having tested on any linux-based machine, however).

    • David Aimi
      Permalink to comment#

      Hey Ricardo,

      Get yourself setup with a boilerplate of some sort… it will help in getting setup with a grunt.js / node and maybe give you a few of those “aha!” moments.

      Checkout something like ngBoilerplate. It has a project in a box that will prob help you out a bit with node and grunt. (However its in AngularJS, so you may need to find a substitute if you’re not comfortable with AngularJS).

      No its not MAC only. SASS has compass which is on Mac, hence why you hear that frequently.

    • It wasn’t completely clear to me that Browser Sync could be run without Grunt, until I read your comment below: “…(and without grunt too)”. I ran it via the Command Prompt and it worked*.

      Not sure if other front end devs that have no idea about using Grunt are getting discouraged by thinking that this can only be used with Grunt because although you mention that it can run as a “…stand-alone command-line tool”, this part doesn’t seem to be clear enough.

      Aaaanyway, *I was able to make it watch a test /css folder and a test index.html file, however, I’m unable to make my browser “see” the HTML file. So I have some questions.

      Where can I submit questions about my case, in the Issues tracker of the repo in Github? Here? Where else?

      Thanks in advance.

    • Unreal, I was able to make it work!

      Thing is that what I did is not described here nor in the README.md instructions on the repo.

      I’ll try to think of something to put that information out there for others.

      After unsuccessfully trying so many other ‘live injection’ tooIs out there, I finally have a way to see my changes happen live without having to refresh and without the need to use some complex framework or library.

      A million Thanks Mr. Shane.

  7. Areeg
    Permalink to comment#

    Thanks so much :)

  8. This tool is really amazing. I’ve used it a number of times and can say that I love it.

    Are there any plans for making this tool into something similar like GhostLab where there’s an interface that’ll plug all this in without the Grunt config step? Not that I’m complaining – I find Grunt easy to use and don’t mind the setup but I’m thinking about those who don’t feel as comfortable.

    Anyways – keep up the epic work here Shane I love the tool and can’t wait to see where it goes :)

    • Yeah I have plans for small interface stuff (like notifications, the ability to disconnect individual browsers, or to enable/disable ghost-mode without having to resort back to the command line etc).

      I also want to integrate debugging tools like weinre that will enable remote debugging of mobile devices through your main browser. (so image running browser-sync with all the features it already has + you’ll get a webpage that lists all of the connected browsers – you could choose one & start debugging it with chrome dev-tools!).

      The infrastructure is in place to build some really awesome features like that.

      Glad you like it, & thanks for helping drive it’s development in the early days at @wearejh :p

  9. Donatas
    Permalink to comment#

    Great tool no doubt. However after I have fallen in love with livejs.com it’s hard to resist such an easy setup. Its just a single js file that you include in your pages and it automaticaly inserts updated css files, js files and even refreshes page after html changes (configurable). It’s so simple to use. But it doesn’t have all the great features of this setup. But as long as I’m a coding designer it’s just enough.

    • This is just another weapon in our arsenal. No need to apologise because you like something else already! :)

      I love any/all tools that help automate our workflow.

      Probably worth mentioning though, if you are working with html, css & js, then browser-sync can be used with a single command (and without grunt too). Check the docs

  10. Permalink to comment#

    Wow, great stuff.

    Could you explain how to set this up with Mamp?

    Thanks,

    Flo

    • This tool can be used with Mamp no problem. Just run it as described in the docs & you’ll get a HTML snippet that you can paste into the footer of your website.

      To get the full benefit though, you need to be connecting to your Mamp server through an IP address (you can configure this through Mamps server settings).

      If you need further help finding the correct IP to use, it just so happens that I’ve built a little tool to help you :p

      dev-ip – Find a suitable IP host to view local websites on

  11. Permalink to comment#

    Thanks so much! I have been waiting for a great LiveReload alternative for the longest time, oh, oh, oh, for the longest time… I’m so happy, I’m singing!

  12. Uncle
    Permalink to comment#

    Looks good, but I think livereload also handles html & javascript changes.

    • Browser-sync can handle as many files as you want & will reload/inject where possible.

      Example Grunt Config:

          browser_sync: {
              files: {
                  src : [
                  'assets/css/*.css',
                  'assets/img/**/*.jpg',
                  'assets/img/**/*.png',
                  'assets/js/**/*.js',
                  '**/*.php',
                  '**/*.html',
              ],
              options: {
                  server: {
                      baseDir: "./"
                  },
                  ghostMode: {
                      scroll: true,
                      links: true,
                      forms: true
                  }
              }
          }
      

      Or if you are using it stand-alone, you can pass a config file as explained in the docs

  13. Ebenezer Scrooge
    Permalink to comment#

    Uhm, why didn’t you just submit a PR for livereload to have a fallback when it doesn’t support websockets?

  14. Never had a really good reason to use grunt but this is amazing, not to mention extremely useful. I am also tired of the same old refresh across multiple browsers. Definitely an install for me. Nice work though.

  15. Permalink to comment#

    Thanks for great tool!

    Is there a solution for the opposite ? Saving dev. tools changes into dev. server files ?

  16. Permalink to comment#

    It was easy to setup with the command line. It says watching 1 file (my style.css)…But. I keep geting in the browser:
    Cannot GET /add-member.php

    can you shed any light as to why this is occuring? my address bar has: http://192.168.1.65:3001/add-member.php

    my config file is:
    module.exports = {
    files: “*.css”,
    debugInfo: true,
    host: “192.168.1.65″,
    ghostMode: {
    links: true,
    forms: true,
    scroll: true
    },
    server: {
    baseDir: “/mvyc/”
    },
    open: true
    };

    thanks

  17. tom
    Permalink to comment#

    @Vahagn
    There is livestyle which is new http://livestyle.emmet.io/ and there was DevTools Autosave not sure how active that is and it only worked in Chrome https://github.com/NV/chrome-devtools-autosave

  18. Sam
    Permalink to comment#

    This is great, love it! One thing I noticed is that it doesn’t work when using this “trick” for getting around IE’s limitations on # of stylesheets while in development:

    <style>
      @import url("resource1.css");
      @import url("resource2.css");
      ...
    </style>
    
  19. Michael
    Permalink to comment#

    Hey Shane,

    I. Love. Browser-Sync!

    Thanks a lot for developing this and just to inform you:

    It works perfectly on both my Linux setups (Linux Mint 15 & Ubuntu 13.04)

    • That’s good to know as I haven’t had chance to test on any Linux distros (apart from the CI server builds)

      Thanks, glad you like it :)

  20. Rohit
    Permalink to comment#

    Hello Shane,

    Can we use it in custom javaEE projects (non-node.js project)? If yes, any extra config. needed?

    Thanks

  21. steelpulsefan@gmail.com
    Permalink to comment#

    Shane thank you very much! This is freaking awesome and will same me a lot of time. It refreshes the browser instantly after I save my editor file.

    Blessings

    Norman

  22. Frayde
    Permalink to comment#

    Hi !

    i’m new too grunt so i’ve set up a simple grunt in a new folder. I install Browser-sync as described here, with minimal settings :
    browser_sync: {
    files: {
    src : ‘css/style.css’
    }
    }.
    No server or nothing is installed, only grunt and browser-sync.

    When i hit grunt browser_sync, all i got is “Browser Sync could not find any files to watch”.

    Did i missed something, is there any issue Windows related ?

    ps : the command-line version finds 4 .css files on my path, plugins just doesn’t want to find my css files :).

  23. How can I use this plugin at the same time as the watch plugin? As I’m using LESS and the watch plugin to process it, this plugin is kind of useless if I can’t use both. Running grunt browser_sync runs the plugin, but just running grunt runs watch only.

    Grunt noob here, sorry!

    • I’m not sure if this is the best way, but I’m now using grunt-shell and grunt watch & grunt browser-sync to run both of them at the same time.

      Thanks for the awesome plugin, this is great!

  24. Nimesh Gurung
    Permalink to comment#

    Managed to get it working with chrome devtoools. I used chrome workspace [which is available in devtools] to map my css file to the actual source file and did a setup on browser-sync to watch over the css file and this automatically injects the css to all the connected devices, reflecting any changes I make in devtools to all the connected devices. Sweet!!

    However I did have to put a change in ‘browser-sync-client.js’ to make it work, because when browser-sync injects the css file it timestamps the newly injected css file. So I changed the following line elem[attr] = currentValue + “?rel=” + timeStamp; to elem[attr] = currentValue; in ‘browser-sync-client.js’. Without the change the css injection will only work for the first time using chrome devtools.

  25. Permalink to comment#

    Hi Chris,
    Thanks great article on the grunt… which made my day on workflow.

    Now i am getting error on browser_sync.

    Warning: Cannot find module ‘optimist’ Use –force to continue.

    Regards,
    Jesudas

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".