On CSS selectors, HTML ID and class attributes and reducing cost
If an organization neglects to properly delegate this task, it typically ends up in the hands of those responsible for marking up and styling web content. These individuals tend to have little incentive to care about the consistency and semantic value of the names they choose as selectors. This has the potential to cause problems with those writing Javascript, as they use class and id attributes to select elements out of the DOM. Furthermore, it drives the cost up of adding microcontent later on, as the entire base of HTML templates, CSS stylesheets and Javascript is likely to have to be scrubbed.
It should be safe to assume the following:
- Only marketing, search engine optimization (SEO) people and information architects should actually care about what these values say, as this is valuable microcontent.
- Javascript programmers and markup/layout people should only care that the values are granular enough to "grab on to".
- Back-end programmers should only care that documentation of these values exists, should they even care about any of this at all.
Some good guidelines to follow:
- Set an official company-wide policy for CSS classname notation — decide once, decide forever.
- Decide on a set of semantic classnames based on the nature of the objects being displayed on the screen — better yet, generate them from the object model hierarchy.
- Use ancestor-descendant selectors and composite classes wherever possible — increase selector granularity without introducing excess classes.
- CSS authors need never create selectors based on element IDs — please, let me know of a case (stone-age compatibility notwithstanding) in which it would be 100% necessary.
- Produce consistent, unique element IDs for the Javascript authors — they're the ones who rely on them the most.
The HTML class attribute is of DTD type CDATA, meaning that literally any content can be placed into it, provided that content is escaped properly. CSS class selectors split on whitespace, meaning any opaque token can be used as a class. An example of an opaque token could be a QName like an XML Schema datatype or perhaps an rdfs:Class URI. Both parent document formats of those datatypes — XML Schema and RDF Schema — have more than adequate capacity to document the nature of the object to be displayed on the screen.
The HTML id attribute is of DTD type ID, which, according to the spec, must start with an ASCII letter, followed by any number of ASCII letters, digits, hyphens, underscores, colons and periods. This means that it is possible to use an ASCII-conformant XML QName (except those in which the namespace prefix begins with "_"), and still maintain a valid document.
On the CSS side, characters in selectors that collide with the CSS grammar can be escaped with a backslash ("\"), followed by their Unicode codepoint (one to six hexadecimal digits), followed by optional whitespace if the code is shorter than the full six digits. Meaning that:
<div class="http://xmlns.foo.com/MyType">Look at me!</div>can be styled by the CSS rule:
The preceding has been confirmed to work in recent versions of Gecko, MSIE, Opera, Konqueror and Safari.
.http\3a\2f\2fxmlns\2e foo\2e com\2fMyType {
background-color: #f00f00;
}
To keep designers happy, I recommend devising a tool that will generate CSS stubs based on their object model hierarchy. Note that "inheritance" in the context of CSS is not the same as it is in a standard object hierarchy. Here is an example:
/*
com.foobar.myproject.Manager - A Manager in a Company
`-- com.foobar.myproject.Employee
`-- com.foobar.myproject.Person
*/
.com\2e foobar\2e myproject\2e Manager {
/* enter Manager-specific rules here */
}
Or, in order to respect an application's object inheritance tree, it could be possible to generate an element with the following:
<div class="com.foobar.myproject.Manager com.foobar.myproject.Employee com.foobar.myproject.Person">Content!</div>
And then generate style rule stubs like so:
/*
com.foobar.myproject.Person - A representation of a person
*/
.com\2e foobar\2e myproject\2e Person {
/* Generic styling */
}
/*
com.foobar.myproject.Employee - An employee of a company
`-- com.foobar.myproject.Person
*/
.com\2e foobar\2e myproject\2e Employee.com\2e foobar\2e myproject\2e Person {
/* More specific styling */
}
/*
com.foobar.myproject.Manager - A manager of a company
`-- com.foobar.myproject.Employee
`-- com.foobar.myproject.Person
*/
.com\2e foobar\2e myproject\2e Manager.com\2e foobar\2e myproject\2e Employee.com\2e foobar\2e myproject\2e Person {
/* Most specific styling */
}
Now, this is an extreme example of a set of fully qualified class names and how to map them to CSS. SEO folk might argue that the token repetition is bad for Google scores and performance geeks might say that the length of the class names needlessly bloat the size of the document. I would recommend experimenting to find an optimal solution.
But, to satisfy the aforementioned people in the meantime, here is an example of ancestor-descendant selectors using XML Schema simple types to designate the datatypes and FOAF QName types to designate the relationships. Picture this markup:
<div class="foaf:Person">
<span class="foaf:name">Dorian Taylor</span>
<a class="xsd:anyURI foaf:weblog" target="_blank" href="http://heavy-industries.blogspot.com/">heavy industries.</a>
<span class="xsd:gMonthDay foaf:birthday">08-17</span>
</div>
Followed by this (potentially generated) CSS:
Upon implementing a system like this, I suggest that it is possible for those interested in the minutiae of the naming of CSS selectors (the marketing/SEO and the information architects) to employ those who benefit directly from the automation and documentation thereof (the programmers) to dictate values to those who would rather not have to think about choosing them anyway (the CSS authors). The result? Better cooperation, fewer bugs and quicker time to market.
/*
XML SCHEMA TYPES
*/
/*
xsd:anyURI - An arbitrary URI reference
*/
.xsd\3a anyURI {
/* URI-specific styling, like hover colour, etc? - possible macro for pseudo classes */
}
/*
xsd:gMonthDay - Month-day string on Gregorian calendar
*/
.xsd\3a gMonthDay {
/* date-specific styling, like localization, etc? possibly useful via Javascript */
}
/*
FOAF SCHEMA TYPES
*/
/*
foaf:Person - A person. Living, dead or imaginary.
*/
.foaf\3a Person {
/* style Person container */
}
/*
foaf:birthday - A person's birthday.
*/
.foaf\3a Person foaf\3a birthday {
/* style birthdate as it relates to a foaf:Person */
}
/*
foaf:name - A person's name.
*/
.foaf\3a Person foaf\3a name {
/* drop-cap the first initial or something cute like that */
}
/*
foaf:weblog - The URL of a person's weblog.
*/
.foaf\3a Person foaf\3a weblog {
/* make it flash or something gaudy */
}