<!DOCTYPE html>
<!-- ========================================== kroc camen of camen design ============================================= -->
<title>code · Targeting Browsers With CSS Using mod_rewrite</title>
<link rel="stylesheet" type="text/css" href="/design/design.css" />
<meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=no" />
<link rel="alternate" type="application/rss+xml" href="/code/rss" title="Just code" />
<link rel="canonical" href="/code/mod_rewrite-css-targeting" />
<!-- =================================================================================================================== -->
<header>
	<h1><a href="/" rel="index">
		Camen Design
	</a></h1>
	<nav><ul>
		<li><a href="/">all</a></li>
		<li><a href="/projects">projects</a></li>
		<li><a href="http://forum.camendesign.com">forum</a></li>
	</ul><ul>
		<li><a href="/quote/">quote</a></li>
		<li><a href="/writing/">writing</a></li>
		<li><a href="/blog/">blog</a></li>
		<li><a href="/photo/">photo</a></li>
		<li><a href="/code/" rel="tag">code</a></li>
		<li><a href="/art/">art</a></li>
		<li><a href="/link/">link</a></li>
		<li><a href="/poem/">poem</a></li>
		<li><a href="/audio/">audio</a></li>
	</ul><ul>
		<li><a href="/web-dev/">web-dev</a></li>
		<li><a href="/annoyances/">annoyances</a></li>
		<li><a href="/eve/">eve</a></li>
		<li><a href="/code-is-art/">code-is-art</a></li>
		<li><a href="/inspiration/">inspiration</a></li>
		<li><a href="/windows/">windows</a></li>
		<li><a href="/gift/">gift</a></li>
		<li><a href="/gaming/">gaming</a></li>
		<li><a href="/mac/">mac</a></li>
		<li><a href="/osnews/">osnews</a></li>
		<li><a href="/c64/">c64</a></li>
		<li><a href="/linux/">linux</a></li>
	</ul>
	<a rel="previous" href="/code/how_to_learn_html5">
		older article →
	</a><a rel="next" href="/code/uth6_beautiful_css_dates">
		← newer article
	</a></nav>
</header>
<!-- =================================================================================================================== -->
<article><header>
	<!-- date published or updated -->
	<time pubdate datetime="2008-12-16T10:17:00+00:00">
		<sup>10:17<abbr>am</abbr> • 2008</sup>
		<abbr title="December">Dec</abbr> 16
	</time>
	<!-- categories -->
	<ul>
		<li><a href="/code/mod_rewrite-css-targeting" rel="bookmark tag">code</a></li>
		<li><a href="/web-dev/mod_rewrite-css-targeting">web-dev</a></li>
		<li><a href="/code-is-art/mod_rewrite-css-targeting">code-is-art</a></li>
	</ul>
	<!-- licence -->
	<small>
		<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_GB">c</a>
		share + remix
	</small>
</header>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<section>
<h1>Targeting Browsers With CSS Using mod_rewrite</h1>


<h2 id="targeting-need">Ⅰ. the Need for CSS-Hacks</h2>
<p>
	<strong>In a world</strong> of imperfect browsers, the CSS-hack can be both a beautiful thing, and also an ugly
	thing too.
</p><p>
	It is unavoidable that at some point some feature or design you wish to implement hits a limitation or parsing /
	rendering bug of one (or more) browsers you are supporting.
</p><p>
	If this has never happened to you; well done—you’re an IE only web-developer who’s never stepped outside of
	Microsoft software, nor table-based design. You’re probably confused by the term <q>CSS</q>.
</p><p>
	If, however, you do not own that privileged position of ignorance, everybody comes across the need to target a
	particular bit of CSS to a particular browser.
</p>


<h2 id="targeting-maintenance">Ⅱ. the Question of Maintenance</h2>
<p>
	There are <a href="http://24ways.org/2008/the-ie6-equation" rel="external">many</a>
	<a href="http://meiert.com/en/blog/20081209/re-the-ie-6-equation/" rel="external">arguments</a> about if CSS-hacks
	should be totally avoided, embraced, or properly managed somehow.
