Semantic CSS With Intelligent Selectors

Advertisement

“Form ever follows function. This is the law.” So said the architect and “father of skyscrapers” Louis Sullivan1. For architects not wishing to crush hundreds of innocent people under the weight of a colossal building, this rule of thumb is pretty good. In design, you should always lead with function, and allow form to emerge as a result. If you were to lead with form, making your skyscraper look pretty would be easier, but at the cost of producing something pretty dangerous.

So much for architects. What about front-end architects — or “not real architects,” as we are sometimes known? Do we abide by this law or do we flout it?

With the advent of object-oriented CSS2 (OOCSS), it has become increasingly fashionable to “decouple presentation semantics from document semantics3.” By leveraging the undesignated meanings of classes4, it is possible to manage one’s document and the appearance of one’s document as curiously separate concerns.

Overthinking how a functional thing should look.

In this article, we will explore an alternative approach to styling Web documents, one that marries document semantics to visual design wherever possible. With the use of “intelligent” selectors, we’ll cover how to query the extant, functional nature of semantic HTML in such a way as to reward well-formed markup. If you code it right, you’ll get the design you were hoping for.

If you are like me and have trouble doing or thinking about more than one thing at a time, I hope that employing some of these ideas will make your workflow simpler and more transferable between projects. In addition, the final section will cover a more reactive strategy: We’ll make a CSS bookmarklet that contains intelligent attribute selectors to test for bad HTML and report errors using pseudo-content.

Intelligent Selectors

With the invention of style sheets came the possibility of physically separating document code from the code used to make the document presentable. This didn’t help us to write better, more standards-aware HTML any more than the advent of the remote control resulted in better television programming. It just made things more convenient. By being able to style multiple elements with a single selector (p for paragraphs, for instance), consistency and maintenance became significantly less daunting prospects.

television remote reading DRIVEL

The p selector is an example of an intelligent selector in its simplest form. The p selector is intelligent because it has innate knowledge of semantic classification. Without intervention by the author, it already knows how to identify paragraphs and when to style them as such — simple yet effective, especially when you think of all of the automatically generated paragraphs produced by WYSIWYG editors.

So, if that’s an intelligent selector, what’s an unintelligent one? Any selector that requires the author to intervene and alter the document simply to elicit a stylistic nuance is an unintelligent selector. The class is a classic unintelligent selector because it is not naturally occurring as part of semantic convention. You can name and organize classes sensibly, but only with deliberation; they aren’t smart enough to take care of themselves, and browsers aren’t smart enough to take care of them for you.

Unintelligent selectors are time-intensive because they require styling hooks to be duplicated case by case. If we didn’t have p tags, we’d have to use unintelligent selectors to manufacture paragraphs, perhaps using .paragraph in each case. One of the downsides of this is that the CSS isn’t portable — that is, you can’t apply it to an HTML document without first going through the document and adding the classes everywhere they are required.

class selector called paragraph

Unintelligent selectors at times seem necessary, or at least easier, and few of us are willing to rely entirely on intelligent selectors. However, some unintelligent selectors can become “plain stupid” selectors by creating a mismatch between document structure and presentation. I’ll be talking about the alarming frequency with which the unintelligent .button selector quickly becomes plain stupid.

Vive la Différence

Intelligent selectors are not confined just to the basic elements offered to us in HTML’s specification. To build complex intelligent selectors, you can defer to combinations of context and functional attribution to differentiate basic elements. Some elements, such as <a>, have a multitude of functional differences to consider and exploit. Other elements, such as <p>, rarely differ in explicit function but assume slightly different roles according to context.

header p {
   /* styles for prologic paragraphs */
}

footer p {
   /* styles for epilogic paragraphs */
}

Simple descendent selectors like these are extremely powerful because they enable us to visually disclose different types of the same element without having to physically alter the underlying document. This is the whole reason why style sheets were invented: to facilitate physical separation without breaking the conceptual reciprocity that should exist between document and design.

a semantic heirarchy of needs: what it is, how it functions, where it is5

Inevitably, some adherents of OOCSS treat the descendent selector with some suspicion6, with the more zealous insisting on markup such as the example below, found in BEM’s “Definitions7” documentation.

<ul class="menu">
  <li class="menu__item">…</li>
  <li class="menu__item">…</li>
</ul>

I won’t cover contextual selectors any further because, unless you have a predilection for the kind of overprescription outlined above, I’m sure you already use them every day. Instead, we’ll concentrate on differentiation by function, as described in attributes and by attribute selectors.

