Adding CSS with Drupal right now is not straighforward and it's equally hard to override and force our CSS files as well (I don't want drupal.css nor event.css to load for instance).

So here's a patch to clean this up and make it super straightforward.

Modules simply use drupal_add_css('mycss.css', 'module'); to add their CSS. It's processed in correct order so that core CSS always comes first, then module CSS, then theme CSS. Right now to ensure this requires some crazy uses of theme_style_import() and what not. Hard to use and hard to explain how to use. This patch fixes that.

Not only that, but it allows for cool tricks like this:

  $css['core']['misc/drupal.css'] = '';
  print drupal_get_css($css);
   //print $styles

Instead of using the $styles var with *all* styles I load the new $css var, take out drupal.css file and then output it myself. Voila, no drupal.css. Makes it really easy to get rid of all *core* or all *module* CSS in one fell swoop too.

Too handy not to go in, this is the first step towards making theming a lot easier: super easy CSS and overridable CSS support.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Crell’s picture

Oh dear. Looks like you and I are tackling the same problem at the same time in two entirely different ways. :-)

http://drupal.org/node/73949

m3avrck’s picture

Crell, your patch seems to address the issue slightly, but isn't as comprehensive. It still doesn't fix the larger problem of overriding CSS and make sure the CSS files are loaded in a proper, overridable way. However, it is a nice patch for 4.7, but for the next release, I think an entirely new approach is a much better alternative.

Crell’s picture

How does it not allow for overriding? The patch I submitted allows a theme author to override a CSS file for a given module with no PHP code needed. As I recall, there was talk of using the exact same method for template files (split all theme files out to template files, then copy/paste/modify to create a theme).

eaton’s picture

After looking it over, this appears to be pretty flexible.

Am I correct in assuming that the real benefit here is consistency in function naming? IE, we don't have modules calling 'invisible' theme functions to do something that's not output related? Other than that, it seems to be a different way of doing what can be done now...

m3avrck’s picture

Yes, the biggest benefit is clear and consistent behavior, plus a simpler function call. It also solves the CSS mess.

Not only that, but it stores the list of CSS files in an array for easy manipulation.

Win-win for everyone :-)

eaton’s picture

I think you're right; maintaining things in a list of properly organized CSS files is a win, and moves things closer to the emerging Drupal standard: 'store stuff in structured arrays until the very last moment necessary.'

If I understand correctly, *previously* themes would override theme_get_style() and pull out stylesheets they didn't want included. With this patch, they would (in template.php, or on a page-by-page basis in page.tpl.php) just reach into the variables collection and do whatever they like to $css['core'][] or $css['module'][] arrays. Then, when the styles are output, those changes are reflected.

Is that correct? I think the new way makes more sense and avoids the strange 'use this theme function even though it doesn't output anything' effect. My only question is whether the benefits outweigh 'yet another task for module developers in their 4.7->4.8/5.0 conversion list.'

I lean towards yes.

rkerr’s picture

Wow, I didn't realize something as simple as adding a new CSS file was currently so complicated. This approach looks to be much more straight forward.

m3avrck’s picture

Jeff, you are correct, in page.tpl.php to get rid of drupal.css, all you need is:

  $css['core']['misc/drupal.css'] = '';
  print drupal_get_css($css);
  //print $styles ... don't need this anymore since we customized this list
kkaefer’s picture

*subscribe*

I am all for this.

kkaefer’s picture

I tested the patch. It works. +1 for this.

eaton’s picture

Status: Needs review » Reviewed & tested by the community

I dare to set this rtbc. It's a small change but one that can remove a lot of confusion and inconsistency.

Anonymous’s picture

w00t mr ted has done it again!

+154

:P

kkaefer’s picture

There is still an issue: theme_stylesheet_import() is called *before* any theme can override it, thus making the override function worthless. The creation of the HTML code should take place in drupal_get_css() rather than in drupal_add_css().

m3avrck’s picture