</p><p>
	Firstly, to think they can be avoided is naïve. I have a website that only supports Acid2 compliant browsers, uses
	CSS3 liberally, and doesn’t support IE one iota—and still I have CSS-hacks to deal with browser idiosyncrasies.
</p><p>
	The next two lines of argument are either a) just embed hacks in your normal document—it’s less maintenance to
	only edit one document and b) keep hacks to a separate file where they can be managed individually, ideally using
	conditional comments to target IE specifically or Javascript to correct behaviour.
</p><p>
	Embedding CSS-hacks in stylesheets increases maintenance by making them an integral part of testing in all browsers.
	CSS designed to correct one browser may have undesired effects on another browser. Or a hack that has been used
	before, may no longer work on a newer version of a browser, but now you have to scramble to somehow support both
	browsers.
</p>
<blockquote>
	<p>
		A hack is anything that has the potential to fail disastrously<br />
		each time something changes expectedly.
	</p>
	<cite>Kroc Camen</cite>
</blockquote>
<p>
	Having to make corrections to your whole stylesheet to please one browser, is unnecessary maintenance. Having all
	your failure points in one document is not less maintenance. I have not very fond memories of having to rewrite
	large portions of a stylesheet because of being unable to please one browser without effecting
	<small><ins>sic</ins></small> others.
</p><p>
	The answer therefore is to tie CSS-hacks—or at least alternative CSS—directly to the browser it fixes, so that
	it does not becoming a ticking time bomb for other browsers and future versions.
</p><p>
	In the case of IE, conditional comments give very precise targeting capabilities. Conditional comments are an
	excellent solution for applying alternative CSS, rather than relying on CSS-hacks preying on broken parsing.
	However, there are a couple of let-downs:
</p>
<ul>
	<li>IE Only</li>
	<li>Conditional Comments rest inside the HTML</li>
</ul>
<p>
	Having to maintain HTML because of a browser, is like suddenly waking up one day to discover that the letter Z has
	been officially depreciated by the European Union and now you have to update all your documents to conform.
</p>
<blockquote>
	<p>
		Your HTML, like diamonds, should be forever.
	</p>
	<cite>Kroc Camen</cite>
</blockquote>
<p>
	Lastly, using a Javascript fix is only increasing the length of thread you have to follow when something breaks. If
	suddenly you find that a CSS property you apply does not have the expected behaviour in IE, but gives different
	results with the Javascript fix on, and then off: you now have a maintenance nightmare that involves back-tracking
	through ‘x’-tons of Javascript, that likely, you didn’t write yourself.
</p><p>
	What if a new version of a browser comes out and your Javascript fix doesn’t work with that browser, but also, the
	site design won’t render correctly without it? Now you’re forced to either remove the Javascript and do the CSS
	again, properly, from scratch; or update the Javascript with all the work that entails.
	<br /><br />
	A solution is needed that:
</p>
<ol>
	<li>Works for all browsers and does not rely on browser-specific capabilities to implement</li>
	<li>Negates the need for CSS-hacks, and instead encourages alternative CSS</li>
	<li>Targets browsers/versions specifically or according to a range</li>
	<li>Does not fail when a new browser or version arrives on the scene</li>
</ol>


<h2>Ⅲ. the Answer Is Still Targeting</h2>
<p>
	We all know that user-agent targeting is bad. It defeats the purpose of having standards to work across all
	browsers. However, thus far, we have, by proxy, ascertained that there are two types of CSS:
</p>
<ul>
	<li>CSS that works in all browsers</li>
	<li>CSS that does not work in all browsers</li>
</ul>
<p>
	The maintenance problems so far outlined are because there is no clean separation of these two—except for IE
	conditional comments, with the acknowledgement that they are an incomplete solution.
</p><p>
	We have also ascertained that browser-specific CSS is eventually required given imperfect browsers. You can either
	choose to compromise on your design to avoid this, or compromise on the browser and keep the design. Either way, the
	end-user should <em>never</em> be compromised on.
