Hi, first let me introduce myself and say hello, as this is my first time using Drupal. I am a freelance web designer/developer who has been using Xaraya almost exclusively for my CMS needs. I have tried Drupal on a couple of occasions and found it didn't quite suit my purposes, but with the release of Drupal 4.7 I feel it is time to get to know this CMS and so far I've found it to be excellent. In a short space of time I have created a working site with features I would normally have had to implement in a more laborious fashion. You've already saved me time because of the ease of implementation, so I thank you for that.
Now, on to my problem: I am unsure as to how to override a module's CSS. Currently I am wanting to style the Event module's output and am meeting failure in simply redeclaring the relevant css in my theme's stylesheet.
I have found a function in a forum post that allows me to disable the drupal.css file and I have then re-included a customised version of it with an import statement in my theme's css file. I would like to do the same for other css files imported by various modules I have installed. Does anyone know the best way to go about doing this?
Any help would be much appreciated and please let me know if I've posted in the wrong forum or otherwise made any mistakes.
Comments
Rephrase the question
I'd like to rephrase my question after doing some more investigation:
Does Drupal have a standard override system for CSS files that come with a module? Or do I have to work out how to override them in my template.php file? My objective is not to touch any code in the module's directory so that when it comes to upgrading the module every adjustment has been made in the theme's directory.
Does this make sense? In Xaraya there is a simple override system so if you place a css file in the correct directory it will automatically override the css file that comes with a module. From what I have read and examined it seems Drupal doesn't have this feature. It also looks like the API call to load the module's css is implemented differently by different module developers. This makes it tricky for me to work out how to override (or completely cancel) the loading of a module stylesheet.
I read a forum post where people were discussing a module's css file and they were simply changing the css file directly in the module directory. This seems like a bad idea to me but please correct me if I'm wrong.
Override using the theme's CSS
You can quite easily "override" a module's CSS file by writing additional rules in the theme's style sheet.
This works because the CSS specification says that a rule that is specified in a "later" style sheet overrides a rule that is specified in an "earlier" style. In Drupal, the theme's style sheet is always included last.
For example, if a module's style sheet defines:
You can simply override this by placing the following in the theme's style sheet:
The only caveat is that you will need to make sure that the selector (in the example,
.event-calendar table) is exactly the same as the one specified in the module's CSS. Otherwise strange things may result.Additional overhead
kmo, thanks for your reply. Overrides in this fashion are fine for minor changes but in my opinion it isn't suitable for heavy traffic sites where a number of modules and their associated style sheets are used as it doubles the amount of information the browser needs to load per css declaration (one module stylesheet then one overriding stylesheet). This increases the overhead and in some environments this can make a critical and even financial difference to the running of the web site.
What I'm looking for is a way to override the module's stylesheet before it is loaded by the browser so that a custom stylesheet is loaded in it's place. I'm curious to know how people achieve this as I'm sure this must be an issue for some people. Of course many people probably directly edit the module's stylesheet but again I feel this isn't appropriate as further module updates will need careful checking to ensure no new rules have been introduced or the stylesheet isn't accidentally over-written.
I'm beginning to think there is no clean solution to this problem from other forum posts. I'd like to see a better override system where a css file placed in the correct template directory would automatically override one in the associated module directory. Otherwise a solution in the phptemplate theme file would be adequate.
Another way
There is one solution using the
page.tpl.phpfile in the template, but it's slightly clumsy.In
page.tpl.php, you should find a line similar to the following:print $styles;The
$stylevariable contains all the style sheet import statements specified by Drupal and its modules, as well as the default style sheet (style.css) for the theme.However, these style sheets also specified in an array returned by the theme_add_style function.
Combined with the theme_stylesheet_import hook, you could replace the code above with the following:
A more complete solution
If, like me, you're a control freak when it comes to stylesheets, you won't want to use any default stylesheets unmodified. You also probably don't want to make modifications to the stylesheets provided with various modules directly, since you will either lose them or have to manually import them if you upgrade the module at any point. If, like me, you're also lazy, you won't want to keep opening your template.php file to add conditions checking for new stylesheets every time you add a new module.
Put all these requirements together, think about them for a few minutes, and you'll get a solution like this:
Just rememeber to set $theme_directory_name to the actual directory name(s) you use in your theme.
If you're using a solution like this, and you find you actually do want to use a module's stylesheet unmodified, you can (if you have ssh access to your *nix server) simply create a symlink inside your theme directory to the stylesheet you want to use. For example, if your theme is located at /sites/default/themeName, and the module stylesheet is located at /sites/default/modules/someModule/module.css, navigate to /sites/default/themeName and type the following:
ln -s ../modules/someModule/module.css module.css-b
-- b
Just a small addition.
Just a small addition. Change
$theme_directory_name = 'themeName';to$theme_directory_name = path_to_theme();and your theme directory and name will be located automatically. This will be more theme independent.Ups!
Ups! Forgot one! This line
$output = base_path() . 'sites/default/themes/' . $theme_directory_name . '/' . $temp_stylesheet;gets simpler and has to be changed as well, to$output = base_path() . $theme_directory_name . '/' . $temp_stylesheet;. No need to hardcode the theme location any more.This can get even better.
This can get even better. Not quite sure how to implement it though. May you can do it. Check for file_exists( myModuleCSSinMyThemeDirectory ) if true load it, if not load the default css file of the module it self. No need for symlinks. Just lay back and throw a custom css file in your theme directory if needed. If there exists one it will be loaded if not we use the modules default. This could be one for the handbooks.
New and Improved
Thanks for the API pointers and the idea--I didn't know about path_to_theme(). Here's a better version:
I dunno about the handbooks, I'd say the lack of something like this is a serious omission in the Drupal core--no doubt it would be possible to achieve much more efficiently in the core too.
In any case, the function is pretty much totally generic now though (as always) you'll have to change the first word of the function name to match the name of your theme.
-b
-- b
Thanks
Thank you so much, this works perfectly.
Regards,
Tanc
New and Improved
Does this script work in Drupal 5? Do I have to name the new stylesheets in a special way or place them in a certain sub folder? It's not working for me.
API Updated -- Override Style Sheet
This script uses some functions that are not part of Drupal 5. Drupal 5 added a new function, drupal_add_css(), that will allow you to add to the array of style sheets. Please refer to the handbook page 66122 for an example of how to add this to your page.tpl.php file. Using the drupal_add_css() function in a page.tpl.php file will allow you to call a style sheet and have it only apply to that particular page layout. If it is something that will be universal throughout your site, use the drupal_add_css() function in the template.php file
Here's an updated version...
<rant>This is still something that the Drupal core should be doing. The situation as it is now--even in version 5.x--is a major pain for theme developers and site-maintainers and, frankly, enforces poor css-coding practices. For example, if it's necessary/desirable to override the event module's default css in a theme, the only option available by default is to override event.css in the theme's stylesheet (i.e. since the module stylesheets should under no circumstances be edited directly--if the modules are ever updated, any such changes would be lost).
This results in a great number of duplicate css declarations being present in any theme where the various module defaults are, for one reason or another, unsuitable.
Furthermore, the default output of the css block in the document head is quite clueless--a separate style element for every single stylesheet?! What's wrong doing it this way:
... instead?
It could be argued that the default way provides a widely-compatible way of specifying the stylesheet's media, but since the default values are all "all", I'd think it would be better if the default were formatted as above and that theme authors could re-output them differently in the (less common) case where different media values are required.
I know that these defaults can be overridden--that's what this discussion is about--but as defaults, they're quite mediocre.
</rant>In any case, anyone that's just read through my rant should be rewarded, so here's a new function that can be used to force Drupal to use theme-specific stylesheets when they are available. It looks through all of the available stylesheets and checks to see if there is a similarly-named file available in the current theme directory. If there is, that file is used instead. So, for example, a stock Drupal site may have a call to the file "/sites/default/modules/event/event.css", but this script replaces that call with a call to "/sites/default/themes/{theme name}/styles/event/event.css" The function uses Drupal 5 API calls:
Add this function to your template.php file, and call it in page.tpl.php. The function processes one media type at a time, so you may need to run it more than once (this is probably not ideal, but I only discovered how media types were stored in the css array after I wrote the basic function...this was the easiest way to support other media types...) Sample code follows:
Tested and apparently working with Drupal 5.1.
-- b
Even Lazier
Warning: my method is not good!
I changed the webroot_themes_style_directory to not include the trailing "styles/" (note I left the trailing "/" before "/styles/" in the code, otherwise it won't work later on down the road.)
To make it work, I also changed the $stylesheet_location to be just the stylesheet filename.
Here's how I get just the stylesheet filename:
The reason I do this, is because I'm keeping my site fairly simple and just want to be able to see a filename, like "user.css", and copy it to my theme folder, call it "user.css" and have it override the default one.
This way I don't have to manage /styles/user/user.css or anything like that.
This is bad for many many reasons, not the least of which is the performance hit of turning each file path into an array to pop off the end. (There's also the danger of multiple .css files with the same filename but different paths.)
I just thought I'd point out how to be lazy with poor code :)
question about implementation
Sorry for the very simple question. hopefully this will get me a very simple answer.
;-)
This looks like a fantastic fix for something that I am trying to do - replace module style sheets with custom versions, *without* having to dig into them directly.
My only question is this: How do I implement this fix? I assume that I add the large php function into my theme's template.php file. Where do I call the function and what variables do I need to pass? Does it replace the 'print $styles;' call in the page.tpl.php file?
Again, I apologize and offer many thanks in advance.
PS: I am running drupal 6.9
Take care,
Finius
I just put up this handbook
I just put up this handbook on this and I think it's the most efficient. 2 approaches.. the first is best I think. It loads up modified module styles on top of the original when it's detected for a given page. Drupal 5 preprocesses the styles and loading the originals makes almost no difference. Browsers will only consider the last ruleset so it won't affect performance. There will be a bit more to load but it's not a big factor in *most* cases. It's better than stuffing a bunch of styles in a single location (style.css). Easy to track just by copying for example node.css from the modules folder to the styles directory in your theme named "mod-node.css" and placing just the stuff you want that's node related.
The other is to do exactly what you describe Tanc. Replace originals. I've used it a bit but replacing everything is usually a waste of time. Completely replacing the styles just means more to manage in the end since most themers will only override a fraction of the style rules but of course your milage may vary.
http://drupal.org/node/155400
An Even Cruder Solution
Hey, folks:
Maybe I'm taking an extreme position here, but I wanted to build a new theme more or less from scratch, and I was appalled at how hard Drupal makes this. Other CMSs (e.g. Joomla) are smart enough to check the theme/template directory for custom templates automatically, making overrides trivial - just put a file in the theme folder with the same name, and it gets used instead of the default. Drupal has a jaw-dropping quantity of presentational cruft coming from the core, and the workarounds all feel like ugly hacks. So I came up with one even uglier. This approach certainly isn't for everyone, but I found it pretty eye-opening. I ran the following Bash script inside Drupal's directory on a test site I didn't mind breaking - running it anywhere else would be disastrous:
Now I have a file called rescuedstyles.css with all of the CSS the core used to excrete - all 1,075 lines of it (!!), and the core modules put out nothing but global CSS resets. Of course, the site looks quite bare, and this probably isn't a practical solution for most folks, but now it's very easy for me to include or override whatever I want. When upgrading, I'll just run the same script on the new version.
This was addressed in Drupal 6
Note that Drupal 6 has its own way of doing this kind of override. See http://drupal.org/node/263967.