CSS isolation: there has got to be a better way
CSS can be a tricky thing. I’m trying to do something that I think should be pretty simple. Let’s say a page contains a section (e.g. an admin panel) that must be styled independently from the rest of the page, but consistently and predictably. The DOM and CSS for the main part of the page is undetermined (e.g. because it’s part of a user-defined theme). Of course, you could use iframes, which are about the only isolation mechanism in HTML but we can’t do this here because iframes are quite rigid in shape (they are rectangles), they make scripting the DOM more difficult and they pretty much require an additional round-trip to the server to serve their contents.
The real problem to solve here is that if the main CSS for the page defines very general styles that for example target all elements with a given tag name, and those styles are going to bleed into our specialized region unless we find a way to block that CSS from cascading down. Ideally, you’d have an attribute on the tag, something like inheritcss=”false”, but no such thing exists.
So the only way I’ve found to solve this problem is to write a stylesheet that explicitly resets the defaults for all properties and all elements. Here is an excerpt from the CssIsolation.css file:
.isolate * { background: white none repeat scroll 0% 0%; border: 0px none black; border-collapse: separate; border-spacing: 0px;
[...] z-index: auto; } .isolate p { display:block; margin-top: 16px; margin-bottom: 16px; } .isolate strong, .isolate b { font-weight: bold; } .isolate h1 { display: block; font-size: xx-large; font-weight: bold; margin-top: 21px; margin-bottom: 21px; }
This is lame for a number of reasons:
- Defaults might vary from browser to browser (this is particularly true of fonts and sizes).
- Some styles can’t be reset to defaults, such as default button styles (at least, not without using browser-specific styles that aren’t consistently available):
- An exhaustive list of tags and style properties is sure to be wrong now or in the future (I did CSS 2, so it’s already outdated by CSS3).
Still, imperfect as it is, this is a solution that works reasonably well and can be improved as problems are found. The equivalent of that inheritcss=”false” attribute, using my isolation stylesheet, is to set class=”isolate” on the parent element of the section of the page you want to isolate. None of the styles defined for the rest of the page (which is defined just as usual, with no difference whatsoever) should bleed then.
Here a snapshot of my test page, where you can see, side-by-side, unstyled HTML and HTML with reset styles:
Differences exist but are quite subtle.
The HTML for an isolated section is defined like this:
<div class="isolate" id="isolated1"> Isolated 1... </div>
If for example the main style sheet defines the style of lists like follows, this style won’t bleed into any element that has the isolate class (on the right on the next screenshot), just because we set the “isolate” class on the parent element.
ul li { list-style-type: square; } ol li { list-style-type: upper-roman; }
You can then style each of these sections independently by just qualifying each of the style selectors with the ID of the parent element. In other words, just paste “#isolated1” in front of each selector of the local stylesheet, if the id of the parent element is “isolated1”:
#isolated1 p { color: Green; border: solid 1px black; }
This style will override the isolation CSS because it is qualified by id, which always wins over styles that are only qualified by class:
I really wish one of you will tell me how stupid I am for not knowing about feature/hack X that is way simpler and gets you to the same place… There has got to be a better way.
The code for the isolation stylesheet, as well as the test pages, can be found here:
http://weblogs.asp.net/blogs/bleroy/Samples/CssIsolation.zip