FileSize
5.83 KB

timcn, good catch. Updated patch to fix this, with even more flexibility as the array doesn't store the themed() CSS import (so easy to swap names of CSS files if need be).

merlinofchaos’s picture

This is awesome. +1.

rkerr’s picture

Rock on \m/ +1

m3avrck’s picture

FileSize
6.61 KB

Fixed missing $media variable and new 'module' default.

m3avrck’s picture

FileSize
6.66 KB

Forgot media again, good to go now.

m3avrck’s picture

FileSize
5.8 KB

Missing theme arg and cleaned up foreach loop.

m3avrck’s picture

FileSize
5.8 KB

Last one I swear :-)

m3avrck’s picture

FileSize
5.81 KB

Sorry I lied :-P

m3avrck’s picture

FileSize
5.81 KB

This is not my day.

eaton’s picture

I'm so never setting anything RTBC again. ;)

That said, the new approach to allow theming of the individual items is good.

kkaefer’s picture

Looks solid. I can't find any errors. Ready to commit!

Good job, m3avrck!

Crell’s picture

Usage question here.

Where is the "correct" place in a module, then, to queue up a CSS file? Does this fix the "dump it into hook_menu()" problem?

If a module adds a CSS file, then when overriding it do I need to know where the module is located, so I know its path?

The override example in #8 is PHPTemplate centric, if I understand it correctly. How would other themes handle it?

Are we going to properly slice and dice drupal.css finally? :-)

merlinofchaos’s picture

The proper place is either 1) when you know you need the CSS file or 2) in hook_menu if you want it to always be there.

Yes, this is a prelude to splitting up drupal.css

Crell’s picture

Would it work in hook_init()? And what about the override ability?

Generally a module has just one CSS file it tosses into the mix. Themes need to be able to easy cherry pick that list, regardless of where it's coming from.

m3avrck’s picture

Yes you can easily override CSS files, see the top post and this one

This patch is ready :-)

After this goes in: refactored CSS files :-D

Crell’s picture

I did look at those, which is why I'm asking. You have:

  $css['core']['misc/drupal.css'] = '';
  print drupal_get_css($css);
  //print $styles ... don't need this anymore since we customized this list

That is explicitly specifying the path to drupal.css, relative to drupal root. Does that mean that the code to disable the default location module CSS file (for instance) will be different if the location module is installed in /modules/ or /sites/all/modules/ or /sites/default/modules/? That's what I'm asking about.

If no, and you can override the incoming CSS regardless of where the module is, great. If you do have to know the path, however, then it means you can't make portable themes that override a module's CSS file, since they'd be dependent on each installation's path setup. That would be a show stopper.

I haven't worked with non-PHPTemplate themes enough (read: at all) to know if there is any impact on them. Someone else will have to chime in on that front.

(I'm not trying to be a stick-in-the-mud, really! :-) )

merlinofchaos’s picture

Assuming the file I'm looking for is SOMEPATH_I_DON'T_KNOW_ABOUT/foo.css and that is in the variable $path:

basename($path) == 'foo.css';

Thus:

if (basename($path) == 'foo.css') {
  $path = path_to_theme() . 'bar.css'; // cause I'd rather have bar than foo
}
m3avrck’s picture

Conversly, you can use

 $path = drupal_get_path('module', 'location') . 'location.css';
drumm’s picture

Status: Reviewed & tested by the community » Needs work

drupal_get_css(drupal_add_css())

- PHP code in default implementations of themeable functions should be as simple as possible, this isn't.
- This doesn't follow the convention of similar functions in Drupal.

I'm guessing the goal here is the ability to modify the CSS data structure before it gets to drupal_get_css(). I don't see that as necessary since there is theme_stylesheet_import() called later. But to maintain this functionality, I'm okay with keeping the argument with a default value of NULL that signals it should default to drupal_add_css().

rkerr’s picture

Status: Needs work » Needs review
FileSize
5.81 KB

Changed drupal_get_css($css) to drupal_get_css($css = NULL) and if $css is null, then $css = drupal_add_css() as drumm suggested.

How's this look now?

m3avrck’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me, that's a nice change I overlooked :-)

rkerr’s picture

Status: Reviewed & tested by the community » Needs work

Oops. Patch doesn't apply. I'll have to reroll... That's what you get for simply editing a patch file instead of making it properly! :)

rkerr’s picture

Status: Needs work » Needs review
FileSize
6.06 KB

Try this one.

merlinofchaos’s picture

+ RewriteBase /~rowan/drupal-cvs/HEAD/drupal

Probably not what you want in drupal =)