Hyperlink Attributes

Even those who advocate for conceptual separation between CSS and HTML are happy to concede that some attributes — most attributes besides classes and custom data attributes, in fact — have an important bearing on the internal functioning of the document. Without href, your link won’t link to anything. Without type, the browser won’t know what sort of input to render. Without title, your abbr could be referring to either the British National Party or Banco Nacional de Panama.

Some of these attributes may improve the semantic detail of your document, while others are needed to ensure the correct rendering and functioning of their subject elements. If they’re not there, they should be, and if they are there, why not make use of them? You can’t write CSS without writing HTML.

The rel Attribute

The rel attribute emerged as a standard for link relations8, a method of describing some specific purpose of a link. Not all links, you see, are functionally alike. Thanks to WordPress’ championing, rel="prev" and rel="next" are two of the most widely adopted values, helping to describe the relationship between individual pages of paginated blog content. Semantically, an a tag with a rel attribute is still an a tag, but we are able to be more specific. Unlike with classes, this specificity is semantically consequential.

The rel attribute should be used where appropriate because it is vindicated by HTML’s functional specification9 and can therefore be adopted by various user agents to enhance the experience of users and the accuracy of search engines. How, then, do you go about styling such links? With simple attribute selectors, of course:

[rel="prev"] {
  /* styling for "previous links" */
}

[rel="next"] {
  /* styling for "next" links */
}

Attribute selectors like these are supported by all but the most archaic, clockwork browsers, so that’s no reason not to use them anywhere the attributes exist. In terms of specificity, they have the same weight as classes. No woe there either, then. However, I recall it being suggested that we should decouple document and presentation semantics. I don’t want to lose the rel attributes (Google has implemented them10, for one thing), so I’d better put an attribute that means nothing on there as well and style the element via that.

  <a href="/previous-article-snippet/" rel="prev" class="prev">previous page</a>

The first thing to note here is that the only part of the element above that does not contribute to the document’s semantics is the class. The class, in other words, is the only thing in the document that has nothing functionally to do with it. In practice, this means that the class is the only thing that breaks with the very law of separation that it was employed to honor: It has a physical presence in the document without contributing to the document’s structure.

OK, so much for abstraction, but what about maintenance? Accepting that we’ve used the class as our styling hook, let’s now examine what happens when some editing or refactoring has led us to remove some attributes. Suppose we’ve used some pseudo-content to place a left-pointing arrow before the [rel="prev"] link’s text:

.prev:before {
  content: '2190'; /* encoding for a left-pointing arrow ("←") */
}

previous link with arrow

Removing the class will remove the pseudo-content, which in turn will remove the arrow (obviously). But without the arrow, nothing remains to elucidate the link’s extant prev relationship. By the same token, removing the rel attribute will leave the arrow intact: The class will continue to manage presentation, all the time disguising the nonexistence of a stated relationship in the document. Only by applying the style directly, via the semantic attribute that elicits it, can you keep your code and yourself honest and accurate. Only if it’s really there, as a function of the document, should you see it.

Attribute Substrings

I can imagine what you’re thinking: “That’s cute, but how many instances are there really for semantic styling hooks like these on hyperlinks? I’m going to have to rely on classes at some point.” I dispute that. Consider this incomplete list of functionally disimilar hyperlinks, all using the a element as their base:

  • links to external resources,
  • links to secure pages,
  • links to author pages,
  • links to help pages,
  • links to previous pages (see example above),
  • links to next pages (see example above again),
  • links to PDF resources,
  • links to documents,
  • links to ZIP folders,
  • links to executables,
  • links to internal page fragments,
  • links that are really buttons (more on these later),
  • links that are really buttons and are toggle-able,
  • links that open mail clients,
  • links that cue up telephone numbers on smartphones,
  • links to the source view of pages,
  • links that open new tabs and windows,
  • links to JavaScript and JSON files,
  • links to RSS feeds and XML files.

That’s a lot of functional diversity, all of which is understood by user agents of all sorts. Now consider that in order for all of these specific link types to function differently, they must have mutually differential attribution. That is, in order to function differently, they must be written differently; and if they’re written differently, they can be styled differently.

In preparing this article, I created a proof of concept, named Auticons11. Auticons is an icon font12 and CSS set that styles links automatically. All of the selectors in the CSS file are attribute selectors that invoke styles on well-formed hyperlinks, without the intervention of classes.

art_auticons13

