Identify unused CSS
Geoff Wagstaff / July 20, 2007

Recently finished a new page design? We all know how cluttered code can get when dealing with problems in the design and during the development process. Often, CSS class declarations that were written for elements that were removed from your html page are not removed from the CSS too, and remain as pointless entries in your CSS file, increasing its size and volume of clutter unnecessarily.
Therefore, I have written a small PHP script that uses several regular expressions to check the styles of a page you specify, and tell you which CSS classes remain in your CSS file(s) but are not ustilised in the HTML code. After being told these useless CSS entries, you can manually remove them from your CSS file(s). For now I will just post the code of this file up, so you can use it on your server if you wish. I would host it on the GoSquared server but this script is somewhat vulnerable to cross-site scripting attacks and the like, and has not been designed with security in mind. It is merely a tool. Therefore, I advise you to make sure only you can access this file on your server, and don’t make it public, if you use it on your server.
<?php /********************************************************************\ * GoSquared ltd. |css_checker.php * * Authors: Main code by Geoff http://www.gosquared.com * * Extended by Damian http://level0.ch/ * * Adapted to OOP by Geoff * * Date: 10/07 * * Version: 1.2.0 * *--------------------------------------------------------------------* * THIS SCRIPT IS OPEN SOURCE AND MAY BE USED BY ANYONE. SUBSEQUENTLY,* * THE AUTHORS ACCEPT NO RESPONSIBILITY FOR ITS USAGE NOR GURANTEE ITS* * RELIABILITY. NONE OF THE AUTHORS STATED ABOVE CAN BE HELD RESPONSI-* * BLE FOR PROBLEMS RESULTING FROM THE USAGE OF THIS SCRIPT * *********************************************************************/ class css_scanner{ public $helptext = "<p> <h3>Identify unused CSS</h3> Main code: <a href=\"http://www.gosquared.com/liquidicity/archives/203\">http://www.gosquared.com/liquidicity/archives/203</a> by Geoff<br /> Bug fix & more: <a href=\"http://level0.ch/fileserv/csschecker.php.html\">http://level0.ch/fileserv/csschecker.php.html</a> by Damian<br /> Further development, bug fixes and additions by Geoff<br /> <b>Usage:</b><br /> <ul> <li>http://www.myserver.com/csschecker.php?page=index.html</li> <li>http://www.myserver.com/csschecker.php?page=http://www.another-server.net/index.html</li> <li>http://www.myserver.com/csschecker.php?page=index.html,about.html,download.html</li> <li>http://www.myserver.com/csschecker.php?page=index.html&visual=inline</li> <li>http://www.myserver.com/csschecker.php?page=index.html&visual=iframe</li> </ul><br /> CSS Scanner will look for and scan any embedded or external CSS files in your HTML code. </p>"; public $page_content = ''; public $unused = array(); public $multi_page; public $page; public $page_content_visual; function __construct($page){ //$multi_css = explode(',', $css); if(empty($page)){ echo "No page specified to scan" . $this->helptext; exit; } $this->multi_page = explode(',', $page); //$css_content = ''; if(count($this->multi_page) > 1){ $i = 0; $this->page_content = array(); foreach ($this->multi_page as $page) { $this->page_content[$i] = file_get_contents($page); $i++; } } else{ //$css_content .= file_get_contents($css); $this->page = $page; $this->page_content = file_get_contents($page); } if(is_array($this->page_content)){ $i=0; foreach($this->page_content as $page_content){ $this->page = $this->multi_page[$i]; $this->page_content_visual = $page_content; $this->unused = $this->scan($page_content); $this->print_report(); $i++; } } else{ $this->page_content_visual = $this->page_content; $this->unused = $this->scan($this->page_content); $this->print_report(); } } function scan($page_content){ $i = 0; if(ereg("<style( *[\n]*.*)>\n*(.\n*)*<\/style>", $page_content)){ if(preg_match_all("/(@\s*import\s* (url((\"|')?)?((\"|')?)|(\"|'){1}).+(\"|')?\)?)/", $page_content, $ext_stylesheets)){ foreach($ext_stylesheets[0] as $stylesheet){ $css_content[$i] = preg_replace("/(@\s*import\s*)|(url\(?((\"|')?))|(\"|'){1}|\)?(\"|')?;|(\s)/", "", $stylesheet); $i++; } $array = 1; } $inline_notused = $this->check_file($page_content, $page_content); } else die("No page styles, sorry!".$this->helptext); $unused = array('internal' => array(), 'external' => array()); if(isset($array) && $array == 1){ foreach($css_content as $css_file){ $css = file_get_contents($css_file); if(!empty($css)){ $not_used = $this->check_file($css, $page_content); array_push($unused['external'], array('css_file' => $css_file, 'external' => $not_used)); } } } if($inline_notused != false){ $unused['internal'] = $inline_notused; } return $unused; } function print_report(){ echo "<h1>Report for page: ".$this->page.'</h1>'; foreach($this->unused['external'] as $unused){ echo 'CSS entries not used from external CSS file '.$unused['css_file'].':<br />'; if(count($unused['external']) > 0){ foreach($unused['external'] as $entry){ echo $entry.'<br />'; } }else echo 'None, good job!'; echo '<br /><br />'; } if(count($this->unused['internal'] > 0)){ echo 'CSS entries not used from internal CSS code block:<br />'; foreach($this->unused['internal'] as $entry){ echo $entry.'<br />'; } echo '<br /><br />'; } if(isset($_GET["visual"]) && $_GET['visual'] == 'iframe'){ echo "<hr><iframe src = \"$this->page\" width=\"100%\" height=\"100%\"></iframe>"; } elseif(isset($_GET["visual"]) && $_GET['visual'] == 'inline'){ echo "<hr>", $this->page_content_visual; } } function check_file($css, $page_content){ preg_match_all("/\.([a-zA-Z-_][a-zA-Z0-9-_]+)({|[\s].*{)/", $css, $css_classes); preg_match_all("/#([a-zA-Z-_][a-zA-Z0-9-_]+)({|[\s][^;]*{)/", $css, $css_ids ); preg_match_all("/<[a-zA-z0-9][^>]*class\s*=\s*[\"'](.+?)[\"']/", $page_content, $page_classes); preg_match_all("/<[a-zA-z0-9][^>]*id\s*=\s*[\"'](.+?)[\"']/", $page_content, $page_ids); $clean_page_classes = array(); $clean_page_ids = array(); foreach($page_classes[1] as $s) { $classes = explode(" ", $s); foreach($classes as $class) { array_push($clean_page_classes, $class); } } foreach($page_ids[1] as $s) { $ids = explode(" ", $s); foreach($ids as $id) { array_push($clean_page_ids, $id); } } $not_used = array(); foreach($css_classes[1] as $css){ if(!in_array($css, $clean_page_classes) && !in_array('.'.$css, $not_used)){ array_push($not_used, '.'.$css); } } foreach($css_ids[1] as $css){ if(!in_array($css, $clean_page_ids) && !in_array('#'.$css, $not_used)){ array_push($not_used, '#'.$css); } } return (count($not_used) > 0) ? $not_used : false; } } $scanner = new css_scanner($_GET['page']); ?>
Feel free to test it and suggest improvements.
USAGE: You can use this script in a couple of ways. In the querystring, you can do: http://www.yoursite.com/csschecker.php?page=url_to_page.html
The script will attempt to detect styles that are imported into your page. The script will use external css files included using
<style>@import "path to css file";</style>
or similar;
As I said, do not make this script public on your server. Use it as your own private tool. You have been warned!
This script is free. Use at your own risk.
—
GoSquared cannot be held responsible for any problems that may be caused through the usage of this script.





Lukas said,
July 21, 2007 at 12:29 ()
Well It's not useful for me, because I use Microsoft Expression Web to code my websites and this app has teh function to remove unused css.
Good job anyway
Ausome1 said,
July 21, 2007 at 15:22 ()
This is some useful code for some. You should also plug this out on http://2020code.com and get some votes for it.
Adilson said,
July 31, 2007 at 23:07 ()
Great script!
Work fine here, using it to check my css in ASP pages.
Thanks
eric said,
August 21, 2007 at 02:58 ()
it doesn't function for php built sites. tried it and it just came up blank when using index.php for page=
echo said,
September 12, 2007 at 19:37 ()
eric,
In response to your comment, have you tried the full URL path to the file you want to check, so http://www.yoursitesurl.com/index.php in the page= bit of the query string. This should work.
Thanks,
Geoff
Damian said,
September 13, 2007 at 22:55 ()
I found your code very useful, thanks a lot.
When using the script, I found that some cases were not treated correctly:
- only the first class / id in the css definition is taken into account
- multiple class names in the same class attribute were not recognised (resulting in "false positives")
- identical class / id will not report even if unused
(I have the feeling I forgot another issue, but I can't remember...)
So I went ahead, corrected these bugs, and added some more features:
- output prefixed with "." respectively "#" to distinguish classes and id's
- support for multiple html / css pages (as in larger projects) added, use a comma-separated list as "page" parameter
- added an optional parameter "visual" which will make the script print the evaluated css and html to the browser window.
The part which looks for CSS definition in the header was left unchanged.
http://level0.ch/fileserv/csschecker.php.html
echo said,
September 13, 2007 at 23:20 ()
Damian,
This is some excellent work, thanks. As for the problems/bugs you reported, the script wasn't originally designed to deal with those cases, however I probably should have made it so it would :)
I shall be posting the improved version of the code as soon as I have time.
Thanks,
Geoff
Damian said,
September 15, 2007 at 11:50 ()
Thanks for the compliments, but without your script I would not have known where to start. Using this basis, it was only finetuning and fiddling with regexp's to complete it.
By the way, just in case you've already copied the code I posted two days ago, there was a small bug in the regexp preventing it from finding all the classes. This is fixed now.
As for a future version, one could integrate support for link type css definition
and probably its quite easy now to evaluate also inline css definitions. But as I won't use this functionality, I end the development here.
We are very similar after all - if we don't need the functionality, we won't implement it ;)
Damian said,
September 15, 2007 at 11:59 ()
I wanted to illustrate the link type css definition with the corresponding code, but it was stripped by the blog software. Lets see if this works:
<link rel="stylesheet" type="text/css" href="style.css"/>
Des said,
November 19, 2007 at 23:05 ()
Worked great for me. Thanks, guys.
Marius said,
December 3, 2007 at 15:07 ()
Nice idea, exactly what I was looking for. But all it does for me is printing out my external css-file. No comment like "CSS entries not used from" or similar.
Marius said,
December 3, 2007 at 15:12 ()
Damian Version (Post #7) works fine for me!
Thanks.
xxcemil said,
June 2, 2008 at 15:08 ()
about css parameters
http://css-lessons.ucoz.com/css-parameters.htm
html dersi said,
June 12, 2008 at 19:32 ()
HI i need your help i really want to create my own website/web page but i dont know how to go about doing it so can you please help me outhh
Venkadesan Tharshan said,
July 18, 2008 at 04:30 ()
thanks.i used it on my website.
MadTurki said,
September 30, 2008 at 20:33 ()
That's brilliant work...
------------------------------
Lukas said,
July 21, 2007 at 12:29 pm ()
Well It's not useful for me, because I use Microsoft Expression Web to code my websites and this app has teh function to remove unused css.
Good job anyway
------------------------------
If you're using a MS product to code your sites you've got more to worry about than a straggling CSS element...
Ebrahim said,
February 26, 2009 at 14:11 ()
Hi,
Thanks for the excellent script, but I can't get it to work. Errors:
Notice: Undefined index: css in /Volumes/disk2/www/css_redunancy_check.php on line 49
Notice: Undefined variable: css_content in /Volumes/disk2/www/css_redunancy_check.php on line 55
Notice: Undefined variable: page_content in /Volumes/disk2/www/css_redunancy_check.php on line 58
I'm using Damian's code.
Thanks
Geoff said,
March 2, 2009 at 21:59 ()
Ebrahim, please ensure you are supplying the appropriate (and required) variables in the querystring. Check the help message inside the script for more information, or reply back here if you're still having problems.
Henry Cons said,
November 29, 2009 at 13:32 ()
nice idea
ken said,
January 20, 2010 at 17:17 ()
sounds interesting though. I'll check it later. All my css is full of junk. Thanks.
Adam said,
March 22, 2010 at 05:01 ()
does this code work? i've been looking for a script like this. i guess i'll give it a whirl!
Flak said,
December 9, 2010 at 19:17 ()
Well : php 5.3 says:
Deprecated: Function ereg() is deprecated in scan.php on line 82
vlad said,
January 18, 2011 at 17:11 ()
Hey!
This is exactly what I'm looking for right now.
Any updates on the code?
I can't access Damian's code (403 error) and I can't tell if the code in the blog post is updated or not :)
That being said, I will test it when I get home and provide feedback.
Trackbacks said,
February 4, 2012 at 02:37 ()