rkerr’s picture

FileSize
5.5 KB

Hmm yeah right :) ... removed that .htaccess chunk.

kkaefer’s picture

FileSize
7.04 KB

Several changes:

  • Moved the drupal_get_css() and drupal_add_css() functions to common.inc as they aren't theme functions anymore.
  • Removed the call to drupal_add_css in phptemplate.engine as it isn't necessary anymore.
  • Changed theme_get_styles() to drupal_get_css() in chameleon.theme (which has been forgotten).
Dries’s picture

In chameleon we have:

$output .= drupal_get_css();
$output .= theme('stylesheet_import', base_path() . path_to_theme() ."/common.css");

Is that correct? Looks like we're almost able to remove theme_stylesheet_import as well?

kkaefer’s picture

FileSize
7.92 KB

You're right. I remove the call to theme('stylesheet_import') in chameleon.theme and replaced it with drupal_add_css(..., 'theme');.

I also removed theme_stylesheet_import() and replaced the only remaining call to this function with the source code itself. Now that we can control what styles are added, we don't need to theme it anymore. We don't theme link and script tags either.

kkaefer’s picture

FileSize
8.02 KB

Added an (optional) to some arguments and explained what it does when no argument is given.

Dries’s picture

Status: Needs review » Needs work

In the code comments and PHPdoc some sentences end with a point, while others don't. Please try to make this consistent/professional. Otherwise, great work refining the documentation! Rock on. Given the documentation gets some additional love, this appears to be ready.

kkaefer’s picture

FileSize
8.2 KB

Changed the documentation a bit.

m3avrck’s picture

Status: Needs work » Reviewed & tested by the community

Changes look solid to me!

Can't wait to get this in so I can cook up my next CSS patch (hopefully next day or two). :-D

Dries’s picture

Status: Reviewed & tested by the community » Needs work

Committed to CVS HEAD. Marking this 'code needs work' until the upgrade instructions in the handbook have been updated. Set as 'fixed' once this is properly documented and communicated.

Good job, guys.

rkerr’s picture

Added very brief comments to the theme and module converting pages:
Converting 4.7 themes to HEAD
Converting 4.7 modules to HEAD

kkaefer’s picture

I wrote a patch to port the same system (core, module, theme) to JavaScript files: http://drupal.org/node/76637.

m3avrck’s picture

democritus-1’s picture

Which themes does this work in? Tested on? I'm using a customized blue theme
[mostly shopping cart and user membership changes, plus graphics.]
seattleonly.com

I followed the instructions: patch -p0 < css_13.patch

result- site unusable:

Fatal error: Call to undefined function: theme_add_style() in /virtual/seaonly/dev_site/modules/nice_menus/nice_menus.module on line 26

Thankfully I ran a backup before I patched. I now have to reload the entire site.

Thanks :)

Tobias Maier’s picture

democritus: this is a patch for the current drupal developer version NOT for drupal 4.7
You can't use this patch for drupal 4.7

it has no effects on you current site - only if you disabled drupal.css...

webchick’s picture

You don't have to reload the entire site. Just use the -R (reverse) argument to "unapply" a patch:

patch -p0 -R < patch_file.patch
Anonymous’s picture

Status: Fixed » Closed (fixed)