We are repeatedly warned in the
Windows SharePoint Services (WSS)
SDK not to alter the files in the
60 Hive, including the built-in ows.css file. To customize our sites, it would be helpful if we had our own
Cascading Style Sheet (CSS) file that we could use to override the CSS classes that WSS uses to display pages. The file could contain alterations to the CSS classes WSS has already defined and include our own CSS classes without worry about service patches or patches overwriting our changes. However, this isn't as easy as it should be and there are lots of pits to avoid. I've documented them below.
First, the WSS SDK states:
Warning: Changes made to OWS.CSS may be lost when Windows SharePoint Services is updated, such as through installation of service packs or patches. An alternate .css file can be specified in the ONET.XML file of a site definition by using the AlternateCSS attribute of the Project element.
[AlternateCSS is] used in an ONET.XML file to specify the name of an alternate CSS file located in the Local_Drive:\Microsoft Shared\Web Server Extensions\60\TEMPLATE\LAYOUTS\1033\STYLES directory that defines styles to use in the site definition.
So, the AlternateCSS attribute sounds like something we should implement on all Site Definitions, even if we don't immediately need it. Otherwise, sites that we create between now and when we do implement this attribute won't retroactively benefit from future style changes.
Since CSS styles cascade, I _thought_ that this attribute would leave the existing ows.css styles in place and I would simply have to override the styles that I wanted to alter and add the CSS classes that I wanted to add. Unfortunately, I discovered that I had to completely copy all the built-in styles into my alternate CSS file. This means that if the WSS team changes the styles in the ows.css file in the future, my sites won't reflect those style changes. I will have to manually copy the changes into my alternate CSS file. This will likely be a very rare occurrence, so I determined that the benefit outweighed the redundancy.
I initially created a copy of the ows.css file called mindsharp.css and placed it into the 60 Hive's ..\60\TEMPLATE\LAYOUTS\1033\STYLES subdirectory along-side the ows.css file. The SDK incorrectly instructs that it should go there (more about this momentarily). I altered the .ms-bannerframe class so that I could easily see the effect in the top nav of any site.
I then added the AlternateCSS attribute to the Project element in a copy of the STS Site Definition's ONET.XML file as follows:
<Project Title="Team Web Site" ListDir="Lists" xmlns:ows="Microsoft SharePoint" AlternateCSS="mindsharp.css"> |
| CAML |
|
After an iisreset, I created and subsequently templatized a new WSS site using my altered Site Definition hoping that was all there was to it. However, when I saw the Home page, I was disappointed. I saw no visible effect. In fact, the only pages in the site that looked different were the administrative pages in /_layouts/1033 (AKA direct mode pages), and those pages didn't appear to have any style applied to them at all! This wasn't what I expected and obviously was an undesirable effect.
After some investigative research, I discovered that if I put the mindsharp.css file into the /_layouts/1033 directory rather than the /_layouts/1033/Styles directory (as the SDK suggested), the administrative pages began reflecting my style change. However, I don't like to put files into the /_layouts/1033 directory. I have an /_layouts/1033/Custom directory where I typically put my custom files. So, I moved the mindsharp.css file into the 60 Hive's ..\60\TEMPLATE\LAYOUTS\1033\Custom directory, renamed mindsharp.css to ows.css (so I'd know what file I was customizing), and changed the AlternateCSS attribute as follows:
<Project Title="Team Web Site" ListDir="Lists" xmlns:ows="Microsoft SharePoint" AlternateCSS="Custom/ows.css"> |
| CAML |
|
Of course, I had to reset IIS, create a new WSS site, and templatize it using my altered Site Definition again. After that, the administrative pages DID reflect my style change but the site pages still did NOT reflect my style change.
Rats, more research. What I subsequently discovered is very unfortunate, but can be overcome.
The administrative pages use the following server control to include either the ows.css file OR the Site Definition defined AlternateCSS file (note that the DefaultUrl is a page relative reference):
<SharePoint:CssLink DefaultUrl="styles/ows.css" runat="server"/> |
| ASPX |
|
Therefore, as previously noted, both CSS files are not included so we need to copy all the classes defined in the ows.css file into our alternate CSS file.
The site pages use the following HTML Link tag to include the ows.css file regardless of how the AlternateCSS attribute is defined (note how the LCID is dynamically included):
<Link REL="stylesheet" Type="text/css" HREF="/_layouts/<%=System.Threading.Thread.CurrentThread.CurrentUICulture.LCID%>/styles/ows.css"> |
| ASPX |
|
To continue, I would need to replace the Link tag on all the site pages in my Site Definition with the SharePoint:CssLink server control. I initially tested this with the default.aspx page to see how well this would work. I had to use a root relative DefaultUrl property on the SharePoint:CssLink server control as follows:
<SharePoint:CssLink DefaultUrl="/_layouts/1033/styles/ows.css" runat="server"/> |
| ASPX |
|
I did try to dynamically resolve the LCID using the <%=System.Threading.Thread.CurrentThread.CurrentUICulture.LCID%> server value in the DefaultUrl property but the string was escaped instead of being resolved. So, I hard coded the US English LCID, 1033, instead. While unfortunate, I didn't pursue an alternative because this will work for our implementation (of course, suggestions are welcome).
Once again, I tested this configuration. But once again, the default.aspx page did not find the alternate CSS file. This problem was finally resolved by also making the AlternateCSS attribute in the ONET.XML file root relative as follows:
<Project Title="Team Web Site" ListDir="Lists" xmlns:ows="Microsoft SharePoint" AlternateCSS="/_layouts/<%=System.Threading.Thread.CurrentThread.CurrentUICulture.LCID%>/Custom/ows.css" > |
| CAML |
|
Finally, in each of the other roughly 100 files in the Site Definition (mostly in the LISTS folder), I replaced the HTML Link tag to the root relative version of the SharePoint:CssLink server control. I left the administrative direct mode pages with a page relative SharePoint:CssLink server control since it does work for pages in /_layouts/1033 and we are warned not to modify those files either. And after a final iisreset, ALL changes to my ..\60\TEMPLATE\LAYOUTS\1033\Custom\ows.css file were instantly reflected across all site and administrative pages throughout the entire site. I tested it on both top-level sites and child sites.
In summary, using the AlternateCSS attribute is possible (if inaccurately documented), but it requires a minor alteration to every ASPX page in the Site Definition. This is just one more example where the use of root relative WSS references would be very helpful. Also, the alternate CSS file must include a redundant copy of every CSS class defined in the ows.css file. While I would still recommend using this feature in all WSS Site Definitions, it isn't as easy to do as I'd hoped.
Tasks required to implement this solution:
-
Create a Custom directory in the 60 Hive's ..\60\TEMPLATE\LAYOUTS\1033 directory.
-
Copy the ows.css file into the Custom directory.
- Create a custom Site Definition with related WEBTEMP configurations (it's strongly recommended you don't alter the built-in SharePoint Team Site (STS) or Multi-Page Site (MPS) Site Definitions).
-
Add a root relative AlternateCSS attribute in your custom Site Definition's ONET.XML file as follows:
<Project Title="Team Web Site" ListDir="Lists" xmlns:ows="Microsoft SharePoint" AlternateCSS="/_layouts/<%=System.Threading.Thread.CurrentThread.CurrentUICulture.LCID%>/Custom/ows.css" > |
| CAML |
|
-
Replace the HTML Link tag with the SharePoint:CssLink server control in all ASPX pages in all directories of your custom Site Definition.
Replace this (near the top of each file):
<Link REL="stylesheet" Type="text/css" HREF="/_layouts/<%=System.Threading.Thread.CurrentThread.CurrentUICulture.LCID%>/styles/ows.css"> |
| ASPX |
|
With this:
<SharePoint:CssLink DefaultUrl="/_layouts/1033/styles/ows.css" runat="server"/> |
| ASPX |
|
-
Create a new site using your custom Site Definition and any changes that you make to the ows.css in the Custom directory will reflect on all pages in your new site.
<Todd />