</p><p>
	It’s important to state that writing standards-compliant, proper CSS is always desired. Legacy browsers are
	exactly that: legacy. Therefore a solution to dividing the two types of CSS must primarily acknowledge that proper
	CSS comes first and does not require any maintenance going forward. The current solutions mentioned earlier do not
	give weight to the better CSS over the bad CSS. Basically, with a perfect solution, a perfect browser would never
	parse any hack or CSS intended for only one browser—it would be ignorant of legacy browsers.
</p><p>
	The important thing with User-Agent targeting is the ability to also <em>not</em> target user-agents. Some believe
	user-agent targeting to be a form of quicksand that has you chasing after every browser ever made.
</p>
<blockquote>
	<p>
		A browser that applies the standards, and requires no browser-specific CSS to render the design, does not
		need to be targeted. No solution is actually needed.
	</p>
	<cite>Kroc Camen</cite>
</blockquote>
<p>
	My proposal is therefore a solution only for legacy browsers. If a browser does not need browser-specific CSS to get
	it to render a design right, then it requires zero maintenance on part of this solution. There is no
	<em>interference</em> by the solution to browsers that do not need it.
</p>


<h2>The Solution</h2>
<p>
	Apache’s mod_rewrite module allows us to take a file requested by the browser, and provide a different file
	instead, based on some matching or non-matching criteria.
</p><p>
	What if we had a “fixes.css” file that was different for each browser that <em>needs</em> to be targeted, but
	void by default so that any browser that doesn’t need fixes, doesn’t get any?
	<br /><br />
	We start with two stylesheets declared in the HTML <code>head</code> element:
</p>

<pre><code>&lt;link rel="stylesheet" type="text/css" href="css/main.css" /&gt;
&lt;link rel="stylesheet" type="text/css" href="css/fixes.css" /&gt;</code></pre>

<p>
	You write your main stylesheet using <strong>absolutely no CSS-hacks or browser-specific workarounds</strong>.
	<em>Write for the future</em>. As more and more browsers come on board with Acid 2 / 3, CSS3 compatibility
	<abbr title="et cetera">&amp;c.</abbr> they should automatically fall into working with your site—zero maintenance
	needed.
</p><p>
	Remember, this mod_rewrite solution is only to bring non-complaint browsers into spec. Ideally, for example, you’d
	write your stylesheet to meet Acid 2 capabilities and when IE8 is released the site <q>just works</q>, and the IE7
	workarounds stay with IE7.
	<br /><br />
	The actual ‘fixes.css’ file itself is, therefore, completely blank.
</p><p>
	In your ‘.htaccess’ file use the following two lines to target a browser and rewrite the ‘fixes.css’ file to
	a browser-specific version: (IE7 in this example)
</p><p>
	<small>(Assuming you’ve already got “<code>RewriteEngine on</code>” in your ‘.htaccess’ file and
	“<a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#RewriteBase" rel="external"><code>RewriteBase</code></a>”
	if needed.)</small>
</p>

<pre><code>RewriteCond %{HTTP_USER_AGENT} MSIE\s7\.(?!.*Opera)
RewriteRule ^(.*fixes)\.css$ /$1.ie7.css [L]</code></pre>

<p>
	This selects a user-agent containing <q>MSIE 7.</q> as long as it’s not followed by <q>Opera</q> at some point (to
	ignore Opera spoofing as IE). The ‘fixes.css’ file is then rewritten to a ‘fixes.ie7.css’ file where you may
	store the IE7 specific fixes you need to apply.
	<br /><br />
	Now users of IE7 will receive a different ‘fixes.css’ than users of any other browser.
</p><p>
	It’s a bit like having a <a href="http://userstyles.org" rel="external">user style</a> on your own website. You
	can apply a CSS “patch” to the site for specific browsers, keeping your main stylesheet legacy free and
	future-facing.
</p>


