SharePoint Branding – How CSS works with master pages – Part 2
Jeez if I had realised how long it would take to write these damn articles, I probably wouldn’t have started! In my first article of this topic, I discussed the theory behind master pages, the publishing feature, and what I think is the main issue with SharePoint branding – APPLICATION.MASTER and CORE.CSS. In this article I will now list a branding scenario that I had to deal with, and the various options you can use to deal with the challenges of APPLICATION.MASTER and CORE.CSS
The Scenario
Like many organizations, my client had an existing corporate branding standard that was used in a non SharePoint environment and naturally enough, they wanted their SharePoint site to look like this branding.
This was for a fully featured intranet/extranet that utilized most of the MOSS2007 features such as
- Document collaboration
- Infopath Forms Services
- Workflow
- Enterprise Search
- Excel services
- Business Data Catalog
- Custom web parts
- Event Handlers
It was *not* a public site at all.
Initial investigation soon concluded that we would need a custom master page. DEFAULT.MASTER didn’t quite have the design flexibility that was required. In fact the branding requirements were actually closer to some of the built in master pages such as BLUEGLASS.MASTER, since this was for intranet purposes, particularly collaborative document management, those master pages are unsuitable. (I will explain why soon).
Other major requirements included mandatory consistency of branding across the entire farm. This meant that we had to deal with the APPLICATION.MASTER issue as described in my last post. Additionally, different semi-autonomous divisions within the client’s organisation wanted to be able to modify the corporate brand in accordance with existing branding policy (mainly colours/skinning) while maintaining the existing structural look and feel. Additionally, as well as the corporate branding, there was another, very different branding which was used as part of a communications plan for some organizational change stuff. This had a deliberately different look to the rest of the site, yet the web designer had been clever when this design was created. They were able to completely rebrand the look and feel of the additional branding just through additional CSS and not wholesale HTML structural changes. So, really, we had two general classes of sites. ‘Standard’ corporate sites and the custom branded ‘Organisational Change’ site.
|
The type of MOSS environment this was to be applied to was a medium to large SharePoint farm with three web front end (WFE) servers, and there were several SharePoint web applications and site collections in the farm that needed this branding. Further down the track, there was to be other SharePoint farms in different offices around the world with localized language requirements.
The final requirement (the one that complicated things) was that many of the CORE.CSS styles needed to be overridden to ensure the branding was correct.
The Custom Master Page
The first decision to make is whether to stick to just CSS modifications to an existing master page, or to create a new master page. This post is based around a new master page, however the CSS methods I outline in this post are equally valid when using, say, DEFAULT.MASTER or one of the publishing master pages like BLUEBAND.MASTER.
So since we knew we had to use a custom master page, we had 3 choices in creating a new master page.
- Make a copy of DEFAULT.MASTER and use that
- Start with a completely blank master page
- Make a copy of one of the publishing master pages like BLACKBAND.MASTER
I’ll actually deal with the third option first because we initially used it and then discounted it.
As previously mentioned, in our case BLUEGLASS.MASTER was the closest of the built in master pages to our branding needs but in the end we did not use it. Publishing master pages are actually somewhat simpler than the default.master page. Some web controls/placeholders have been modified/removed from publishing master pages, and if you use them for say, a document collaboration site, you will find quirks like:
- Missing breadcrumb navigation in document libraries when browsing subfolders
- Missing recycle bin link
- Different navigational structure
- Tree-view not being rendered despite being selected
- Themes are ignored (I discuss this in detail in part 3)
Furthermore, I actually read not so long ago that it was recommended that these pages be generally used for internet facing sites which by definition tend to be publishing sites. (I’m trying to find the reference and as soon as I locate it I’ll paste here), but Tyler’s post offers some hints where he talks of custom CSS and navigation out of the box.
A blank master page is potentially a great option, either the MSDN one that everyone seems to hate or Heather’s, that the general population seems to like. Ultimately flexibility, of course, as it means that you can code up the look and feel from scratch. Unless you are a world class web designer and have a great understanding of ASP.NET, it may not be worth the time and budget (especially for an internal facing site).
So, we chose to simply make a copy of default.master because we actually did not have to change that much to achieve our ends. Yes it is complex and fiddly, but it is the out of the box master page that works with SharePoint, so using it as a base is a pretty safe bet. The main issue you need to watch out for when doing this, is related to the publishing feature.
So, for the purposes of this article, our new master page will be referred to as PIMPMYSHAREPOINT.MASTER – and we have to get our custom CSS styles into this.
Over-riding CSS – SharePoint:CSSRegistration
We took a look at SharePoint:CssRegistration and its brother SharePoint:CssLink in part 1, but I think it’s worth a recap.
SharePoint:CssRegistration defines a CSS file and Sharepoint:CssLink is the control that actually renders the SharePoint:CssRegistration styles as well as CORE.CSS.
<head runat="server">
[snip]
<Sharepoint:CssLink runat="server" /
<!–Styles used for positioning, font and spacing definitions–>
<SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/pimpmysharepoint.css%>" runat="server"/>
[snip]
</head>
And the result is…
<head>
[snip]
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/pimpmysharepoint.css" mce_href="/sites/nopub/Style%20Library/pimpmysharepoint.css"/>
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/core.css" mce_href="/_layouts/1033/styles/core.css"/>
[snip]
</head>
As discussed in part 1, CORE.CSS, has been rendered, despite the fact that it is not explicitly defined. What’s more, CORE.CSS is always listed last.
Ah! But remember our requirement above? Our custom styles need to override styles in CORE.CSS! GONG! This isn’t going to do it for us.
Cleverworkaround Rating: Useless for me
Over-riding CSS – Master Page Settings
So we look at the next most obvious way to defeat this CORE.CSS issue. On a MOSS07 site with the "Office SharePoint Server Publishing Infrastructure" site collection feature enabled, we have the means to add a custom CSS to a master page via the GUI. It can be found in Site Settings on any site. Navigate to look and feel and choose "Master Page".
Scroll to the bottom of this page and you will see the CSS override settings.
From here, you can choose a custom CSS file to use. In this example we have chosen pimpmysharepoint.css which was uploaded to the style library for this site collection. Let’s examine the HTML output once pimpmysharepoint.css has been selected.
<link rel="stylesheet" type="text/css" href="/_styles/core.css" mce_href="/_styles/core.css"/>
<link rel="stylesheet" type="text/css" href="/Style%20Library/PimpMySharePoint.css" mce_href="/Style%20Library/PimpMySharePoint.css"/>
Aha! We have overridden CORE.CSS – nice! Given that we have not modified the master page itself, we can conclude that SharePoint:CssLink will render the "alternate CSS URL" last. In addition, we have solved the APPLICATION.MASTER problem. Hands up who can tell me why ? 🙂 The reason is that APPLICATION.MASTER uses SharePoint:CssLink as well, so it will render this CSS in _layout pages derived from that master page.
Furthermore, we did it with no coding or custom work, and it can be inherited or forced out to all subsites from this one. WOHOO!
So? Have we found our clever workaround? No we have NOT. DOH! We have a workaround, but I do not believe that it’s clever yet!
Remember my requirements. We had a corporate brand that required CORE.CSS override for a few styles. In addition, we have the ‘organizational change’ branding CSS that overrides the corporate branding CSS. In addition, even within corporate branding we had CSS overrides for individual departments too.
So what is our problem then?
Using this method, we can only override once! Once you set this alternate CSS, that’s it. Thus, if I was to use this method, I would have to maintain several large custom CSS files, one for each branding scenario. Given that 80% of the actual styles are consistent between them, I prefer a method where the common stuff is kept common, rather than risk being fragmented over time with changes to one of the CSS files not being propagated to others.
I’m sure many of you would be okay with this, and for certain clients, I would be too. However, call me anal if you want but I wanted to find a better way.
CleverWorkaround Rating: Close, but no cigar!
Over-Riding CSS – Static CSS Link
Now, given that SharePoint:CssLink control renders stylesheet links at runtime, what is stopping us simply adding a static CSS link in the master page? The source code below illustrates the idea.
<head runat="server">
[snip]
<Sharepoint:CssLink runat="server" /
<!–Styles used for positioning, font and spacing definitions–>
<SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/pimpmysharepoint.css%>" runat="server"/>
<link rel="stylesheet" type="text/css" href="/Style Library/anothercss.css"/>
[snip]
</head>
This works on the premise that 80% of your branding (the stuff that CORE.CSS doesn’t screw with) is in PIMPMYSHAREPOINT.CSS. The other 20% that needs to override CORE.CSS is in ANOTHERCSS.CSS.
So, now I have embedded a static link into the master page. Before we go on to assess its cleverness, can you see my mistake?
Imagine, that you have 3 site collections named:
- http://mysite
- http://mysite/sites/collection1
- http://mysite/sites/collection2
That static link, will always try and use the style library for the top site collection, even for the bottom two site collections. But what if the users do not have permission to the top site collection? Will the CSS render? Probably not as access to it would be denied.
Here is the adjusted source to fix this – using a token to render the correct site collection URL.
<head runat="server">
[snip]
<Sharepoint:CssLink runat="server" /
<!–Styles used for positioning, font and spacing definitions–>
<SharePoint:CssRegistration name="<% $SPUrl:~SiteCollection/Style Library/pimpmysharepoint.css%>" runat="server"/>
<link rel="stylesheet" type="text/css" href="<% $SPUrl:~SiteCollection/Style Library/anothercss.css%"/>
[snip]
</head>
>
See what I have done (in bold)? I have placed the rendered link into the master page source. I have used the $SPURL:~SiteCollection token though which at runtime gets replaced with the path to the site collection. (In the example below the site collection was called "/sites/nopub").
So let’s examine the HTML output of this.
<head>
[snip]
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/pimpmysharepoint.css" mce_href="/sites/nopub/Style%20Library/pimpmysharepoint.css"/>
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/core.css" mce_href="/_layouts/1033/styles/core.css"/>
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/anothercss.css" mce_href="/sites/nopub/Style%20Library/anothercss.css"/>
[snip]
</head>
Wohoo! We have our custom styles after CORE.CSS. What’s more, we can add as many <link> entries as we like here. Although not shown above, I could have one <link> with the 80% of common styles and another <link> with the 20% of custom ones.
So have we found our branding utopia? No – hell no!
One of the disadvantages with this method is that if your <link> is placed AFTER SharePoint:CssLink, then it will also trump your "alternate CSS URL" setting described in the "master page setting" method. This is because the alternate CSS URL is rendered by SharePoint:CssLink. Placing your static link before SharePoint:CssLink is utterly pointless, because all of the CSS files specified by SharePoint:CssRegistration will trump the <link> entry. The whole point here was to override CORE.CSS.
If that wasn’t bad enough, the clincher is that _layout pages, based on APPLICATION.MASTER master page, will not have this static link anyway. Therefore, your branding is inconsistent for _layout pages and in fact is likely WORSE because it will apply the 20% customizations of ANOTHERCSS.CSS but the other 80% of PIMPMYSHAREPOINT.CSS common styles will be missing.
CleverWorkaround Rating: Pretty damn crappy actually !
So, what can we try next?
Over-riding CSS – Mike’s method
Now this method I was very, very impressed with (and cursed that I did not think of it myself). Mike came up with a novel way to solve the CORE.CSS problem. He created his own asp.net control that REPLACES SharePoint:CssLink. He has inherited the CSSLink class and modified the rendered output to move CORE.CSS from last to first. Ingenious!
So, to implement this, you need to compile it to a DLL in visual studio. You need to copy the DLL into the global assemble cache, and then mark the web control as safe. This is done by making a modification to the <safecontrols> section of the XML file, WEB.CONFIG of the web application that is going to render this control.
<SafeControls>
[snip]
<SafeControl Assembly=" MichaelHofer.SharePoint.PublishingEnhancements, Version=1.1.0.0, Culture=neutral, PublicKeyToken=7ce575c89ea427a4" Namespace=" MichaelHofer.SharePoint.PublishingEnhancements " Safe="True" />
</SafeControls>
Then, in the custom master page, you have to tell SharePoint how to find the DLL. It is called ‘declaring the assembly’. Here is Mike’s example:
<%@ Register Tagprefix="PublishingEnhancements" Namespace="MichaelHofer.SharePoint.PublishingEnhancements" Assembly="MichaelHofer.SharePoint.PublishingEnhancements, Version=1.1.0.0, Culture=neutral, PublicKeyToken=7ce575c89ea427a4" %>
And now, you delete the reference to <SharePoint:CssLink> in the master page and use Mike’s control.
<PublishingEnhancements:EnhancedCssLink runat="server"/>
Now, let’s examine the output when using this method.
<head>
[snip]
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/core.css" mce_href="/_layouts/1033/styles/core.css" />
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/pimpmysharepoint.css" mce_href="/sites/nopub/Style%20Library/pimpmysharepoint.css" />
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/anothercss.css" mce_href="/sites/nopub/Style%20Library/anothercss.css" />
[snip]
</head>
So, CORE.CSS is now moved to the top and the world is a happy place! Or is it?
I had one problem with this method. Moving CORE.CSS unexpectedly changed the look of my branding. Then I realized the problem. Now CORE.CSS was being overwritten by every other style, and this was causing unforeseen side effects.
One other DOH problem. APPLICATION.MASTER will not reflect this change – so it has the same issue as the <link> method.
Despite this, I am going to give Mike a high cleverworkaround rating for this solution, but I am going to have to take off one point because he did not package it up into a solution 🙂 Mike will probably think this is terribly unfair, but on my SharePoint large farms with multiple WFE servers, if it is not a solution, it doesn’t go into production! (That’s the topic of part 4 of this series of articles).
CleverWorkaround Rating: Flawed genius – respect, man !
Over-riding CSS Files – My method
Inspired by Mike’s cleverness, I came up with an idea to improve his control. I dusted off VS2005 and added a public property called DefaultCSS. This holds the value of a CSS file I am interested in.
private string CSS;
public virtual string DefaultCSS
{
get
{
return CSS;
}
set
{
CSS = value;
}
}
Next I changed the render method, to swap the value stored in this property with CORE.CSS
protected override void Render(System.Web.UI.HtmlTextWriter output)
{
// Let base render the stylesheets
StringWriter sw = new StringWriter();
base.Render(new HtmlTextWriter(sw));
string renderedOutput = sw.ToString();
if (this.CSS == null) {
output.Write(renderedOutput);
}
else
{
// Split the styleSheets into an array
string[] styleSheets = renderedOutput.Split(new char[] { ‘\n’ }, StringSplitOptions.RemoveEmptyEntries);
if (styleSheets.Length == 0) {
output.Write(renderedOutput);
}
else
// Find reference of core and CSS property
{
int CSSRef = new int();
int CoreRef = new int();
for (int i = 0; i < styleSheets.Length; i++) {
if (styleSheets[i].Contains(this.CSS)) {
CSSRef = i;
}
else if (styleSheets[i].Contains("core.css")) {
CoreRef = i;
}
else {
styleSheets[i] = styleSheets[i] + "\n";
}
}
// Now swap those elements
string tmp = styleSheets[CSSRef];
styleSheets[CSSRef] = styleSheets[CoreRef] + "\n";
styleSheets[CoreRef] = tmp + "\n";
output.Write(string.Concat(styleSheets));}}
}
}
}
So, as with Mike’s control, I compiled it to a DLL and copied it into global assemble cache (GAC), and then marked the web control as safe in web.config.
Then, in the master page, I added a reference to my DLL.
<%@ Register Tagprefix="PublishingEnhancements" Namespace="cleverworkarounds.SharePoint.PublishingEnhancements" Assembly=" cleverworkarounds.SharePoint.PublishingEnhancements, Version=1.1.0.0, Culture=neutral, PublicKeyToken=7ce575c89ea427a4" %>
And now, you delete the reference to <SharePoint:CssLink> in the master page and use my version of the control. (Don’t forget to add the DefaultCSS property!).
<PublishingEnhancements:EnhancedCssLink runat="server" DefaultCSS="anothercss.css" />
I then packaged this up as part of my branding feature/solution (more on this in part 4) and then deployed with success. Now let’s examine the output when using this method.
<head>
[snip]
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/pimpmysharepoint.css" mce_href="/sites/nopub/Style%20Library/pimpmysharepoint.css" />
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/core.css" mce_href="/_layouts/1033/styles/core.css" />
<link rel="stylesheet" type="text/css" href="/sites/nopub/Style%20Library/anothercss.css" mce_href="/sites/nopub/Style%20Library/anothercss.css" />
[snip]
</head>
So now, only ANOTHERCSS.CSS has been swapped with CORE.CSS. PIMPMYSHAREPOINT.CSS is rendered prior to both, but the key here is CORE.CSS is not rendered last!
So, was I a genius? Was this the clever workaround? Can I give myself a high clever rating? 🙂
Unfortunately not. Both Mike’s method and my BRO (blatant rip off) of his method fail to deal with the _layouts/APPLICATION.MASTER issue. In addition, this method requires the most customization. We need to add a DLL to the GAC, declare it in the master page, as well as tell the various SharePoint web applications that the control is safe to use.
If you do all this manually on a single server WFE farm, you can probably get away with this, but on a medium to large scale farm, it should be packaged up as a solution before being deployed.
CleverWorkaround Rating: Was a worthwhile exercise
So … let’s take a breath and recap on the 5 methods discussed so far and list their relative merits and disadvantages.
Method | Advantages | Disadvantages |
SharePoint:CSSRegistration |
– Easy to implement |
– Does not overrides core.css |
Master Page Settings | – Easy to implement – Overrides core.css – Works on _layout pages |
– Can only be used once – Has to be set up for each site |
<link> method | – Set in master page once – Overrides core.css |
– Precludes "Master Page Settings" CSS override – Does not work with _layout pages |
Michael’s webcontrol | – Set in master page once – Overrides core.css – Works nicely with "Master Page Settings" CSS override |
– Does not work with _layout pages – core.css as the first CSS can be problematic |
My webcontrol | – Set in master page once – Overrides core.css – Works nicely with "Master Page Settings" CSS override – core.css is not the first stylesheet in the list – allows choice as to what css overrides core.css |
– Does not work with _layout pages |
Now for many SharePoint logical architectures, these methods will likely work very well (Okay… maybe not the <link> method). I hope that some people will find that one of these options solves their respective issues. Unfortunately in my situation each had enough drawbacks that I kept looking.
So, in part 3 of this series of articles, I will explain some more options, and explain the method that eventually worked the best for me.
Hi,
Very interesting post – nice one.
I must ask you though: Why not use themes? A theme will override the rendering order of core.css!
I am working on a public facing site at the moment…And our custom theme is being rendered after core.css
Am I missing something here? Why did you not do this?
Steve
Hi mate
Check part 3 of this series 🙂
I have used SPD to move elements of the master page around and saved as a custom master page. How can I get the _layouts pages to match my custom master page look?
Overriding the pre-render event will certainly work, however, I think it is easier to just do a custom httpModule to catch the css link.
i too find it easier to httpModule to catch the css link, this is a process i use often where needed.
You guys are funny! Do you think you can use any of these methods to sharepoint enterprise search??? I try to change the css to a damn page and I want to send to hell the beautiful microshit logic.
“Can you tell me where/how to apply a master page and edit the CSS files for the My Profile (Public) MySites? I was able to change the My Home version but not the My Profile one.
I understand that changes made to the My Profile page will show up on everyone’s page”
Now I really worked out above issue, Just easy! but it take me a times to fix it 🙂
I prefer SharePoint Designer for my editor,
1. First of all click File-> open site then type your mysite location, maybe it look something like this:
http://xxxxx/mysite/
2. then open person.aspx page for edit layouts
3. add new line below to person.aspx
that css file reference to your new one style sheet you want dressing up this page. The rough content within this css file look something like this:
.webPartZoneRight .ms-WPTitle a
{
color: #ffffff;
}
.webPartZoneRight .ms-WPTitle
{
color: #ffffff;
background: #ffffff url(’/Style%20Library/wpBgWhite.gif’) top left transparent no-repeat;
border-color: #bababa;
padding: 5px 0 5px 10px;
}
.webPartZoneRight .ms-WPHeader div.ms-HoverCellActiveDark
{
background: #ffffff url(’/Style%20Library/wpBgWhite.gif’) top right transparent no-repeat;
border-color: #bababa;
height: 26px;
margin: 0;
border-style: none;
}
.webPartZoneRight .ms-WPHeader div.ms-HoverCellInActive
{
background: #ffffff url(’/Style%20Library/wpBgWhite.gif’) top right transparent no-repeat;
border-color: #bababa;
height: 26px;
margin: 0;
border-style: none;
}
.webPartZoneRight .ms-WPBorder
{
border-color: #bababa;
background: #FFFFFF;
padding: 5px;
}
.webPartZoneRight .ms-partline
{
background: #FFFFFF;
}
.webPartZoneRight .ms-WPBorderBorderOnly
{
border-color: #bababa;
background: #FFFFFF;
padding: 5px;
}
This style indicate how to change displaying the title of webpart
4. Then color full your page as you needs
5. Save and Refresh My Profile page
Have fun!
Did you see this : http://panvega.wordpress.com/2009/04/01/sharepoint-masterpages-css-registrations-approaches/
That solves the issues of the order of the CSS file that is due to an alphabetical ordering.
Maybe you could add this element to this excellent article ?
The DefaultUrl attribute of the SharePoint:CssLink tag will override core.css. Just specify the path to your CSS in this attribute and it will be rendered before core.css.
Tis is indeed a good article with tones of useful information. But a final word as a hardcore designer , I have never imagined a more crappy product than Sharepoint where I have to compile my own assembly , then place it the gac and get it approved in the web.config file all for the sake of applying a simple stylesheet !!!!!!!!!!
As a hardcore designer, I wonder whether you would have responded to my psychic powers? 🙂
Arko: You obviously don’t get it! Stick to cut and pasting photos and let us developer folks handle the more complex solutions.
Dropkic: Your narrow understanding of designing speaks for itself when you think its a cut paste work. As far as handling the complex solutions is concerned , I see no reason why you need a DEVELOPER’S SKILLS just to deploy a simple theme. This is where Sharepoint sucks … even 3rd grade kid can understand it !
Thanks !
Okay boys.. cool it. In case you hadn’t noticed, a lot of the content on this blog is about topics of shared understanding and why blogs are very poor environments for dealing with disagreement and tend to turn into potential flamewars. How about you both take a chill pill and read the “one best practice” series as it is much more valuable than the branding series and you might learn something bigger than design or code.
Everyone seems to be missing the point here. There is a little known function called @import (supported by all standards-compliant browsers). Overriding any core styles via the master is a requisite. No one is denyiong that.
But on a per site basis where custom CSS is required, you just need to specify your custom CSS file at the site level (via Master Page settings). Then within that CSS file you simply insert @import calls to your branding CSS files (at the top!) and then add any site-specific styles after.
Hiya
Thanks for the suggestion. My bet is you never read part 3 of this series though 🙂
regards
Paul
Doh! I knew it wouldn’t be like you to miss something so obvious Paul. You were just keeping the best till last. My impatience won out. 🙂
Please help me with this query. Even after applying the right custom css to application pages, the look of those pages will be different form the site pages due to difference in the master page layouts right? The only alternative is to use a different master page than application.master ( which is not recommended) ?
Hi,
thank you very much for the tips. They helped us a lot.
Patrick Lamber
———————————————————————————————-
http://patricklamber.blogspot.com (SharePoint, ASP.NET, PowerShell)
I am using blueglassband master page for our intranet pages. I have problem with display in IE. If there is left navigation(Quick Launch) the width of the page increases. So for some of the pages the staff will have to scroll to the right to see the contents. However when the default master page adjusts itself so the entire screen is visible on the monitor.The culprit is center section of the page. On Mozilla the width always remains the same. I am not sure how to fix this?
ThankU very Much.