Multiple Sites with Varying Designs in a Single CSS File

Multiple Sites with Varying Designs in a Single CSS File

Hi, everyone! It’s your friendly neighborhood SharePoint hacker Dustin here with a slick trick you have to try out to believe!

Recently, Heather wrote an article (Master Pages, who needs them anyways?) that talked about creating your custom designs in SharePoint using only a single Master Page. From the very first delivery of our SharePoint UI/UX Class, our students have excitedly jumped on board – it’s amazing just how much you can do with a single master page!

One student in particular spoke up: “Okay, great, one master page to rule them all. What about one CSS file? Any fancy tricks that will let me keep all my design work for all the sites and site collections in my entire farm in a single CSS file? Even those department sites that want different colors?”

It didn’t seem like too much to ask.

Please note this article is cross posted on nothingbutsharepoint.com.

CSS that varies based on site URL

You’re gonna love this, designers. First, just imagine what you could do if you could write CSS like this:

/* Every site gets dark blue "ribbonrow" header */

body #s4-ribbonrow {
    background-color:#21374c;
}

/* 
   Except marketing. They're fancy. They get a gradient.
   All their subsites get the same gradient.

   Their site collection root is /sites/marketing
*/

body.sitecol-sites-marketing #s4-ribbonrow {
    background: #ebf1f6; /* Brain dead browsers */
    background: linear-gradient(to bottom, #ebf1f6 0%, #abd3ee 50%, #89c3eb 51%, #d5ebfb 100%); /* Smart browsers */
}

/* 
   But then there's the "SkunkWorks" group
   in the Marketing department. They want the ribbon row
   removed outright.

   Their site URL is /sites/marketing/skunkworks
*/

body.site-sites-marketing-skunkworks #s4-ribbonrow {
    display: none;
}

How cool would that be?

JavaScript to the rescue

No, not jQuery. JavaScript. Good old fashioned JavaScript. Using your favorite web developer extension (Firebug for FireFox, the built-in inspector for Safari or Chrome), open up a SharePoint site and try this in the JavaScript console:

console.log(_spPageContextInfo);

You might have to expand the “Object” that appears in the console. Here’s what mine looks like:

alertsEnabled: true
allowSilverlightPrompt: "True"
currentLanguage: 1033
pageItemId: 1
pageListId: "{5962d2fd-47b4-4be9-973c-3e0183489eb8}"
siteServerRelativeUrl: "/sites/marketing"
userId: 14
webLanguage: 1033
webServerRelativeUrl: "/sites/marketing/skunkworks"
webUIVersion: 4

Nice. We’ve got the site collection root in a property called “_spPageContextInfo.siteServerRelativeUrl” and the current site’s base URL in a property called “_spPageContextInfo.webServerRelativeUrl”.

It’s almost like you could copy and paste those values in the “class” attribute of the body tag – but of course you can’t. You can’t have a slash (/) character in a CSS selector. In that same console, try this:

var siteColClass = 
        'sitecol' + 
        _spPageContextInfo
            .siteServerRelativeUrl
                .replace(///g, "-")
                .toLowerCase()
;
var siteClass = 
        'site' + 
        _spPageContextInfo
            .webServerRelativeUrl
                .replace(///g, "-")
                .toLowerCase()
;

console.log('The class for site collection is: ' 
    + siteColClass);
console.log('The class for site is: ' 
    + siteClass);

I added a bunch of white space to make that more readable. Lines 5 and 12 in that last sample handle the simple bit of string magic that makes this solution so elegant. Just replace the forward slash (/) with a hyphen (-) and you’ve got yourself a valid CSS selector.

Okay, now to get these values attached to the body element as CSS classes. Try this in your console:

document.body.className = 
        document.body.className 
            + ' ' 
            + siteColClass
            + ' '
            + siteClass;

Your console should echo back the new classes attached to the body element. For me, it looks like this:

"v4master sitecol-sites-marketing site-sites-marketing-skunkworks"

Finally, the script that can go into your master page (I’d place this right before the closingtag):

<script language="javascript">
    _spBodyOnLoadFunctionNames.push("speBodyClasses");
    function speBodyClasses(){
        var siteColClass = 
                'sitecol' + 
                _spPageContextInfo
                    .siteServerRelativeUrl
                        .replace(///g, "-")
                        .toLowerCase()
        ;
        var siteClass = 
                'site' + 
                _spPageContextInfo
                    .webServerRelativeUrl
                        .replace(///g, "-")
                        .toLowerCase()
        ;
        document.body.className = document.body.className + ' ' + siteColClass + ' ' + siteClass;
    }
</script>

With that, you can now add “conditional CSS” in your single CSS file that can re-style anything on your site based on the path of the site collection root, or the path of the current site.

Coming in a future article: Extending this script to add classes based on the current user, the current page layout and other cool hacks.

What do you think? Will this change how you write CSS for your designs? What other additions would you like to see added to this script? I have a few lined up, but let’s see what YOU want to see!

This was originally posted on NothingButBranding.com in September 2012, which is a site that has since been closed.