<h2>Potential Uses</h2>
<ul>
	<li>
		<p>
			Use <code>@font-face</code> to render titles using embedded fonts, but fall back to images on
			unsupported browsers
		</p>
	</li><li>
		<p>
			Use <code>box-shadow</code> and <code>text-shadow</code> effects in the main stylesheet, but
			fall back to transparent PNGs or generated content to replicate the effect in older browsers
		</p>
	</li><li>
		<p>
			Provide a “lo-fi” design for legacy browsers (try viewing
			<a href="http://www.stuffandnonsense.co.uk/index.php/" rel="external">this site</a> in a good
			browser, and then in IE6)
		</p>
	</li>
</ul>
<p>
	In short, old browsers shouldn’t prevent you from using the latest and greatest that up-to-date browsers offer.
	It’s up to you to keep legacy where it belongs.
</p>


<h2>Addressing Concerns</h2>
<p>
	<em>“Aha!”</em>, I hear you say. <em>“But surely this creates more maintenance, now we have many CSS
	files!”</em>
</p><p>
	With this solution, when you make a browser-specific correction, you only need to test in that browser itself. Less
	maintenance. Without this solution, a browser-specific fix also gets parsed by all browsers, and you need to test
	them as well. More maintenance.
	<br /><br />
	<em>“But now you have to maintain the ‘.htaccess’ file!”</em>
</p><p>
	As browsers get more capable, the less browser-specific CSS you will need to target, if at all. An old browser
	doesn’t stop being an old browser. As long as you’re not changing the HTML, this solution can safely bit-rot
	without ever being looked at.
</p><p>
	This solution also allows you to react smoothly and quickly to an unexpected regression between versions of
	browsers. Imagine you try out a beta browser like <a href="http://www.opera.com/browser/next/" rel="external">Opera
	10</a> or <a href="http://www.mozilla.com/en-US/firefox/all-beta.html" rel="external">Firefox 3.1</a> and you
	suddenly find your design breaks with it. You can—without touching a line of your existing main stylesheet—add
	two lines to the ‘.htaccess’ file to detect the new browser, and then develop, on a new sheet, the necessary
	fixes without compromising any of the existing CSS. You can then choose to leave that as is, or then once working
	roll it back into the main sheet. This is significantly less panic-prone than having to apply fixes for an
	unreleased browser to a live, perfectly working stylesheet.
</p><p>
	Imagine you want to stop supporting a legacy browser. With this method, you remove the two lines in the
	‘.htaccess’ file, delete the specific ‘fixes.css’ file and that’s it—browser unsupported. Without this
	solution, you would have to manually unpick the CSS-hacks from your main stylesheet and slowly rub-out any influence
	that browser had on the design of the CSS. Sometimes the only way to truly remove a browser’s influence from a
	large bit of CSS is to simply rewrite it (due to all the extra capabilities opened up with newer versions that you
	should now be making use of).
	<br /><br />
	<em>“What about people/browsers spoofing user-agents?”</em>
</p><p>
	Not a problem. Seriously. Writing the right regex to spot lame attempts at spoofing is not difficult. Many browsers
	add <q>like Gecko / khtml</q> despite this being patently false. It’s becoming more common even to add
	<q>Firefox</q> into Gecko browsers given Firefox’s weight in the market. It’s sad, really, that some have given
	in to this pathetic behaviour.
</p><p>
	If you’re literally browsing with a completely false user-agent <em>by default</em>, you are a) 0.01% of the
	population and b) have a personality disorder, or something. Spoofing has its uses, but it’s legacy as well and
	should be sent the way of the dodo (if browser vendors stop with this ridiculous behaviour of saying they’re
	engines they are nothing like).
	<br /><br />
	In other words, the answer is: <a href="http://www.regular-expressions.info/" rel="external">learn regex</a>.
</p>


<h2>The HTTP-Request Tax</h2>
<p>
	I hate wasting HTTP-Requests. If you want a fast site, it’s not bandwidth or file size that matters; no, it’s
	the number of HTTP-Requests. Each one could have potential delays up to 1–2 seconds in worse cases.
</p><p>
	So there’s a severe flaw with this solution, the browser has to fetch two CSS files, even if the browser doesn’t
	need any fixes. This is a tax on good behaviour!
</p><p>
	There is a workaround for this that presents only one stylesheet to compliant browsers, but two stylesheets to those
	browsers that need fixes.
	<br /><br />
	If you present just one stylesheet in the header:
