The CSS preprocessor does not work as advertised. I understand completely the reasoning behind aggregating css files to reduce the number of http connections to the same server and thereby supposedly decrease page load times. I have read all the discussion and cannot find anyone anywhere who has addresses Grugnog2's points made in this comment.
The real world result of this is that currently my site's css cache has 21 (!) separately aggregated css files at an average file size of over 46kb - BUT the smallest is 43.6kb and the largest is 49.7kb. This small range of sizes suggests that the vast majority of code is being redundantly cached over and over every time a module injects the smallest piece of css on a specific page (and it is most probably your theme's style.css file - more on that later). On one site I run, there are a row of 7 tabs representing primary links for the site - and 6 of them require different aggregated CSS files to be downloaded when clicked.
So I'm trading off a slightly quicker page load on the first view of my site for substantially slower page loads every time the user hits a page which uses a different cached CSS file - which looks to be quite often if I look at the cached CSS directory on my server. I can't help but feel that this is a significant waste of time and bandwidth - totally the opposite of what it aggregation is trying to achieve.
One major contributor to this is modules that use drupal_add_css() without specifying the preprocess flag as FALSE (by default it is TRUE, which is a bug IMO). Some example modules I have found include CCK, Devel, Views, Panels, Image Attach, Thickbox etc - significant, major modules which all unnecessarily cause new cached CSS to be created because they tell drupal to preprocess CSS that is only included on selective pages, rather than site-wide.
I would like to see:
- The drupal_add_css preprocess flag switched to FALSE by default, so only module authors who know what they are doing set it to true. Core modules should be patched to call it with a true flag if they don't already.
- Add the option to preprocess either (a) every module's css or (b) every core module's css into one big one - or even let the administrator select which ones. I would rather have my users download one big file once and cache it locally, than have to download 21 medium sized ones regularly.
In the meantime, we have done the following, and I'd advise anyone who feels this is a problem to do the same:
Rename your theme's CSS file from style.css to anything else (we used base.css) and include it manually in your theme's page.tpl.php. So you might have something like this in your page.tpl.php's head tag:
<?php print $styles ?>
<style type="text/css" media="all">@import "<?php print base_path() . path_to_theme(); ?>/base.css";</style>
This will stop your theme's CSS from being preprocessed (because "style.css" is hardcoded into core), and for us that represents a significant chunk (28kb out of 46kb) that is no longer aggregated with all the module CSS. And all it will cost you is one extra http connection on the first page load, and then it will be cached by the browser. So now we still have oodles of supposedly optimised cached CSS files, but they are all significantly smaller (under 20kb).
Comments
Comment #1
sinasalek commentedSince it changes drupal behavior i don't think that core developers fix it. i use css caching for reducing css size and http connection.
Comment #2
mr.j commentedBut that's exactly the point - you are using it to (a) reduce http connections and (b) reduce css size. Point (a) is covered by aggregation.
Point (b) is totally the opposite. If a user could potentially download 21 css files at an average of 46kb each, that is 966kb of css downloaded, instead of the size of the single largest cached css file (49.7kb in my case). So how does making a user download 966kb instead of 49.7kb accomplish the goal of reducing css size?
Comment #3
Crell commentedAll that means is that contrib modules need to get better about how they queue up their CSS; generally they should queue it up always unless it's admin-only CSS. In my experience, switching the compressor on can cut page load time in HALF, even with duplication. Yes, CSS files only used once or twice should be non-cached, but it's even better, if the CSS file is small, to just send it always and let the compressor handle it.
So this isn't a core bug, but a module bug. Please file an issue with the appropriate modules.
Comment #4
mr.j commentedYou are right in that it is a module problem, but seeing how prevalent the problem is, it is clear that this has not been communicated or documented adequately for module authors. They simply don't know what the effect is of just calling drupal_add_css without the false flag.
On this point you could not be more wrong. That is exactly why this mess has been created.
For example - the Panels module. It always queues its CSS for aggregation whenever you hit a panels page. But on a typical site that might be 2 or 3 pages out of hundreds. It is not admin-only CSS but it clearly isn't useful on every page. So when you hit a page that uses panels, you get to re-download the entire CSS for the site plus a few lines of panels CSS.
I could pick out a dozen other examples like that from the handful of modules we use on our sites alone.
This would be far better served as a separate CSS file, or as I suggested preprocessed as part of one singular CSS file that does not change depending on what module is used on the page you are hitting.
Of course it can on a singular page load. I am not asking for the preprocessor to be thrown out. But respectfully, you are living in a fantasy world if you think the scenario I presented (966kb vs 49.7kb) is going to offer a user a faster experience over a browsing session. Remember the 49.7kb looks to be the largest possible size needed, so it would obviously be better to aggregate every possible css file once and serve it on every page to allow the browser to cache it once, rather than the current inadequate solution which forces my users to download up to a megabyte of redundantly cached css as they browse the site.
Comment #5
sinasalek commentedWe all know that . if you're a developer you can find most problematic modules and fix them and then send the patch to module issue queue so it will be fixed in next releases. (that's what i do most of the time).
You can also patch drupal_add_css to do what you want (I think this way is faster).
PS : As i mentioned before , your solution changes Drupal behavior and it's against Drupal release policy. if you hurry to might be able to apply it to Drupal 7 which is still under development.
Comment #6
mr.j commentedPoint taken. Switching to a drupal 7 feature request as for my clients it is a significant issue.
Comment #7
mr.j commentedSetting back to active
Comment #8
Crell commentedThen file an issue against Panels to add its CSS on hook_init() or hook_menu(!$may_cache) (Drupal 5 and Drupal 6, respectively) rather than just on selective pages. This is not a core problem.
Comment #9
mr.j commentedI think I have explained adequately why it is a core problem.
I picked panels as an example. There are thousands of modules out there. Do you really expect me to file an issue against every one that is making this mistake?
Comment #10
dvessel commentedYou know there's sf_cache module if you want to have more control over aggregation.
As for setting aggregation to false by default, then we'd have the opposite problem of having too many concurrent connections. Opting in vs. opting out..? I think the former would only bring us back closer to the problem aggregation was trying to solve.
Comment #11
mr.j commenteddvessel thank you.
Support File Cache looks like it is exactly what we need (just in time too because we were about to write something ourselves given the resistance to this idea). Thanks once again.
Comment #12
millions commentedI need to solve this issue too, but sf cache won't work with php4 which is what my godaddy account runs. Any other suggestions? If I disable CSS aggregator my site doesn't work right in ie6, if I enable it, I get blank pages showing randomly. If I could tell people to refresh in that instance it would be ok, but visitors don't email to ask...
Comment #13
mr.j commentedNo sorry I can't help you more than what I have posted above. All I can suggest is move as much of the common CSS as possible out into a separate file as I suggested above if you cannot use Support File Cache.
I have looked back at the site I was talking about when making the post above. It now has 40 separately cached files in the CSS directory. The average file size is much smaller now that we took the "base.css" approach - around 16k. Imagine if our 28k base.css was being added to each one - 40 separate files, around 44k on average. This is clearly unacceptable, and I still regard it as a bug. While it may be "by design", the design is faulty if the result of attempting to improve speed is making someone re-download the same CSS over and over and over and over.
Many of our users still use modems. But this design decision forcing them to download reams of redundant CSS every 2nd or 3rd page load makes their browsing experience much better!
Comment #14
dvessel commentedVery nice. If you really want this fixed, how about a reasonable alternative to opting in? Minimizing concurrent connections was the main reason we needed aggregation. This move coincided with splitting a monolithic style sheet into each module folder so they are easier to work with.
No one said aggregating was perfect and I'd love to see it improved but opting into it is a poor solution.
BTW, the latency involved with dial-up connections is far worse. All sites will vary but that initial load would be pretty bad when there are multitudes of style sheets. Most browsers only support 4 concurrent connections. Try funneling all the linked files plus the latency through that tiny window.
Comment #15
mr.j commentedApologies if my last post was dripping with sarcasm, but I have spent days and weeks tweaking Support File Cache on multiple sites to work around this problem.
As for alternatives, I gave 3 in addition to changing the drupal_add_css preprocess flag to false:
1. Preprocess every possible css file into one big one (core + non core modules). Disadvantage: lots of unnecessary css on every page, but at least the user only downloads it once.
2. Preprocess every core css file into one big one, and every non core module into one big one. (2 css files total). Disadvantage: arbitrary distinction that is unlikely to reflect real world usage.
3. Admin chooses, ala Support File Cache (not the default, as it is obviously a power user feature). Disadvantage: lots of configuration and testing needed.
If none of those suit you, as a starting point what about these?
4. Do not include the theme's style.css in the preprocessed stylesheet. As demonstrated above this can result in significant savings of both bandwidth and time. Added benefit, if you change theme your cached files are not invalidated.
5. Combine the above approaches. i.e. don't cache style.css, and preprocess core only.
Also I disagree on the modem users. A 46k css file is going to take ~10 seconds to download for a 56k user. Double that for 28/33k. But if 7/10 of the css files that make up that cached version have already been downloaded and browser-cached, then they only have to grab 3. With a typical modem latency of 300ms, thats only 1 second of latency plus the 3 files' download time (a lot of assumptions there but I am sure you get the point).
Comment #16
momper commentedsubscribe