The weakest link

2013-08-31

How do you add a css stylesheet in such a way that it becomes the weakest rule of all, only being stronger than the browser defaults (not including !important rules..)? It's actually an interesting question which doesn't appear to have such an interesting answer.

I'll give you a partial answer. Partial because I can think of a few cases where things might be different but that I didn't investigate.

The problem with introducing any stylesheet is that it's bound to override certain styles in place. In order to add css rules that only override browser defaults there are a few conditions:



Use the weakest selectors possible

That means element selectors (like div, body, and li). Element selectors are the weakest selectors of all.

Make sure the stylesheet is at the top of the document

The nature of stylesheets is that they will override rules in other existing stylesheets, if present, and equal or stronger. So if there's a stylesheet that already has an element selector, you don't want to override it with your "defaults" anymore.

Include it as an external stylesheet

Now this is tricky, but you need to include it as an external stylesheet because they are weaker than inlined stylesheets. I'm not sure what the rationale was behind that rule, but that's how it is. See below for two dynamic examples.

Include certain html style attribute selectors

For instance, I got hit by a <img align="absmiddle"/> on the github blog. But for some reason, my img element selector was being stronger than Chrome's built-in img[align="absmiddle"] selector, so I was still overriding a stylesheet. There are actually many html attributes that imply a certain style. For most cases, this isn't a problem because it won't be for a style you'd want to reset. But for those cases where you do, this could pose an interesting challenge. One way you could fix this is by making special rules for these cases (using attribute selectors), to ensure the built-in behaviour is not changed.

Make sure you know which styles are inherited and which are not

Certain styles are inherited, like font-family while certain styles, like margin and padding, are not. In order not to reset too aggressively you need to make sure you're doing it right. Also keep in mind that certain elements override inherited styles, like the <b> and <i> elements. So make sure you do reset them on elements that explicitly change the markup. And good luck with <font>, if you have to care about it ;)

Pseudo-selectors

Don't forget to look at things like a:hover and a:visited, as they are stronger than just the element selector.

!important and user rules

User defaults are stronger than browser defaults, but weaker than anything else. So you'll probably be overriding them. That's not the case for !important rules, as it's impossible (afaik) to remove them without explicitly using them yourself. But that would defeat the point because it would make your rule stronger than anything else ;)

Media queries

I did not investigate this

@import

I did not investigate this



My first attempt was rather straightforward. But I did it as an inlined stylesheet (my environment requirements would not like an external stylesheet to solve the problem we were having). That screwed up various stuff, of course, so my first order of business was finding a way to get an external stylesheet without using an external resource. Not quite the first time I tackled this kind of challenge ;)

I've got two approaches to inline an external stylesheet in such a way that they're recognized as such (in terms of specificity).

The first one is a bit obvious; it uses a datauri. The approach is quite simple, actually. You might think that you now have to convert the whole thing to base64, but no. You can actually just inline a stylesheet as `src="dataurl:text/css;,div{color:red;}" (that trailing comma is not a typo!). So that's easy, right? :) Better yet, it's supported in all current browsers. Full example:

Code:
var link = document.createElement('link');
link.setAttribute('href', 'data:text/css;,div{color:red;}');
link.setAttribute('rel', 'stylesheet');
document.body.appendChild(link);
var div = document.createElement('div');
div.innerHTML = 'Hi, red texxt!';
document.body.appendChild(div);


The other approach is using a Blob. Blobs are interpreted by the browser as "an external file", but you generate it dynamically. The problem with blobs is of course the lack of support. It's much newer than dataurls. Here's an example of doing that:

Code:
var blob = new Blob(['div { color: red; }']);
var link = document.createElement('link');
link.setAttribute('href', URL.createObjectURL(blob));
link.setAttribute('rel', 'stylesheet');
document.body.appendChild(link);
var div = document.createElement('div');
div.innerHTML = 'Hi, red text!';
document.body.appendChild(div);


Demo:

DataURL:


Blob: