Contextual CSS Is Good When You Know Everything About Your Content
Cascading Style Sheets are the cat's pajamas; they've revolutionized the way that we build websites in as significant a way as I'd say server-side scripting did. In short, they are awesome. But, while the syntax for CSS is rather finite and easy to understand, recognizing the best way in which to architect your CSS is as much an art as it is a science. And, when you take into account that not all browsers support the same CSS, it gets even more complicated.
Right now, I'm in the middle of building an in-house CSS framework to act as the base on which to build future applications. Because this will be used for applications that have not yet been created, there is a large amount of information that I don't have on hand. In most cases, this isn't a problem as things can be added as deemed necessary; but, when it comes to content "containers," what you don't know now might cause serious, unexpected problems later.
For example, UI modules that can be abstractly thought of as "containers" might hold any type of content going forward (potentially including nested containers of the same type!). In situations like this, I am discovering that "contextual css" can be painful and dangerous. Contextual CSS, if you are not aware, is simply a generic term for descendant-based selectors. For example:
ul#primary-navigation li { ... }
... styles LI elements in the context of the UL with id, "primary-navigation". Contextual styling allows us to style elements without giving them specific classes (or giving them only generic classes) by using their ancestors as the rule by which to apply styles. This allows us to keep our markup cleaner and more meaningful.
Using contextual CSS with things like primary navigation, site headers, and site footers is very nice and easy since the type and amount of content that will exist within these elements is relatively finite. But, when we have to style content containers, contextual CSS might inadvertently cause the contained content to be mis-styled. For example, consider the following CSS for a modal window:
div.modal-window div.content-container { ... }
Here, we are applying style rules to what is intended to be the "content" area of the modal-window. This is all fine and good when our modal window is empty; but, the second we add content to the modal window that has, itself, an element "div.content-container", we are in big trouble. Suddenly, our nested "div.content-container" has styles that should only be applied to the modal window itself.
So, what can we do to deal with this situation?
The easiest solution might be to make use of the CSS child selector ">":
div.modal-window > div.content-container { ... }
The ">" selector denotes direct parent-child relationship; so, in the above, we would be applying the styles to only the "div.content-container" that is a direct child of "div.modal-window". This would prevent styles from cascading down into the content itself.
This is a nice solution, but be aware that the child selector was not implemented in Internet Explorer until version 7. This means that in IE6, you get nothing. And, it's not that it renders oddly or reverts back to generic, descendant-based selector behavior - it simply doesn't style anything. This is fine if you are working within a set browser environment; but, if you are building anything public or used in the educational system (where there are a lot of old computers), it's something to consider.
NOTE: Of the IE users that access this blog, close to 30% use a version of IE lower than 7.
If you've weighed the pros and cons and decided that child selectors are not an option, then what can we do?
I think the next solution would be to cut reliance on contextual CSS and simply be more explicit with your container-based class names. For example, rather than using:
div.modal-window div.content-container { ... }
... we could have:
div.modal-window-content-container { ... }
This is a lot more wordy, but the chances that this name would be nested within the future content is significantly smaller (because the name itself is not generic and implies a very specific use). In fact, naming classes in this way might even make your CSS easier to organize and maintain as it is based less on relationships and more on specific instance meaning.
And, what's nice about this strategy is that contextual CSS can still be used for general themeing:
body#night-time div.modal-window-content-container { ... }
I hope you don't think that I'm trying to sell you against contextual CSS - it's a really awesome mechanism and part of what makes CSS so powerful while keeping our XHTML fairly lightweight; all I'm pontificating about is the fact that when it comes to content containers specifically, contextual CSS might be more harmful than beneficial in the long run. Of course, I'm still learning the art and science of all of this, so I might be way off the grid.
Reader Comments
I wrestled (still do) with this exact issue. How to create widgets that may be added anywhere in an application/page and style them easily.
I felt giving each widget its own container (typically a div) and id or class resulted in additional, and some cases unnecessary markup - since the parent container may be all that is required to apply the styles. However in the end it turned out to be the most practical solution.
I also adopted a CSS layout framework (960grid - but there are many to choose from) that certainly speeds layout and gives new users a common starting point.
@Johan,
I think a CSS framework is a good idea, but you still will have to struggle with this issue, I think, regardless. With the stuff I'm working on, I'm gonna try going the specific class name route for things that are designed to hold unknown content. Then, for everything else more finite, I will rely on the power of contextual CSS.
I'll let you know how it goes :)
Maybe this is a no no, but for me, when I want more intelligence in my CSS, I use some jQuery helpers. Obviously I have a base style sheet that covers 90% of what I'm after. (Maybe more like 99%.) But for that 1% where I need something specific, and I don't want to hope it works cross browser I:
$('div.mainNav ul > li:first').css('color:red;');
Or something of the like.
I figure it's about on the same line of having JS help with the sizing of objects to make sure they all line up correctly. (Like getting the tallest pane in a tabset, and setting them all to that height so there's no "jumping" when switching tabs.)
Another benefit, though I haven't explored this, would be the ability to setup variables, to be reused in the styling. Which is something I've always wanted in CSS.
I do not see many that is using cfm files for css? I surely helps when you want variables :)
I sometimes do a style.cfm like this:
<cfset primaryColor = "blue">
<cfset stdBlockMargin = "4">
<cfheader name="content-type" value="text/css">
<cfheader name="expires" value="#GetHttpTimeString(now()+ CreateTimeSpan(0,8,0,0))#">
<cfcontent type="text/css">
.myClass{
color: <cfoutput>#primaryColor#</cfoutput>#;
width: <cfoutput>145 -#stdBlockMargin * 2#</cfoutput>px;
margin: <cfoutput>#stdBlockMargin#</cfoutput>px;
}
If you cache it, performance does not really suffer. I have not seen any browser that gets confused when the stylesheet is .cfm instead of .css, but I can on the other hand not say I have tested it, or used it, very much since most designers get violent when they see something that looks like programming code. I do use it as templates to generate a static .css though
Ben,
I would propose that instead of trying to make a single all encompassing framework that you look at dividing your needs. Create a base framework which defines the core, the immutable, parts. Then look at additional css to define certain aspects.
In my opinion the core is where contextual CSS is best used and something more explicit (or still semantic but constrained) is stored in additional files.
That said in the end it really is about what works best for you.
Good points Ben.
I've found that "namespacing" CSS helps quite a bit when developing a generic css framework. For a really good example of this look at the CSS created by the jQueryUI widgets.
Also another bit that I find very useful is that items can have multiple classes (and your CSS can match on any combination of them).
For example <input class="my-widget-lib my-widget-button" /> will match to css selectors .my-widget-lib and .my-widget-button.my-widget-lib
I felt giving each widget its own container (typically a div) and id or class resulted in additional, and some cases unnecessary markup - since the parent container may be all that is required to apply the styles. However in the end it turned out to be the most practical solution.In my opinion the core is where contextual CSS is best used and something more explicit (or still semantic but constrained) is stored in additional files.
Ist of all i want to say its really a very informative post. But i want to adding that if we should use external CSS. Because internal css is not makes our codding some complex when in looks. So we should avoid internal CSS as possible.
@Tim,
I think Javascript can certainly be used to "clean" parts of the HTML as long as that is OK with the use cases.
@Stefan,
I am a fan of using a CFM file to compile multiple CSS or JS files. And yes, as long as you cache the file, I have not noticed any performance issues. In fact, it was looking into this type of caching where I learned about GetHTTPTimeString().
@Kevin,
I think that's what I'm trying to do; basically, I am creating generic things like data forms and data grids. The point about treating them like containers (or some of them at least) is that that is the *core* framework - the rest will be added as needed. But, that "add as needed" part is particularly why I need to have less contextual CSS for my containers.
@Jon,
I like the idea of name-spaced CSS, but I am not sure that IE supports this?
@Jon, I do that a lot. I often define classes like
.left{float:left}
.border{}
.noborder
.clear
.parent
.bold
and so so that I can do <div class="myMainClass left border"> so I can have variations on my base classes without writing a new class name.
In some ways maybe it makes the css less "pure", but it is better than using the style="" for exceptions and better than writing a new rule for one exception. It is also possible to override for instance 'bold' at a certain place in the css if that is absolutely necessary.
I give all my widgets an id anyway, and all the main sections of a page, so it is easy to do
#main h3{}
#section2 h3{}
#section3 h3{}
What I am trying to do most currently is to keep everything that has to do with sizing and positioning separate from appearence, like font and colors. Usually you make a lot more changes in the appearence sections, so I keep them in separate stylesheets (and merge+minify them at the production servers)
I actually have one stylesheet for each "widget" also, unless it is very insignificant. I found there is less risk of mistakes like that and if I want to change the default behaviour I do that in my "overrides.css"
So I usually have a 'main' css that includes all the parts, like
structure.css
appearence.css
widget1.css
widget2.css
overrides.css
And then my merger can take a look at main.css and merge everything in there into the live.css
I guess there is better ways, but it seems like each time I want to improve it, it just get's more complicated :)
Good points. I'm going through this now as I wanted to define a div#main_content p {} contextual selector for about 90% of a site's copy, but there are certain areas it shouldn't be styled that way (line-spacing, text-indent, stuff like that). Since I won't be maintaining this site forever, I probably won't go this route as anyone after me who puts in a paragraph (even with its own class) down the road will wonder why it is inheriting default styling they don't want. To make things more confusing, the CMS in use is in a highly vertical industry application and requires edits by the hosting web team (external developers like me have to request changes by email). So in this case contextual selectors aren't really the right option, despite their obvious power, cleanliness, and simplicity.
@Greg,
CSS is so powerful and at the same time so frustrating! I just watched a really interesting video about "Object Oriented CSS" that seems to excel in large-scale businesses:
www.bennadel.com/blog/1633-Object-Oriented-CSS-OOCSS-By-Nicole-Sullivan.htm
I'm still trying to digest it, but it has some interesting advice.