Published in CSS on Thursday, February 17th, 2005
In Discussing CSS Management and Optimization we looked at some of the methods used to code and manage CSS, and saw that taken together they could result in a lot of code spread across several stylesheets. In this post we will look at some methods for dealing with that extra code and those extra http requests.
Before we get into the good stuff, a few little notes about dynamic CSS, something used in a few of the ideas discussed below.
Many people are toying with the idea of dynamic stylesheets by doing some server side magic with their CSS by parsing it (with php, asp etc.) before it leaves the server for the client. As we will see below, this helps to offset some of the issues that arise from managing stylesheets that we discussed yesterday.
One thing that I cannot stress enough about dynamic CSS is that you do your homework first, and by this I mean test your stylesheets, make sure that they cache properly (see 'Does it cache?') and make sure that you send them with the correct headers (header("Content-type: text/css")
, for example).
If you do not have a local or remote server where you can test caching effectively, Mark Nottingham links to the Cacheability Engine, a useful tool for testing cacheability.
Hacks are a necessary evil of CSS, and managing them well requires commenting, resulting in code bloat, and possibly extra stylesheets, resulting in extra requests.
To help deal with managing hacks, we can use either CSS filters or browser detection. In the end, both of these methods hide styles from target browsers.
The first method used with the Surgical Correction Strategy (scroll down), can be seen being used on the master stylesheet for Stopdesign.
The second method, browser detection, can be done with your scripting language or conditional comments. Using PHP, for example, comes with the bonus that you can comment for free in the PHP and not worry about delivering the comments to the client.
I remember back in my pre-programming days building a site and using some functionality in TopStyle to convert the rules in my stylesheets to single line and remove extra whitespace. This was for a brochure site, but nonetheless changes later on were a real pita. In addition, the savings just weren't that significant, especially when compared to a compressed stylesheet.
Here the decision is quite simple. If the stylesheet is small enough, compression may not be worthwhile and converting rules to single line not that painful, otherwise I think compression is worth it.
As I outlined in the previous article, splitting up the styles for a site by purpose can be quite helpful. Some people could go to extremes *cough* and easily have > 5 sheets, for example. To be honest, it is really quite helpful to have your navigation rules on one sheet (for sites with complex multi-tiered navigation, for example) and layout-bound background images organized on another. But won't all of this come at a cost?
I know that some of you are saying, 'no, CSS caches, dummy', and yes fine it does, but some sites have more than just CSS coming down on that first visit, so why not make it as pain free as possible?
A while back I wrote a(nother :-) post about organizing CSS, covering a few different ideas where you can put CSS to work with PHP to help keep things better managed.
In the article (see 'Stitch'em together') I outlined an idea whereby using PHP you could stitch together your various stylesheets, thereby reducing browser requests and ultimately turning many sheets into one, and said that I would test it out and report back.
Well, I have put the method to use in places and I can almost safely say that it works just fine. I am using it here on this site, where I have 4 separate sheets beyond "basic.css":
These are all happily sutured together into theRest.php with the following code (Note: headers are handled in another place):
<?php include("$_SERVER[DOCUMENT_ROOT]/css/layout.css"); include("$_SERVER[DOCUMENT_ROOT]/css/layout-style-orig.css"); include("$_SERVER[DOCUMENT_ROOT]/css/typo.css"); include("$_SERVER[DOCUMENT_ROOT]/css/typo-style-orig.css"); ?>
Bringing this all together, what would be the ideal way to set up stylesheets for a site? In the end it depends on many factors, the size of the site and CSS, the time the author wants to waste fishing through their CSS etc., but the following is how I like to lay things out:
Some extra things that can be done:
Well, if you've made it this far you deserve a thanks for reading all of that!! Feel free to sound off your IMO's in the comments ;-)
Sitepoint's web devlopment books have helped me out on many occasions both for finding a quick solution to a problem but also to level out my knowlegde in weaker areas (JavaScript, I'm looking at you!). I am recommending the following titles from my bookshelf:
I started freelancing by diving in head first and getting on with it. Many years and a lot of experience later I was still able to take away some gems from this book, and there are plenty I wish I had thought of beforehand. If you are new to freelancing and have a lot of questions (or maybe don't know what questions to ask!) do yourself a favor and at least check out the sample chapters.
The author line-up for this book says it all. 7 excellent developers show you how to get your JavaScript coding up to speed with 7 chapters of great theory, code and examples. Metaprogramming with JavaScript (chapter 5 from Dan Webb) really helped me iron out some things I was missing about JavaScript. That said each chapter really helped me to develop my JavaScript skills beyond simple Ajax calls and html insertion with libs like JQuery.
Like the other books listed here, this provides a great reference for the PHP developer looking to have the right answers from the right people at their fingertips. I tend to pull this off the shelf when I need to delve into new territory and usually find a workable solution to keep development moving. This only needs to happen once and you recoup the price of the book in time saved from having to develop the solution or find the right pattern for getting the job done..
Comments and Feedback
It'd be interesting to see somebody come up with a PHP class that could automatically parse a CSS file and apply browser hacks as necessary when fed to the browser.
You code your CSS in a way that you prefer with multiline formatting, etc. Then the PHP class loads it in and compresses the stylesheet for you. AND, the best part would be adding or removing CSS based on the user agent requesting the page. So you could use the min-height property in your stylesheet and if IE requests the page, the class could convert it into the height property.
Oh, that'd be hot.
Hey, that's a very cool idea - I like that swapping thing! We'd need a list of matching rules and some good regex, I think.
The sheets would be named according to the browser, I suppose:
theStylefile_ie5.css
etc...The trouble with PHP browser-sniffing is Opera's user-agent fake-out - you might end up delivering the wrong CSS to a compliant browser because it declares itself as something different.
I'd be interested to know if the sites you linked to in the first article are using something like your Stitch'em method - StopDesign and Wired in particular sound like huge files.
Looking forward to that PHP class, too... ;)
Hey Matthew,
I'm not sure all of this UA spoofing hoo-haw is all it's cracked up to be. Yes people can do it, but do they? (I ask as I check my firefox UA widget and see that, in fact, I AM!!)
I'd also imagine that Opera users who are surfing spoofed are doing so for a reason - and are aware that they may run into problems. Heck, they will on this site! (at least < Opera 8.0 users will).
And going by the Live HTTP headers in Firefox, neither Stopdesign or Wired are doing any suturing of their sheets...
Here is a great example of taking the time and care to manage CSS, over at the NCC Website Documentation.
Firstly, let me say "Welcome back!" And with style (both design and content wise). Two really nice pieces here that provide us all with some things to think about.
Better CSS Management is one of my Coding goals for 2005. I've been successful so far in keeping my style sheets modular - general page layout in one file, form layouts in another, and then other specific ones where needed - say, for a group of tabs.
I'll have to take a look at some of your ideas here for optimization as well -- as I'm sure you'll agree, we can always improve...
I hacked together a quick CSS compression PHP script. It not only compresses, but will also remove comments and new lines automatically.
It's also dynamic - meaning it can be called like
compress-css.php?cssfile=main.css
Check it out: Compress-css.php
Mike,
The document you showed us on your comment is really helpful for me. All web site should have similar docs telling relationship between visualization and name space. Thank you.
- that's definitely something I'm using more and more and it is very helpful. The Stylesheet Suturing keeps my conscience clean as well.
I had wondered about somthing like that, but for the hacks. What someone could do is apply your function to the hacked sheets, having used "Filter methods" to keep browser specific hacks on seperate sheets.
Things like the example I gave above. I stumbled across another good example yesterday, when Keith announced the PBDH redesign.
Check out their CSS, they label the ID's with the tag they are associted with. It seemed a little redundant to me (id's being unique and all), until I realized that by doing so, I, someone who has nothing to do with the site, understand right away what some of that code is for (ie, body level page switched etc.).
Together with the other example there is some very good CSS code management happening there (as opposed to stylesheet management, which is what I've mostly been babling on about).
Linking tags with ID's gives a lot of help to people coming in and viewing your css "off the street". I do this with as many classes as I can as well. Helps readability a lot I think.
Hey seth,
I'm building here off of your comment on the previous post, where you said: .
I hear you there - a great idea for an app woud be one where you can set rules for ids, tags etc but have the different 'types' of rules hidden, or located in separate areas.
That way you could work on 'layout' rules in one tab, 'color' rules on another, 'nav' rules on another etc. The file would be parsed, rules put together and the whole thing cached whenever changes are made.
This would be ideal, though I tend to have trouble deciding where to put some values - padding and margins seem to confuse me somtimes, though I suppose they are for layout...
I have thought about stitching stylesheets together before, in our company we have seperated stylesheets out on a per purpose basis (global, navigation, layout, skin etc.) & per section basis - e.g. home layout, home skin, section layout, section skin. This is partly to do with us not having as much site wide consistancy as I'd like - we've only just recently converted from all tables & inline styles to semantic markup & linked stylesheets.
My inital thoughts on stitching these together was that we'd loose the benefits of caching as for each section of the site a new sutured stylesheet would need to be downloaded with would have to include the global, navigation etc.
So my current thoughts are on two major sutured stylesheets - a global one with navigation etc. and then a per site section one.
Just to further describe the app that I mentioned:
It would allow you to:
The subsequent page would be built and either cached or uploaded...
A hurdle would be in what you describe, DEfusion. And hacks. But I'm certain these could be worked in as features ;-)
I typically separate my styles sheets like you mentioned Mike and it works great.
I usually make:
Great second article, I should have commented on this one than the previous-duh.
Wow, a very on point article relating to the work I've been doing of late.
I just started employing CSS compression through the use of server-side scripting (.NET) and inserting dynamic CSS based on browser sniffing (through server-side scripts).
A few weeks ago I enabled CSS optimization on one my projects, Sacramento Web Developers SIG. Here one can see the CSS as a browser would see it. And here you can see the formatted version.
What I've done is put all my standard CSS-style comments (as opposed to server-side), then I use regular expressions to remove the comments (as well as remove whitespace and carriage returns).
I like this technique because it allows the ability to still deliver a comment laden CSS to those who are so inclined to view it (in the case of the "how the heck did you do that?" visitor).
Along the same lines, I used to use external JS files that require extra browser-to-server communication to grab everything. Now I use external server-side files for JS (and some CSS), and when the page is loaded the JS is inserted into the HTML in an optimized form, thereby reducing document requests. This is identical to the idea of separating your CSS, but use PHP includes to create a single document for the browser.
"One thing that we'll need to discuss more, I think, or something that I haven't seen much of, is management of the code itself." (For some reason, I couldn't use
blockquote
.)I agree. How about managing CSS hacks, John Serris style? It's a bit like the PHPSS thing you linked without server-side browser-detection. I figured this might just be the best solution there is, for now.
Hey Mathias, thanks for the note about
blockquote
. I know what the problem is, now I need to fix it ;-)John's article is exactly what I was getting at with the CSS filters that I mentioned above.
Is there really a need to stitch together the CSS files with PHP?
<?php
include("$_SERVER[DOCUMENT_ROOT]/css/layout.css");
include("$_SERVER[DOCUMENT_ROOT]/css/layout-style-orig.css");
include("$_SERVER[DOCUMENT_ROOT]/css/typo.css");
include("$_SERVER[DOCUMENT_ROOT]/css/typo-style-orig.css");
?>
What is wrong with simply adding them to the document header?
<link rel='stylesheet' type='text/css' href='/css/layout.css')/>
<link rel='stylesheet' type='text/css' href='/css/layout-style-orig.css')/>
<link rel='stylesheet' type='text/css' href='/css//css/typo.css')/>
<link rel='stylesheet' type='text/css' href='/css/typo-style-orig.css')/>
(BTW, I wrote a little on this subject on my blog a while back: Modular CSS, and also on PHP Browser sniffing for CSS: Server Side CSS Sniffing - coping with troublesome browsers)
Hey Richard,
Let me lay out an example for you. Lets say I break out the following for my CSS:
6 Pages.
Now, imagine that I also have a javascript file, and that each page relies on at least 4 background images and another 3 'inline' images for display. That's 14 requests.
Now, add 1 for the favicon, 1 for basic.css and another for the page itself and you are up to 17 requests.
You can chop off almost a third of the requests here by suturing your sheets together. This makes sense to me. In addition, by using this method you can break your sheets down to as many as you need to make managing things easier.
(As an aside, if you want to get over-technical here, start thinking about packets and packet sizes and it makes more sense to send one file rather than many, which may each have their own chunk of empty space.)
In the end, for me, it's guilt free splitting of my styles into managable pieces. In addition, someone *ahem - client* can monkey with the modules sheet or some other 'less important' sheet and I don't have to worry about them breaking something in the layout by mistake.
Hey guys,
this is a really great article. I'd like to weigh in on a few things.
For each client, I need to adjust colors, images, fonts, etc to match their brand. I can't do that with static CSS because as the product evolves, the CSS has to evolve with it. If I had to recode the CSS for all 150 clients each time that happened, my boss would have stick just for me.
In addition, I have to be able to handle the larger credit unions who want to look unique and can afford it. These guys get CSS that us totally custom and usually one off.
The only caveat is that you will now have more http requests than with straight caching but most of the time you won't be returning anything in the response
I think that there is something we could do to make managing css easier, especially for complex sites.
Generally, my CSS is built hierarchically, which is to say that I have a lot of CSS that looks like:
.content { ... }
.content p { ... }
.content a:link, .content a:visited { ... }
.sidebar { ... }
.sidebar a:link, .sidebar a:visted { ... }
This is generally not very efficient to work with from a coding point of view. What I would like to have is something like:
.content
{
/* styles */
p { /* style for paragraph*/ }
a:link, a:visited { /* style for links */ }
}
.sidebar
{
/* styles */
a:link, a:visited { /* style for links */ }
}
Obviously this isn't going to make any sense as CSS but it would be very easy to convert that into proper css.
Further, I often find myself repeating colors and stuff so being able to call named values (or blocks) would be valuable. Making it overridable would be very useful. further adding prototypes would be super something like (I'm making up syntax as I go, its probably not efficient):
@template('anchors')
{
%this a:link,
%this a:vistied
{
background-color:^PrimaryColor^;
color:^SecondaryColor^;
display:block;
}
%this a:hover
{
background-color:^SecondaryColor^;
color:^PrimaryColor^;
}
}
@def('PrimaryColor', 'red');
@def('SecondaryColor', 'green');
.content {
@def('PrimaryColor', 'black');
@def('SecondaryColor', 'blue');
%anchors;
}
I realize its a convoluted example but I think it has the potential for making managing complex CSS much less risky in the long run. The sytax is also not optimal but you get the idea.
Mike:
If you link your stylesheets/javascript/favicon separately then after the initial requests are made all these files will be cached an not requested again.
If you use PHP to combine your stylesheets, then the request for the CSS will cause the webserver to re-run your combination script and make the file requests (includes) again for every page.
Don't get me wrong, I'm all for splitting up CSS into manageable modules, but using PHP to recombine them just increases server load needlessly.
Here's how I do it:
<?php
$css = array["layout.css"]; // all pages require the layout CSS
if (/* the page requires the form css file */) {
$css[] = "form.css";
}
if (/* the page requires sidebar CSS */) {
$css[] = "sidebar.css";
}
// ... etc
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Page Title</title>
<?php
foreach($css as $filename) {
echo "<link rel='stylesheet' type='text/css' href='$filename'/>";
}
?>
</head>
<body>
rest of page here
</body>
</html>
(Appologies for the poor formatting, I can't seem to get my carrage returns to stick. Please feel free to reformat it)
Using this method, only stylesheets required by the page are added to the document. You can still split up your CSS into managable chunks, with non of the extra server hits.
... and following on from Adam's post. My current work requires branded webpages for groups of clients (reports, logo's etc).
Using the technique mentioned above, I have one default layout stylesheet and many branding stylesheets. I include the standard layout and then the specific branding stylesheet depending on which client group is viewing the page.
This means my branding stylesheets are 'pure' CSS (and therefor cacheable), and don't need PHP to alter them dynamically.
Hey Richard,
I see what you are saying, however if you deal with your headers correctly and generate a sutured sheet and save that sheet as I outlined in the last section of the article, all of these problems are moot.
These files (favicon, css, js etc.) are cached on the first visit, I get that, and I see how some people will write these ideas (compression and recombining sheets) off because of that.
The idea here, though, is management, and how can I split up my files as I please and not worry about extra requests? Keep in mind that these ideas are for sites with a lot of CSS rules. Heck, looking at the numbers on the previous article, these are issues for some peoples weblogs!
The answer is to stitch them back together. With this we have to worry about server load, the answer then is to generate and cache the CSS file, preferrably with a date string attached to the filename.
Hmm - Richard, re-reading your comment I think that there may be some confusion here. I'm combining the sites core CSS files. On the first visit, this file is downloaded and cached by the browser. So even if we ran the suturing without building and caching a generated copy of the CSS file on the server, that is one hit per visitor. If we do cache and deliver that generated file, then server load isn't an issue.
Appologies Mike, I had missed the part about rebuilding the CSS as a new (cacheable) file. The drawback to that is you must remember to rebuild you combined sheet every time you (or one of your designers, perhaps without your knowledge - you know what designers are like for tweaking things ;-) changes one of the included stylesheet. My method negates that step.
No worries Richard. This is something in it's early stages, just ideas really.
I could see though, where a piece of software like the one that I described in comment 13 would be a great tool for some of us. Something that managed all of the rules and rebuilt and uploaded the CSS files with a click - similar to the way Moveable Type rebuilds posts for a blog.
I could see work becoming easier with a tool like that!