</p>

<pre><code>&lt;link rel="stylesheet" type="text/css" href="css/main.css" /&gt;</code></pre>

<p>
	And then, in the ‘.htaccess’ file, redirect ‘main.css’ to the fix sheets, for those legacy browsers:
</p>

<pre><code>RewriteCond %{HTTP_USER_AGENT} MSIE\s7\.(?!.*Opera)
RewriteRule ^(.*)main\.css$ /$1fixes.ie7.css [L]
# ...

# after legacy fixes, redirect ‘main.css’ to the actual main sheet ‘_main.css’
RewriteRule ^(.*)(?&lt;!_)main\.css$ /$1_main.css [L]</code></pre>

<p>
	And at the top of each fixes sheet, just simply import the main stylesheet.<br />
	(The main sheet uses a dummy name to prevent the <code>@import</code> from getting stuck in an infinite loop.)
</p>

<pre><code>@import "_main.css";
/* browser specific fixes here... */</code></pre>

<p>
	This way, a browser that needs correction gets the fixes sheet first (with the main stylesheet imported at the top)
	and a browser that doesn’t need any fixes just gets the main sheet alone.
</p>


<h2>Example Reference</h2>
<p>
	For those with worn C and V keys, here’s some additional rewrite conditions to select particular browsers (Add the
	<code>RewriteRule</code> according to your chosen method). If you’ve any suggestions for good examples, do
	<a href="mailto:kroc@camendesign.com">send them my way</a>
</p><p>
	If you want some sample user-agent strings to work out the right regex needed for a particular browser,
	<a href="http://www.useragentstring.com/pages/useragentstring.php" rel="external">here’s</a> a good list.
</p>


<h3>Internet Explorer (Any Version):</h3>

<pre><code>RewriteCond %{HTTP_USER_AGENT} MSIE\s\d\.(?!.*Opera)</code></pre>

<p>
	Change <q><code>\d</code></q> to any number to select a particular version
</p>


<h3>Internet Explorer, Version ‘X’ and Below:</h3>

<pre><code>RewriteCond %{HTTP_USER_AGENT} MSIE\s(\d)\.(?!.*Opera)
RewriteCond %1 &lt;=7</code></pre>

<p>
	Change the <q><code>&lt;=</code></q> to <code>&gt;=</code> to target a particular version and above.
</p>


<h3>Firefox, Versions Before 3.0:</h3>

<pre><code>RewriteCond %{HTTP_USER_AGENT} Gecko/(\d{8})(?!.*Opera)
RewriteCond %1 &lt;20080529</code></pre>

<p>
	<strong>Do not ever try detect Firefox using “Firefox”</strong>. Remember that Gecko, the rendering engine that
	powers Firefox is used in many other browsers too that would have equal capabilities, such as
	<a href="http://caminobrowser.org" rel="external">Camino</a>. Many browsers now spoof as Firefox too, so always
	detect using the Gecko date of the browser in the user-agent.
</p>


<h3>iPhone:</h3>

<pre><code>RewriteCond %{HTTP_USER_AGENT} iPhone|iPod</code></pre>


<hr />

<p>
	<strong>Enjoy.</strong>
</p>
</section>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
</article>
<footer>
	<nav><a href="http://forum.camendesign.com">‹ Discuss this in the Forum ›</a></nav>
		
	<a href="mailto:kroc@camendesign.com">kroc@camendesign.com</a>
	<nav>view-source:
		<a href="/code/mod_rewrite-css-targeting.rem">Rem</a> •
		<a href="/code/mod_rewrite-css-targeting.html">HTML</a> •
		<a href="/design/">CSS</a> •
		<a href="/.system/">PHP</a> •
		<a href="/.htaccess">.htaccess</a>
	</nav>
	<form method="get" action="https://duckduckgo.com">
		<input type="hidden" name="sites" value="camendesign.com" />
		<input type="search" name="q" placeholder="search…" />
		<input type="submit" value="Go" />
	</form>
</footer>
<!-- =================================================================================================== code is art === -->