Geoff W / July 20, 2007

Identify unused CSS

Unused CSS

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.

  Filed Under: Code, Design, Technology, liquidicity
Add to StumbleUpon | Add to del.icio.us | Digg this Page

18 Responses so far

  1. 1

    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

  2. 2

    Ausome1 said,

    July 21, 2007 at 3:22 pm ()

    This is some useful code for some. You should also plug this out on http://2020code.com and get some votes for it.

  3. 3

    Adilson said,

    July 31, 2007 at 11:07 pm ()

    Great script!

    Work fine here, using it to check my css in ASP pages.

    Thanks

  4. 4

    eric said,

    August 21, 2007 at 2:58 am ()

    it doesn't function for php built sites. tried it and it just came up blank when using index.php for page=

  5. 5

    liquidicity » Some people just (don’t) get interfaces… said,

    September 11, 2007 at 7:31 pm ()

    [...] a piece of the effort you put in, and share it with everyone. For example, when we wanted to neaten up some CSS files, we built a PHP script to help us out. Shortly after a bit of testing and adapting, we decided to [...]

  6. 6

    echo said,

    September 12, 2007 at 7:37 pm (Administrator)

    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

  7. 7

    Damian said,

    September 13, 2007 at 10:55 pm ()

    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

  8. 8

    echo said,

    September 13, 2007 at 11:20 pm (Administrator)

    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

  9. 9

    Damian said,

    September 15, 2007 at 11:50 am ()

    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 ;)

  10. 10

    Damian said,

    September 15, 2007 at 11:59 am ()

    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"/>

  11. 11

    Des said,

    November 19, 2007 at 11:05 pm ()

    Worked great for me. Thanks, guys.

  12. 12

    Marius said,

    December 3, 2007 at 3:07 pm ()

    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.

  13. 13

    Marius said,

    December 3, 2007 at 3:12 pm ()

    Damian Version (Post #7) works fine for me!

    Thanks.

  14. 14

    Liens de décembre 2007 | SkyMinds.Net said,

    December 31, 2007 at 8:22 am ()

    [...] Identify unused CSS. [...]

  15. 15

    xxcemil said,

    June 2, 2008 at 3:08 pm ()

    about css parameters
    http://css-lessons.ucoz.com/css-parameters.htm

  16. 16

    html dersi said,

    June 12, 2008 at 7:32 pm ()

    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

  17. 17

    Venkadesan Tharshan said,

    July 18, 2008 at 4:30 am ()

    thanks.i used it on my website.

  18. 18

    MadTurki said,

    September 30, 2008 at 8:33 pm ()

    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...

Follow Comments on this Post with RSS · TrackBack URI

Have Your Say