In many cases, Auticons queries a subset of the href value in order to determine the function of the hyperlink. Styling elements according to the way their attribute values begin or end or according to what substring they contain throughout the value is possible. Below are some common examples.

The Secure Protocol

Every well-formed (i.e. absolute) URL begins with a URI scheme14 followed by a colon. The most common on the Web is http:, but mailto: (for SMTP) and tel: (which refers to telephone numbers) are also prevalent. If we know how the href value of the hyperlink is expected to begin, we can exploit this semantic convention as a styling hook. In the following example for secure pages, we use the ^= comparator, which means “begins with.”

a[href^="https:"] {
   /* style properties exclusive to secure pages */
}

a link to a secure page with a lock icon

In Auticons, links to secure pages become adorned with a padlock icon according to a specific semantic pattern, identifiable within the href attribute. The advantages of this are as follows:

  • Links to secure pages — and only secure pages — are able to resemble links to secure pages by way of the padlock icon.
  • Links to secure pages that cease to be true links to secure pages will lose the https protocol and, with it, the resemblance.
  • New secure pages will adopt the padlock icon and resemble links to secure pages automatically.

This selector becomes truly intelligent when applied to dynamic content. Because secure links exist as secure links even in the abstract, the attribute selector can anticipate their invocation: As soon as an editor publishes some content that contains a secure link, the link resembles a secure one to the user. No knowledge of class names or complex HTML editing is required, so even simple Markdown15 will create the style:

