This little blog has been getting a lot of coverage lately thanks to a write up by Ajaxian. Developing with Symfony is great and always gives you a lot to think and write about.
For my new product I was having a CSS conflict. This tends to happen when you include your own html and css into someone else’s website. For instance if you have a widget as such:
html
<div id="mywidget">
<h1>My hello world widget</h1>
</div>
css
H1 {
color:green;
font-size:20px;
}
The solution to this problem is quite straightforward, you simply specify your css selector as div#mywidget H1. However, what if you want to allow people to customize the looks of your widget. Now you could off course ask them to include the div#mywidget part, but chances are this will give problems.
Since I was already using the great sfCombineFilterPlugin an easy solution was available. (If you didn’t use the sfCombineFilterPlugin yet, go check it out immediately. Also have a look at Yahoo’s Yslow)
The sfCombineFilterPlugin uses php to gzip, minify and cache your css and javascript. Here is how to extend that behavior to include the #mywidget specification. (Assuming you have sfCombineFilter installed)
Step 1: open your .htaccess
Just below the RewriteBase instruction add:
# if we are retrieving javascript or css
RewriteRule ^css/packed/prepend/(.*\.css) /sfCombineFilterPlugin/combine.php?type=css&prepend=1&files=$1
RewriteRule ^css/packed/(.*\.css) /sfCombineFilterPlugin/combine.php?type=css&files=$1
RewriteRule ^js/packed/(.*\.js) /sfCombineFilterPlugin/combine.php?type=javascript&files=$1
Step 2: add this class to the top of web/sfCombineFilter/combine.php
Partly based on CSS parser class.
class prependCss { public static function prependCssString($str) { // Remove comments $str = preg_replace("//*(.*)?*//Usi", "", $str); $parts = explode("}",$str); if(count($parts) > 0) { foreach($parts as $part) { list($keystr,$codestr) = explode("{",$part); $keys = explode(",",trim($keystr)); $newkeys = array(); if(count($keys) > 0) { foreach($keys as $key) { if(strlen($key) > 0) { $key = (!strstr($key, '#mywidget')) ? '#mywidget'.$key : $key; $newkeys[] = $key; } } $keystr = implode(', ',$newkeys); } if(!empty($keystr)) //needed for spaces behind last } $rules[] = $keystr . " {" . $codestr . "}"; } $prependedCss = implode("n", $rules); } // return $prependedCss; } public static function prependCssFile($filename) { if(file_exists($filename)) { return self::prependCssString(file_get_contents($filename)); } else { return false; } } }
Step 3: hack around in combine.php
below $minify_js add:
if($_GET['prepend']==1)
$prepend = true;
change the stuff below this comment to:
// Get contents of the files
$contents = '';
reset($elements);
foreach ($files as $path) {
if($prepend && $_GET['type'] == 'css') {
$contents .= "\n\n" . prependCss::prependCssFile($path);
} else {
$contents .= "\n\n" . file_get_contents($path);
}
}
And finally just change your urls to css/packed/prepend/yourcss.css (if you are using relative paths in your css you might need to add an ../)
Conclusion
Using this technique your css will load without any problems in third party sites. This comes in very useful when creating widgets or greasemonkey scripts.