Bootswatch offers many beautiful free themes for bootstrap. Recently, I created a Google Chrome Extension that makes it easy to preview any existing bootstrap sites in one of these Bootswatch themes.
How To Use
Install the extension from Chrome Web Store. Once the extension is installed, you can click on the icon to bring up the following popup. From the popup, select a theme and hit the Apply Theme button to apply theme on current page.
How It Works
The extension has the following components:
- A popup page and script for user interaction. It presents available themes and lets user select desired theme.
- A content script to apply a theme on current web page. This script is injected to the web page. It runs in a special context that has access to the page’s DOM. More about content script later.
- A background script for communication with the content script and organizing common functions and variables.
Content Script
Content script runs in a special environment of isolated world. It has access to the DOM of the page, but not any JavaScript functions or variables created by the page. It also does not have access to the functions and variables defined by the extensions’ pages. To communicate with the parent extension, we need to use message passing.
Message Passing
Since content scripts runs in isolated world and not part of the parent extension, it can only use message passing to communicate with the extension. For simple one time requests like the ones we have in this extension, we can use runtime.sendMessage
or tabs.sendMessage
to send messages, and runtime.onMessage
to receive messages. For long-lived connections, chrome extension API offers other methods you can learn more about here.
The Code
This is the extension’s manifest.json
. Only the activeTab permission is specified as we will only inject content script to web page in the active tab.
{
"manifest_version": 2,
"name": "Bootswatch Theme Preview",
"short_name": "BootswatchThemePreview",
"description": "Preview a bootstrap site with a bootswatch theme.",
"version": "1.0.1",
"background": {
"scripts": ["js/background.js"]
},
"icons" : {
"16" : "img/icon16.png",
"48" : "img/icon48.png",
"128": "img/icon128.png"
},
"browser_action": {
"default_title": "Preview this page with a bootswatch theme.",
"default_popup": "popup.html"
},
"permissions" : [
"activeTab"
]
}
This is the popup.js. When user clicks on the icon, the popup.html will be loaded and execute this script. This script serves two purposes. First, it creates the UI elements on the popup page. Second, it responds to the apply theme button’s click event and injects our content script to the current web page. When the button is clicked, it calls chrome.tabs.query
to get the active tab. In the callback function (most of chrome extension API are asynchronous), it saves the selected theme to background script and calls chrome.tabs.executeScript
to inject the content.js script to the web page.
document.addEventListener('DOMContentLoaded', function () {
createBootswatchSelect();
createBootswatchThumbnails();
document.getElementById("apply-button").addEventListener("click", function(){
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
var bootswatchSelectList = document.getElementById("bootswatch-theme-select");
var bootswatchThemeName = bootswatchSelectList.options[bootswatchSelectList.selectedIndex].value;
chrome.extension.getBackgroundPage().saveUserPreference(bootswatchThemeName);
chrome.tabs.executeScript({
file: 'js/content.js'
});
});
});
});
function createBootswatchSelect() {
var themes = chrome.extension.getBackgroundPage().bootswatchThemes.themes;
var userThemeName = chrome.extension.getBackgroundPage().getUserBootswatchThemeName();
var container = document.getElementById("bootswatch-theme-container");
var selectList = document.createElement("select");
selectList.id = "bootswatch-theme-select";
selectList.className = "form-control";
container.appendChild(selectList);
for (var i = 0; i < themes.length; i++) {
var theme = themes[i];
var option = document.createElement("option");
option.value = theme.name;
option.text = theme.name + " | " + theme.description;
if (theme.name == userThemeName) {
option.selected = true;
}
selectList.appendChild(option);
}
}
function createBootswatchThumbnails() {
var themes = chrome.extension.getBackgroundPage().bootswatchThemes.themes;
var container = document.getElementById("bootswatch-thumbnail-container");
for (var i = 0; i < themes.length; i++) {
(function() {
var theme, div, a, img;
theme = themes[i];
div = document.createElement("div");
div.className = "col-xs-3";
a = document.createElement("a");
a.className="thumbnail";
a.href = "#";
img = document.createElement("img");
img.className = "img-responsive";
img.src = "img/" + theme.name + ".png";
container.appendChild(div);
div.appendChild(a);
a.appendChild(img);
a.addEventListener("click", function(){
console.log(theme.name);
document.querySelector('#bootswatch-theme-select [value="' + theme.name + '"]').selected = true;
});
}());
}
}
This is the content.js. It uses chrome.runtime.sendMessage
api to communicate with the background script. When it receives a reply in the callback function, it gets the selected bootswatch theme’s css cdn
, and appends a stylesheet
element on the current web page.
chrome.runtime.sendMessage({method: "getUserBootswatchTheme"}, function(response) {
var link = document.getElementById("injected-bootswatch-theme");
if (link) {
link.href = response.theme.cssCdn;
}
else {
var link = document.createElement( "link" );
link.href = response.theme.cssCdn;
link.type = "text/css";
link.rel = "stylesheet";
link.id = "injected-bootswatch -theme";
document.body.appendChild(link);
}
});
This is the background script. It listens to messages from the content script and responds with the saved theme.
...
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getUserBootswatchTheme") {
sendResponse({ theme: getUserBootswatchTheme() });
}
});
function getUserBootswatchTheme() {
var themeName = getUserBootswatchThemeName();
for (var i = 0; i < bootswatchThemes.themes.length; i++) {
if (bootswatchThemes.themes[i].name == themeName) {
return bootswatchThemes.themes[i];
}
}
console.log("no user bootswatch theme is found");
}
function getUserBootswatchThemeName() {
var name = localStorage["UserBootstrapThemeName"];
if (name == undefined) {
name = "Readable";
}
return name;
}
function saveUserPreference(bootswatchThemeName) {
localStorage["UserBootstrapThemeName"] = bootswatchThemeName;
}
Conclusion
That’s it. This is a relatively simple and yet useful extension that demonstrates how to use a content script to make changes to an existing page. Now you can preview your bootstrap sites in one of the bootswatch themes without making any changes. When you decide you like the theme, all you have to do is to download the CSS file from bootswatch.com and replace the one in your project to apply it permanently.
References