[Link to secure page](https://payment.example.com/)

Note that using the [href^="https:"] prefix is not infallible because not all HTTPS pages are truly secure. Nonetheless, it is only as fallible as the browser itself. Major browsers all render a padlock icon natively in the address bar when displaying HTTPS pages.

PayPal secure page

File Types

As promised, you can also style hyperlinks according to how their href value ends. In practice, this means you can use CSS to indicate what type of file the link refers to. Auticons supports .txt, .pdf, .doc, .exe and many others. Here is the .zip example, which determines what the href ends with, using $=:

[href$=".zip"]:before,
[href$=".gz"]:before {
   content: 'E004'; /* unicode for the zip folder icon */
}

Combinations

You know how you can get all object-oriented and use a selection of multiple classes on elements to build up styles? Well, you can do that automatically with attribute selectors, too. Let’s compare:

/* The CSS for the class approach */

.new-window-icon:after {
   content: '[new window icon]';
}

.twitter-icon:before {
  content: '[twitter icon]';
}

/* The CSS for the attribute selector approach */

[target="_blank"]:after {
   content: '[new window icon]';
}

[href*="twitter.com/"]:before {
  content: '[twitter icon]';
}

(Note the *= comparator, which means “contains.” If the value string contains the substring twitter.com/, then the style will be honored.)

<!-- The HTML for the class approach -->

<a href="http://twitter.com/heydonworks" target="_blank" class="new-window-icon twitter-icon">@heydonworks</a>

<!-- The HTML for the attribute selector approach -->

<a href="http://twitter.com/heydonworks" target="_blank">@heydonworks</a>

A twitter link with icons to show that it goes to twitter and is external

Any content editor charged with adding a link to a Twitter page now needs to know only two things: the URL (they probably know the Twitter account already) and how to open links in new tabs (obtainable from a quick Google search).

Inheritance

Some unfinished business: What if we have a link that does not match any of our special attribute selectors? What if a hyperlink is just a plain old hyperlink? The selector is an easy one to remember, and performance fanatics will be pleased to hear that it couldn’t be any terser without existing at all.

A basic anchor selector (a)

Flippancy aside, let me assure you that inheritance within the cascade works with attribute selectors just as it does with classes. First, style your basic a — perhaps with a text-decoration: underline rule to keep things accessible; then, progressively enhance further down the style sheet, using the attribute selectors at your disposal. Browsers such as Internet Explorer (IE) 7 do not support pseudo-content at all. Thanks to inheritance, at least the links will still look like links.

a {
  color: blue;
  text-decoration: underline;
}

a[rel="external"]:after {
   content: '[icon for external links]';
}

Actual Buttons Are Actual

In the following section, we’ll detail the construction of our CSS bookmarklet for reporting code errors. Before doing this, let’s look at how plain stupid selectors can creep into our workflow in the first place.

Adherents of OOCSS are keen on classes because they can be reused, as components. Hence, .button is preferable to #button. I can think of one better component selector for button styles, though. Its name is easy to remember, too.

button element selector

The <button> element represents a button.

W3C Wiki16

Topcoat17 is an OOCSS BEM-based UI framework from Adobe. The CSS for Topcoat’s various button styles is more than 450 lines if you include the comment blocks. Each of these comment blocks suggests applying your button style in a manner similar to this introductory example:

   <a class="topcoat-button">Button</a>

This example is not a button. No, sir. If it were a button, it would be marked up using <button>. In fact, in every single browser known to man, if it were marked up as a button and no author CSS was supplied, you could count on it looking like a button by default. It’s not, though; it’s marked up using <a>, which makes it a hyperlink — a hyperlink, in fact, that lacks an href, meaning it isn’t even a hyperlink18. Technically, it’s just a placeholder19 for a hyperlink that you haven’t finished writing yet.

Dog in a shark costume
A dog in a shark costume does not a shark make. (Image: reader of the pack20)

The examples in Topcoat’s CSS are only examples, but the premise that the class defines the element and not the HTML is deceptive. No amount of class name modification via “meaningful hyphenation” can make up for this invitation to turn your unintelligent selector into a plain stupid one and to just code stuff wrong.

Update: Since writing this article, Topcoat.io has replaced these examples with <button> examples. This is great! However, I still have my reservations about the way the examples expound the use of the .is-disabled class while omitting the proper disabled attribute. To hear both sides, find my conversation with the Topcoat representative in the comments. For further examples of OOCSS-facilitated web standards mishaps, look no further than semantic-ui.com21. The “standard button” in their examples is a <div> containing an empty <i>.

See No Evil, Hear No Evil

“If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.”

A link that resembles a button and triggers button-like JavaScript events is, to many, a button. However, this only means that it has passed the first two stages of the “duck test.” For all users to be able to apply inductive reasoning and discern the button as such, it must also quack like one. Because it remains a link, it will be announced by screen readers as “link,” meaning that your allegorical skyscraper is not wheelchair-accessible. Avoiding this kind of unnecessary confusion for assistive technology users is not a pursuit of semantic perfection but a responsibility we should undertake for real benefit.

Nonetheless, some will insist on using a as the basis of their buttons. Hyperlinks are (slightly) easier to restyle consistently, after all. If a is the element of choice, then there is only one way to make it a close-to-true button in the accessibility layer. You guessed it: You must apply another meaningful attribute in the form of a WAI ARIA22 role. To ensure that a hyperlink element looks like a button for good reason, apply only the following attribute selector.

[role="button"] {
   /* semantic CSS for modified elements that are announced as “button” in assistive technologies */
}

Quality Assurance With Attribute Selectors

“CSS gives so much power to the class attribute, that authors could conceivably design their own “document language” based on elements with almost no associated presentation (such as DIV and SPAN in HTML) and assigning style information through the “class” attribute. Authors should avoid this practice since the structural elements of a document language often have recognized and accepted meanings.”

– “Selectors23,” CSS Level 2, W3C

The reason we have two elements — a and button — is to semantically demarcate two entirely different types of functional interaction. While the hyperlink denotes a means to go somewhere, the button is intended as the instigator of an event or action. One is about traversal, the other about transformation. One facilitates disengagement, the other engagement.

To make sure we don’t do anything too daft and get our links and buttons muddled up, we will now build a CSS bookmarklet that uses intelligent attribute selectors to test the validity and quality of the two respective elements.

Inspired partly by Eric Meyer’s post24 and taking a few cues from DiagnostiCSS25, this style sheet will combine attribute selectors and the :not selector (or negation pseudo-class26) to highlight problems in the HTML. Unlike these other two implementations, it will write an error to the screen using pseudo-content. Each error will be written in Comic Sans against a pink background.

By connecting function directly to form, we see that ugly markup results in ugly CSS. Consider this the revenge of an abused document, exacted on its designer. To try it out, drag revenge.css to your bookmarks, and click the bookmark to trigger it on any page that you fancy. Note: It won’t currently work for pages that are served over https.

REVENGE.CSS27
Drag to your bookmarks bar.

Rule 1

“If it’s a hyperlink, it should have an href attribute.”

a:not([href]):after {
   content: 'Do you mean for this to be a link or a button, because it does not link to anything!';
   display: block !important;
   background: pink !important;
   padding: 0.5em !important;
   font-family: 'comic sans ms', cursive !important;
   color: #000 !important;
   font-size: 16px !important;
}

Notes: In this example, we are testing not the attribute’s value, but whether the attribute exists in the first place — that is, whether [href] matches any element with an href attribute. This test is only appropriate on hyperlinks, hence the a prefix. The rule reads like, “For every a element that is not also an [href] element, append some pseudo-content with an error notice.”

Rule 2

“If it’s a hyperlink and has an href attribute, it should have a valid value.”

a[href=""]:after, a[href$="#"]:after, a[href^="javascript"]:after {
   content: 'Do you mean for this link to be a button, because it does not go anywhere!';
   /*... ugly styles ...*/
}

Notes: If the href is empty, ends in a # or is using JavaScript, it’s probably being used as a button without the correct button element. Note that I am using “starts with javascript.” Standard practice for voiding hrefs is to use javascript:void(0), but we can’t depend on that always being written in the same way (with or without a space after the colon, for example).

Rule 3

“If it uses a button class, it should be a button — at least in the accessibility layer.”

.button:not(button):not([role="button"]):not([type="button"]):not([type="submit"]):not([type="reset"]):after,
.btn:not(button):not([role="button"]):not([type="button"]):not([type="submit"]):not([type="reset"]):after,
a[class*="button"]:not([role="button"]):after {
   content: 'If you are going to make it look like a button, make it a button, damn it!';
   /*... ugly styles ...*/
}

Notes: In this example, we’re demonstrating how you can chain negation when testing for attributes. Each selector reads like this: “If the element has a class that says it’s a button but it’s not a button element and it doesn’t have the correct role to make it a button in the accessibility layer and it’s not an input being used as a button, then… well, you’re lying.” I’ve had to use [class*="button"] to catch the many Topcoat class variations (62 in total!) that fail to enforce actual buttons on hyperlinks. I’ve noticed that some authors use button-container and the like on parent elements, which is why the a qualifier is included to avoid false positives. You may recognize the .btn class from Twitter Bootstrap, which (if you’ve read the component’s documentation28 carefully) you’ll know is also unsure about whether links or buttons are buttons.

Rule 4

“If it is an a element with role="button", then it should link to somewhere when JavaScript is off.”

a[role="button"]:not([href*="/"]):not([href*="."]):not([href*="?"]):after {
   content: 'Either use a link fallback, or just use a button element.';
   /*... ugly styles ...*/
}

Notes: We can be fairly sure that hrefs that do not include one of /, . (usually to precede a file extension) or ? (to start a query string) are probably bogus. Getting links to act as buttons and return: false when JavaScript is on is fine — fine, that is, if they have a page to go to when JavaScript is off. In fact, it’s the only legitimate reason I can think of not to use <button> instead.

Rule 5

“You can’t disable a hyperlink.”

a.button[class*="disabled"]:after,
a.btn.disabled:after,
a[class*="button"][class*="disabled"]:after {
   content: 'You cannot disable a hyperlink. Use a button element with disabled="disabled".';
   /*... ugly styles ...*/
}

Notes: Even ancient user agents understand the disabled attribute, so use it with an appropriate element in a compliant way. You can concatenate attribute selectors just as you can concatenate classes: In the last of the three selectors, we’re saying, “If it’s a link that contains the substring button and the substring disabled, then print an error message.” Twitter Bootstrap uses the second form, .btn.disabled, in its style sheet, but not with the a prefix. We’ll only consider it an error if used on hyperlinks.

Rule 6

“Buttons in forms should have explicit types.”

form button:not([type]):after {
    content: 'Is this a submit button, a reset button or what? Use type="submit", type="reset" or type="button"';
}

Notes: We need to determine whether buttons within forms have explicit types, because some browsers29 will treat any button in this context without a specified type as type="submit". We want to be absolutely sure that the form won’t submit if our button has a different purpose.

Rule 7

“Both hyperlinks and buttons should have some sort of content or an ARIA label.”

a:empty:not([aria-label]):not([aria-labelledby]):after,
button:empty:not([aria-label]):not([aria-labelledby]):after,
button:not([aria-label]):not([aria-labelledby]) img:only-child:not([alt]):after,
a:not([aria-label]):not([aria-labelledby]) img:only-child:not([alt]):after {
   content: 'All buttons and links should have text content, an image with alt text or an ARIA label';
   /*... ugly styles ...*/
}

Notes: Buttons and links that don’t include any kind of direction for their usage — in either textual or graphical form — are pretty bogus. These final two selectors are perhaps the most complex I’ve ever written. For the hyperlink version, the selector reads something like this: “If it is a hyperlink that does not have either an aria-label attribute or an aria-labelledby attribute and it contains only an image as content but this image does not have an alt attribute, then write the ugly error message.” Also, note the use of the :empty selector30. Arguably, no element that is not self-closing should ever be left empty.

Ten points to the first person using revenge.css who can tell me where I’ve broken my own rule in this very article. Trust me, the error is definitely there.

Conclusion

The reason I use the kinds of selectors and patterns described above is not to try something different or to have something new to write about. Attribute selectors aren’t, by themselves, anything new anyway. IE 6 is the only browser31 that doesn’t support them. The reason I use them is because I simply do not have the time or mental capacity to “do” HTML and CSS in parallel. My brain just isn’t good enough for that. The reason I style my page headers with [role="banner"] and not .page-header is because that’s the only way I’ll know — upon seeing the intended visual effect — that I’ve put the navigable landmark32 in place. How else does one keep track? You can’t just leave it to testing, because then it’s usually too late.

There’s no such thing as semantic CSS. There’s only semantic HTML and its visible form. In this article I have tried to demonstrate that, by coupling the function and form of Web pages directly, you can create mechanisms for reward and punishment. On the one hand, you can set up selectors that invoke visual motifs only when the suitable markup is used. On the other hand, you can query the markup for bad patterns and erode the visual design as a commitment to the underlying ugly truth.

It’s true that not all the styling hooks in your arsenal will likely ever be uniformly semantic or intelligent. Classes are often desirable as polyfills for much needed elements or attributes that have yet to be standardized. That’s how .footer became <footer> and type="text" (with a bunch of JavaScript) became type="url". Other times, they are helpful for doing non-semantic layout scaffolding with grid frameworks33 and the like.

However, if you are committed to giving CSS its own completely separate logic, then you are bound to create unnecessary arguments between form and function. In this eventuality, only constant vigilance can protect against inaccessibility and invalidity. To make matters worse, trying to manufacture pseudo-semantics purely with classes makes it easy to fall into one of those interminable discussions over what makes a semantic class name34. You’ll start spending less time using the remote to control the TV and more time just sitting there, contemplating the remote control held in your hand.

Life is too short.

(al)

Footnotes

  1. 1 http://en.wikipedia.org/wiki/Louis_Sullivan
  2. 2 http://www.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/
  3. 3 http://nicolasgallagher.com/about-html-semantics-front-end-architecture/
  4. 4 http://www.w3.org/TR/CSS2/selector.html#class-html
  5. 5 http://www.smashingmagazine.com/wp-content/uploads/2013/08/art_heirarchy_mini.png
  6. 6 http://www.impressivewebs.com/when-to-avoid-descendant-selector/
  7. 7 http://bem.info/method/definitions/
  8. 8 http://blog.whatwg.org/the-road-to-html-5-link-relations#what
  9. 9 http://en.wikipedia.org/wiki/Functional_specification
  10. 10 http://googlewebmastercentral.blogspot.co.uk/2011/09/pagination-with-relnext-and-relprev.html
  11. 11 http://heydonworks.com/auticons-icon-font/
  12. 12 http://sixrevisions.com/resources/free-icon-fonts/
  13. 13 http://www.heydonworks.com/auticons-icon-font
  14. 14 http://en.wikipedia.org/wiki/URI_scheme
  15. 15 http://daringfireball.net/projects/markdown/syntax#link
  16. 16 http://www.w3.org/wiki/HTML/Elements/button
  17. 17 http://topcoat.io/
  18. 18 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-href
  19. 19 http://www.w3.org/wiki/HTML/Elements/a
  20. 20 http://www.flickr.com/photos/youngandwithit/
  21. 21 http://semantic-ui.com/elements/button.html
  22. 22 http://www.w3.org/WAI/intro/aria
  23. 23 http://www.w3.org/TR/CSS2/selector.html#class-html
  24. 24 http://meyerweb.com/eric/tools/css/diagnostics/
  25. 25 https://github.com/diagnosticss/diagnosticss
  26. 26 http://dev.w3.org/csswg/selectors3/#negation
  27. 27 (function(){revenge=document.createElement('LINK');revenge.href='//www.heydonworks.com/css/revenge_buttons.css';revenge.rel='stylesheet';revenge.media='all';document.body.appendChild(revenge);})();
  28. 28 http://twitter.github.io/bootstrap/components.html#buttonDropdowns
  29. 29 http://stackoverflow.com/questions/932653/how-to-prevent-buttons-from-submitting-forms
  30. 30 https://developer.mozilla.org/en-US/docs/Web/CSS/:empty
  31. 31 http://www.quirksmode.org/css/selectors/
  32. 32 http://blog.paciellogroup.com/2013/07/enabling-landmark-based-keyboard-navigation-in-firefox/
  33. 33 http://unsemantic.com/
  34. 34 http://css-tricks.com/semantic-class-names/

↑ Back to top Tweet itShare on Facebook

Heydon has a love/hate relationship with CSS and a lust/indifference relationship with javascript. He writes a lot and makes fonts.

Advertising
  1. 1

    No one else has done it yet, so here it is… your rules as a SCSS mixin. https://gist.github.com/jhogue/6370800

    I started using it to test sites while in development, along with a modified version of Holmes which I have been using for a bit now. http://www.tomato-root.com/sandbox/holmes/

    0
  2. 103

    Lewis Cowles (@LewisCowles1)

    September 5, 2013 5:02 am

    Well in an abstract sense I totally agree, but are you suggesting we should (even if we do it deliberately), never break these rules? It just sounds a bit like a grammar Nazi turned web designer turned blogger’s article. Sure We all 100% get that while things are not working in 100% of browsers we need to support, we occasionally do bad things like

    (a.l.a bootstrap)
    <a href=’/controller/method/5′>Something</a>

    instead of

    (a.l.a standard HTML5)
    <form action=’/controller/method/5′ method=’post’>
    <button>Something</button>
    </form>

    really if we take things this far, as far as I am concerned we are no better than grammar Nazi’s that dictate on the English dialects, language is a dynamic, changeable thing, embrace it, have fun and I hope a distilled version of this is taken up everywhere…

    0
    • 154

      The original solution is better in just about every conceivable way.

      A “grammar nazi” would prefer it.

      0
  3. 205

    Just would like to thank you Heydon, I’ve always found the accepted CSS “classitis” to be ugly, and just bad, especially since Sass and LESS which introduced nested selectors.

    And I *never ever* witnessed CSS performance issues appearing after writing CSS more semantically. Bringing up the “performance” argument is a case of premature optimization, “the root of all evil”.

    0
    • 256

      This method is too idealistic for me and though it can work is too unreliable to ever implement. My main gripe is related to the h1/h2/h3 issue. If my page headers are marked up with for example h2’s and are styled simply with the h2 type selector what happens if I need to change half of these h2’s to h3’s? You could add the h3 type selector to the CSS but what if I have a bunch of h3’s that are styled entirely different? Surely it is much more future proof to just say .header uses a certain set of CSS rules and .header__alt (or however you markup your classes) uses another.

      Using type selectors means you are relying on your content to be of a certain nature in order to make it look a certain way. Is that not tying content to presentation?

      -1
  4. 307

    Heydon, this article articulated very thoroughly an idea that’s been under the surface of my mind for a while, but not fully fleshed out until now. Judging by the comments it looks like many others feel the same way.

    With that said, while this approach is clearly ideal, in practice, I am encountering some challenges that I wanted to share with you, and hopefully get your thoughts on. I am currently developing a WordPress theme and attempting to put these ideas to practice. Here are a couple of simple examples that illustrate the challenges I’m having with abandoning classes:

    1) WP normally adds a series of classes to the body tag, identifying the type of page you are on: single, archive, page, etc. Let’s say I wanted to do something as simple as use a particular background image on pages and a different one on posts – how could I do this without these classes?

    2) WP has different post formats: audio, video, normal, etc. Let’s say on my blog index, I would like to include a small icon corresponding to the post format on the top right of each post’s article element. How could I do this without classes?

    I’m sure that you can imagine many other examples like this. My issues have mostly been about how to accomplish certain design tasks with this approach. It’s not always as simple as having an icon on a link based on the filetype. There are more macro issues that need to be dealt with which classes are very useful for. How can we get around this?

    Thanks!

    0
  5. 358

    A common design element is to highlight the link in the navigation for the current page. How would one do this without classes? Is there a semantic attribute indicating that a link points to the current document?

    Granted, it’s pointless to have a link to the currently viewed page in your nav, but sometimes unavoidable. Also, consider a dropdown menu of links. If you are viewing a child page you might want that child link to be highlighted as well as the parent link. This seems even less doable semantically and without classes.

    Any ideas?

    0
    • 409

      tabindex attribute

      0
    • 460

      I have been discussing this issue with Leonie Watson.

      Website navigation has long been lacking a semantic way of saying “you are here”. She is proposing a new ARIA property called “aria-current” which will announce the link in a navigation block which represents the current page or – alternatively – the current “step” in a process such as a multi-page form etc.

      It would probably announce “current page link” in the first of these scenarios.

      0
      • 511

        that would be an excellent addition. for now i’ve decided to remove the link and simply output the text in a span when on that page instead – since it is not necessary to link to the current page and it could also be styled differently.

        0
  6. 562

    One recurring theme I’m seeing in the author’s comments is performance is secondary. I have a serious issue with that and it is a trend that has been going on for quite some time and really needs to stop.

    I’m not arguing that trying to create semantic code is a bad thing, and I fully agree on proper markup particularly when it comes to accessibility. But where I draw the line is suggesting that performance be secondary to semantics. With the trend towards mobile first performance is paramount and anywhere you can eke out a bit more speed is good.

    The author is making assumptions about the user it seems, and again a trend that is frustrating. Everyone has broadband now, everyone has fast computers now… not true. My office connection is quite slow by today’s standards and it’s VERY obvious how few developers think performance is an important factor these days when I browse the web. Many pages take upwards of 30 seconds just to start appearing.

    Some of the reasoning is suspect too. Portable CSS? Really? You do that a lot do you? This has to be the biggest non-issue used as reasoning in the article.

    Overall it was well written and a lot of the bits made sense, it was more the arguments supporting it in the comments I have issue with. I’ve learned some things and I appreciate that but I can’t see myself writing complex selectors instead of classes any time soon just for the sake of semantic code at the expense of even a tiny bit of performance or clarity.

    0
    • 613

      One recurring theme I’m seeing in the author’s comments is performance is secondary.

      Absolutely not. I have been clear that I think performance is really important. That’s why I don’t waste any time chasing down performance issues that don’t exist, like selector performance.

      0
  7. 664

    I’m writing a semantic CSS framework that takes a lot of these ideas and concatenates them into a single framework: https://github.com/herrshuster/Semantic-CSS-Framework . Any input is appreciated

    0
  8. 766

    This method is too idealistic for me and though it can work is too unreliable to ever implement. My main gripe is related to the h1/h2/h3 issue. If my page headers are marked up with for example h2′s and are styled simply with the h2 type selector what happens if I need to change half of these h2′s to h3′s? You could add the h3 type selector to the CSS but what if I have a bunch of h3′s that are styled entirely different? Surely it is much more future proof to just say .header uses a certain set of CSS rules and .header__alt (or however you markup your classes) uses another.

    Using type selectors means you are relying on your content to be of a certain nature in order to make it look a certain way. Is that not tying content to presentation?

    0
  9. 817

    Nice article. How would you select elements with different themes like “default button”, “primary button”, “action button”, etc.?

    0
  10. 868

    I like this article ,thanks a lot!

    0
  11. 919

    What a great article! I fear that unfortunately, those who need it the most won’t spend the time on an article of such (understandable) length. The Web is suffering at the hands of a vast majority who want to churn out code without thinking about why the web was created and not take the time to read these “painful” blog posts. (It’s a lot easier to be sold some new, easy to digest piece of hipster library that will make all your things “lightwieght” and cure cancer, than to be told it’s going to take time to think, appreciate and design your markup).

    Another benefit to “pure” HTML is that it functions well as an API – in fact, a semantic HTML document is more REST than any JSON API (they’re all RPC, across the board) that I’ve seen.

    Just recently I read that Gawker banned strikethrough markup from their articles, because it was “HTML styling”. So sad (not that I care much for the site, but it’s perpetuating the problem).

    What do you think about this?

    <a rel="first" …
    <a rel="next" …
    <a rel="page 3" ..

    and so on.

    In this case I feel like the class is an interesting way to accomplish this, though "first page" is just as an appropriate rel. An aside class=”recent news” seems perfectly semantic too. So class isn’t always evil. It’s tough when you want to mash-up several microformats on a single page. Each component might be loaded separately, with it’s own HTTP request (and perhaps content type), but there is a risk of collision if they are’t classified *somehow* – just not with “presentation” nonsense. I was severely disappointed by Semantic UI. Agreed: there is no such thing as “semantic CSS”, rather, I thought it was going to be a library that *understood* this and got out of your way. I was hopeful at first, because they claim on their homepage that you can use “any element”. Their dropdown “enhancement” says differently.

    Also, that link to nicolasgallagher.com seriously calls in to question whether he understands the basic principles of HTML/The Web. I mean, great job with normalize.css, but seriously, what the heck?

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top