Taming Advanced CSS Selectors

Advertisement

CSS is one of the most powerful tools that is available to web designers (if not the most powerful). With it we can completely transform the look of a website in just a couple of minutes, and without even having to touch the markup. But despite the fact that we are all well aware of its usefulness, CSS selectors are still not used to their full potential and we sometimes have the tendency to litter our HTML with excessive and unnecessary classes and ids, divs and spans.

The best way to avoid these plagues spreading in your markup and keep it clean and semantic, is by using more complex CSS selectors, ones that can target specific elements without the need of a class or an id, and by doing that keep our code and our stylesheets flexible.

CSS Specificity

Before delving into the realms of advanced CSS selectors, it’s important to understand how CSS specificity works, so that we know how to properly use our selectors and to avoid us spending hours debugging for a CSS issue that could be easily fixed if we had only payed attention to the specificity.

When we are writing our CSS we have to keep in mind that some selectors will rank higher than others in the cascade, the latest selector that we wrote will not always override the previous ones that we wrote for the same elements.

So how do you calculate the specificity of a particular selector? It’s fairly straightforward if you take into account that specificity will be represented as four numbers separated by commas, like: 1, 1, 1, 1 or 0, 2, 0, 1

  1. The first digit (a) is always zero, unless there is a style attribute applied to that element within the markup itself
  2. The second digit (b) is the sum of the number of IDs in that selector
  3. The third digit (c) is the sum of other attribute selectors and pseudo-classes in that selector. Classes (.example) and attribute selectors (eg. li[id=red]) are included here.
  4. The fourth digit (d) counts the elements (like table, p, div, etc.) and pseudo-elements (like :first-line)
  5. The universal selector (*) has a specificity of zero
  6. If two selectors have the same specificity, the one that comes last on the stylesheet will be applied

Let’s take a look at a few examples, to make it easier to understand:

  • #sidebar h2 — 0, 1, 0, 1
  • h2.title — 0, 0, 1, 1
  • h2 + p — 0, 0, 0, 2
  • #sidebar p:first-line — 0, 1, 0, 2

From the following selectors, the first one is the one who will be applied to the element, because it has the higher specificity:

  • #sidebar p#first { color: red; } — 0, 2, 0, 1
  • #sidebar p:first-line { color: blue; } — 0, 1, 0, 2

It’s important to have at least a basic understanding of how specificity works, but tools like Firebug are useful to let us know which selector is being applied to a particular element by listing all the CSS selectors in order of their specificity when you are inspecting an element.

Firebug
Firebug lets you easily see which selector is being applied to an element.

Useful links:

1. Attribute selectors

Attribute selectors let you target an element based on its attributes. You can specify the element’s attribute only, so all the elements that have that attribute — whatever the value — within the HTML will be targeted, or be more specific and target elements that have particular values on their attributes — and this is where attribute selectors show their power.

There are 6 different types of attribute selectors:

  • [att=value]
    The attribute has to have the exact value specified.
  • [att~=value]
    The attribute’s value needs to be a whitespace separated list of words (for example, class=”title featured home”), and one of the words is exactly the specified value.
  • [att|=value]
    The attribute’s value is exactly “value” or starts with the word “value” and is immediately followed by “-”, so it would be “value-”.
  • [att^=value]
    The attribute’s value starts with the specified value.
  • [att$=value]
    The attribute’s value ends with the specified value.
  • [att*=value]
    The attribute’s value contains the specified value.

For example, if you want to change the background color of all the div elements that are posts on your blog, you can use the an attribute selector that targets every div whose class attribute starts with “post-”:

div[class*="post"] {
  background-color: #333;
  }

This will match all the div elements whose class attribute contains the words “posts”, in any position.

Another useful usage of attribute selectors is to target different types of input elements. For example, if you want your text inputs to have a different width from the others, you can use a simple attribute selector:

input[type="text"] {
  width: 200px;
  }

This will target all the input elements whose type attribute is exactly “text”.

Now let’s say you want to add a different icon next to each different type of file your website is linking to, so your website’s visitors know when they’ll get an image, a PDF file, a Word document, etc. This can be done by using an attribute selector:

a[href$=".jpg"] {
  background: url(jpeg.gif) no-repeat left 50%;
  padding: 2px 0 2px 20px;
  }
  
a[href$=".pdf"] {
  background: url(pdf.gif) no-repeat left 50%;
  padding: 2px 0 2px 20px;
  }
  
a[href$=".doc"] {
  background: url(word.gif) no-repeat left 50%;
  padding: 2px 0 2px 20px;
  }

In this example, we’ve used an attribute selector that will target all the links (a) whose href attribute ends ($) with .jpg, .pdf or .doc.

Notes on browser support
Apart from Internet Explorer 6, all major browsers support attribute selectors. This means that when you are using attribute selectors on your stylesheets, you should make sure that IE6 users will still be provided with a usable site. Take our third example: adding an icon to your links adds another level of usability to your site, but the site will still be usable if the links don’t show any icons.

2. Child selector

The child selector is represented by the sign “>”. It allows you to target elements that are direct children of a particular element.

For example, if you want to match all the h2 elements that are a direct child of your sidebar div, but not the h2 elements that may be also within the div, but that are grandchildren (or later descendants) of your element, you can use this selector:

div#sidebar > h2 {
  font-size: 20px;
  }

You can also use both child and descendant selectors combined. For example, if you want to target only the blockquote elements that are within divs that are direct grandchildren of the body element (you may want to match blockquotes inside the main content div, but not if they are outside it):

body > div > div blockquote {
  margin-left: 30px;
  }

Notes on browser support
Like the attribute selectors, the child selector is not supported by Internet Explorer 6. If the effect you are trying to achieve by using it is crucial for the website’s usability or overall aesthetics, you can consider using a class selector with it, or on a IE-only stylesheet, but that would detract from the purpose of using child selectors.

3. Sibling combinators

There are two types of sibling combinators: adjancent sibling combinators and general sibling combinators.

Adjacent sibling combinator

This selector uses the plus sign, “+”, to combine two sequences of simple selectors. The elements in the selector have the same parent, and the second one must come immediately after the first.

The adjacent sibling combinator can be very useful, for example, when dealing with text. Lets say you want to add a top margin to all the h2 tags that follow a paragraph (you don’t need to add a top margin if the heading comes after an h1 tag or if it’s the first element on that page):

p + h2 {
  margin-top: 10px;
  }

You can be even more specific and say that you only want this rule applied if the elements are within a particular div:

div.post p + h2 {
  margin-top: 10px;
  }

Or you can add another level of complexity: say you want the first line of the paragraphs of every page to be in small caps.

.post h1 + p:first-line {
  font-variant: small-caps;
  }

Because you know that the first paragraph of every post immediately follows an h1 tag, you can refer to the h1 on your selector.

General sibling combinator

The general sibling combinator works pretty much the same as the adjacent sibling combinator, but with the difference that the second selector doesn’t have to immediately follow the first one.

So if you need to target all the p tags that are within a particular div and that follow the h1 tag (you may want those p tags to be larger than the ones that come before the title of your post), you can use this selector:

.post h1 ~ p {
  font-size: 13px;
  }

Notes on browser support
Internet Explorer 6 doesn’t understand sibling combinators, but, as for the other cases, if your audience includes a small percentage of IE6 users, and if the website’s layout isn’t broken or severely affected by its lack of support, this is a much easier way of achieving lots of cool effects without the need of cluttering your HTML with useless classes and ids.

4. Pseudo-classes

Dynamic pseudo-classes

These are called dynamic pseudo-classes because they actually do not exist within the HTML: they are only present when the user is or has interacted with the website.

There are two types of dynamic pseudo-classes: link and user action ones. The link are :link and :visited, while the user action ones are :hover, :active and :focus.

From all the CSS selectors mentioned in this post, these will probably be the ones that are most commonly used.

The :link pseudo-class applies to links that haven’t been visited by the user, while the :visited pseudo-class applies to links that have been visited, so they are mutually exclusive.

The :hover pseudo-class applies when the user moves the cursor over the element, without having to activate or click on it. The :active pseudo-class applies when the user actually clicks on the element. And finally the :focus pseudo-class applies when that element is on focus — the most common application is on form elements.

You can use more than one user action dynamic pseudo-class in your stylesheets, so you can have, for example, a different background color for an input field depending on whether the user’s cursor is only hovering over it or hovering over it while in focus:

input:focus {
  background: #D2D2D2;
  border: 1px solid #5E5E5E;
  }
  
input:focus:hover {
  background: #C7C7C7;
  }

Notes on browser support
The dynamic pseudo-classes are supported by all modern browsers, even IE6. But bear in mind that IE6 only allows the :hover pseudo-class to be applied to link elements (a) and only IE8 accepts the :active state on elements other than links.

:first-child

The :first-child pseudo-class allows you to target an element that is the first child of another element. For example, if you want to add a top margin to the first li element of your unordered lists, you can have this:

ul > li:first-child {
  margin-top: 10px;
  }

Let’s take another example: you want all your h2 tags in your sidebar to have a top margin, to separate them from whatever comes before them, but the first one doesn’t need a margin. You can use the following code:

#sidebar > h2 {
  margin-top: 10px;
  }
  
#sidebar > h2:first-child {
  margin-top: 0;
  }

Notes on browser support
IE6 doesn’t support the :first-child pseudo-class. Depending on the design that the pseudo-class is being applied to, it may not be a major cause for concern. For example, if you are using the :first-child selector to remove top or bottom margins from headings or paragraphs, your layout will probably not break in IE6, it will only look sightly different. But if you are using the :first-child selector to remove left and right margins from, for example, a floated sequence of divs, that may cause more disruption to your designs.

The language pseudo-class

The language pseudo-class, :lang(), allows you to match an element based on its language.

For example, lets say you want a specific link on your site to have a different background color, depending on that page’s language:

:lang(en) > a#flag {
  background-image: url(english.gif);
  }
  
:lang(fr) > a#flag {
  background-image: url(french.gif);
  }

The selectors will match that particular link if the page’s language is either equal to “en” or “fr” or if it starts with “en” or “fr” and is immediately followed by an “-”.

Notes on browser support
Not surprisingly, the only version of Internet Explorer that supports this selector is 8. All other major browsers support the language pseudo-selector.

5. CSS 3 Pseudo-classes

:target

When you’re using links with fragment identifiers (for example, http://www.smashingmagazine.com/2009/08/02/bauhaus-ninety-years-of-inspiration/#comments, where “#comments” is the fragment identifier), you can style the target by using the :target pseudo-class.

For example, lets imagine you have a long page with lots of text and h2 headings, and there is an index of those headings at the top of the page. It will be much easier for the user if, when clicking on a particular link within the index, that heading would become highlighted in some way, when the page scrolls down. Easy:

h2:target {
  background: #F2EBD6;
  }

Notes on browser support
This time, Internet Explorer is really annoying and has no support at all for the :target pseudo-class. Another glitch is that Opera doesn’t support this selector when using the back and forward buttons. Other than that, it has support from the other major browsers.

The UI element states pseudo-classes

Some HTML elements have an enable or disabled state (for example, input fields) and checked or unchecked states (radio buttons and checkboxes). These states can be targeted by the :enabled, :disabled or :checked pseudo-classes, respectively.

So you can say that any input that is disabled should have a light grey background and dotted border:

input:disabled {
  border:1px dotted #999;
  background:#F2F2F2;
  }

You can also say that all checkboxes that are checked should have a left margin (to be easily seen within a long list of checkboxes):

input[type=”checkbox”]:checked {
  margin-left: 15px;
  }

Notes on browser support
All major browsers, except our usual suspect, Internet Explorer, support the UI element states pseudo-classes. If you consider that you are only adding an extra level of detail and improved usability to your visitors, this can still be an option.

6. CSS 3 structural pseudo-classes

:nth-child

The :nth-child() pseudo-class allows you to target one or more specific children of a parent element.

You can target a single child, by defining its value as an integer:

ul li:nth-child(3) {
  color: red;
  }

This will turn the text on the third li item within the ul element red. Bear in mind that if a different element is inside the ul (not a li), it will also be counted as its child.

You can target a parent’s children using expressions. For example, the following expression will match every third li element starting from the fourth:

ul li:nth-child(3n+4) {
  color: yellow;
  }

In the previous case, the first yellow li element will be the fourth. If you just want to start counting from the first li element, you can use a simpler expression:

ul li:nth-child(3n) {
  color: yellow;
  }

In this case, the first yellow li element will be the third, and every other third after it. Now imagine you want to target only the first four li elements within the list:

ul li:nth-child(-n+4) {
  color: green;
  }

The value of :nth-child can also be defined as “even” or “odd”, which are the same as using “2n” (every second child) or “2n+1” (every second child starting from the first), respectively.

:nth-last-child

The :nth-last-child pseudo-class works basically as the :nth-child pseudo-class, but it starts counting the elements from the last one.

Using one of the examples above:

ul li:nth-child(-n+4) {
  color: green;
  }

Instead of matching the first four li elements in the list, this selector will match the last four elements.

You can also use the values “even” or “odd”, with the difference that in this case they will count the children starting from the last one:

ul li:nth-last-child(odd) {
  color: grey;
  }

:nth-of-type

The :nth-of-type pseudo-class works just like the :nth-child, with the difference that it only counts children that match the element in the selector.

This can be very useful if we want to target elements that may contain different elements within them. For example, let’s imagine we want to turn every second paragraph in a block of text blue, but we want to ignore other elements such as images or quotations:

p:nth-of-type(even) {
  color: blue;
  }

You can use the same values as you would use for the :nth-child pseudo-class.

:nth-last-of-type

You guessed it! The :nth-last-of-type pseudo-class can be used exactly like the aforementioned :nth-last-child, but this time, it will only target the elements that match our selector:

ul li:nth-last-of-type(-n+4) {
  color: green;
  }

We can be even more clever, and combine more than one of these pseudo-classes together on a massive selector. Let’s say all images within a post div to be floated left, except for the first and last one (let’s image these would full width, so they shouldn’t be floated):

.post img:nth-of-type(n+2):nth-last-of-type(n+2) {
  float: left;
  }

So in the first part of this selector, we are targeting every image starting from the second one. In the second part, we are targeting every image except for the last one. Because the selectors aren’t mutually exclusive, we can use them both on one selector thus excluding both the first and last element at once!

:last-child

The :last-child pseudo-class works just as the :first-child pseudo-class, but instead targets the last child of a parent element.

Let’s image you don’t want the last paragraph within your post div to have a bottom margin:

.post > p:last-child {
  margin-bottom: 0;
  }

This selector will target the last paragraph that is a direct and the last child of an element with the class of “post”.

:first-of-type and :last-of-type

The :first-of-type pseudo-class is used to target an element that is the first of its type within its parent.

For example, you can target the first paragraph that is a direct child of a particular div, and capitalize its first line:

.post > p:first-of-type:first-line {
  font-variant: small-caps;
  }

With this selector you make sure that you are targeting only paragraphs that are direct children of the “post” div, and that are the first to match our p element.

The :last-of-type pseudo-class works exactly the same, but targets the last child of its type instead.

:only-child

The :only-child pseudo-class represents an element that is the only child of its parent.

Let’s say you have several boxes (“news”) with paragraphs of text inside them. When you have more than one paragraph, you want the text to be smaller than when you have only one:

div.news > p {
  font-size: 1.2em;
  }
  
div.news > p:only-child {
  font-size: 1.5em;
  }

In the first selector, we are defining the overall size of the p elements that are direct children of a “news” div. On the second one, we are overriding the previous font-size by saying, if the p element is the only child of the “news” div, its font size should be bigger.

:only-of-type

The :only-of-type pseudo-class represents an element that is the only child of its parent with the same element.

How can this be useful? Image you have a sequence of posts, each one represented by a div with the class of “post”. Some of them have more than one image, but others have only one image. You want the image within the later ones to be aligned to the center, while the images on posts with more than one image to be floated. That would be quite easy to accomplish with this selector:

.post > img {
  float: left;
  }
  
.post > img:only-of-type {
  float: none;
  margin: auto;
  }

:empty

The :empty pseudo-class represents an element that has no content within it.

It can be useful in a number of ways. For example, if you have multiple boxes in your “sidebar” div, but don’t want the empty ones to appear on the page:

#sidebar .box:empty {
  display: none;
  }

Beware that even if there is a single space in the “box” div, it will not be treated as empty by the CSS, and therefore will not match the selector.

Notes on browser support
Internet Explorer (up until version 8) has no support for structural pseudo-classes. Firefox, Safari and Opera support these pseudo-classes on their latest releases. This means that if what it’s being accomplished with these selectors is fundamental for the website’s usability and accessibility, or if the larger part of the website’s audience is using IE and you don’t want to deprive them of some design details, it’s be wise to keep using regular classes and simpler selectors to cater for those browsers. If not, you can just go crazy!

7. The negation pseudo-class

The negation pseudo-class, :not(), lets you target elements that do not match the selector that is represented by its argument.

For example, this can be useful if you need to style all the input elements within a form, but you don’t want your input elements with the type submit to be styled — you want them to be styled in a different way —, to look more like buttons:

input:not([type="submit"]) {
  width: 200px;
  padding: 3px;
  border: 1px solid #000000;
  }

Another example: you want all the paragraphs within your post div to have a larger font-size, except for the one that indicates the time and date:

.post p:not(.date) {
  font-size: 13px;
  }

Can you image the number of possibilities this selector brings with it, and the amount of useless selectors you could strip out off your CSS files were it widely supported?

Notes on browser support
Internet Explorer is our usual party pooper here: no support at all, not even on IE8. This probably means that this selector will still have to wait a while before some developers lose the fear of adding them to their stylesheets.

8. Pseudo-elements

Pseudo-elements allow you to access elements that don’t actually exist in the HTML, like the first line of a text block or its first letter.

Pseudo-elements exist in CSS 2.1, but the CSS 3 specifications state that they should be used with the double colon “::”, to distinguish them from pseudo-classes. In CSS 2.1, they are used with only one colon, “:”. Browsers should be able accept both formats, except in the case of pseudo-elements that may be introduced only in CSS 3.

::first-line

The ::first-line pseudo-element will match the first line of a block, inline-block, table-caption or table-cell level element.

This is particularly useful to add subtle typographical details to your text blocks, like, for example, transforming the first line of an article into small caps:

h1 + p::first-line {
  font-variant: small-caps;
  }

If you’ve been paying attention, you’ll know that this means the paragraph that comes immediately after an h1 tag (“+”) should have its first line in small caps.

You could also refer to the first line of a particular div, without having to refer to the actual paragraph tag:

div.post p::first-line { font-variant: small-caps; }

Or go one step farther and target specifically the first paragraph within a particular div:

div.post > p:first-child::first-line {
  font-variant: small-caps;
  }

Here, the “>” symbol indicates that you are targeting a direct child the post div, so if the paragraph were to be inside a second div, it wouldn’t match this selector.

::first-letter

The ::first-letter pseudo-element will match the first letter of a block, unless it’s preceded by some other content, like an image, on the same line.

Like the ::first-line pseudo-element, ::first-letter is commonly used to add typographical details to text elements, like drop caps or initials.

Here is how you could use the ::first-letter pseudo-element to create a drop cap:

p {
  font-size: 12px;
  }
  
p::first-letter {
  font-size: 24px;
  float: left;
  }

Bear in mind that if you use both ::first-line and ::first-letter in the same element, the ::first-letter properties will override the same properties inherited from ::first-line.

This element can sometimes produce unexpected results, if you’re not aware of the W3C specs: it’s actually the CSS selector with the longest spec! So it’s a good idea to read them carefully if you’re planning on using it (as it is for all the other selectors).

::before and ::after

The ::before and ::after pseudo-elements are used to insert content before or after an element’s content, purely via CSS.

These elements will inherit many of the properties of the elements that they are being attached to.

Image you want to the words “Graphic number x:” before the descriptions of graphs and charts on your page. You could achieve this without having to write the words “Graphic number”, or the number itself yourself:

.post {
  counter-reset: image;
  }
  
p.description::before {
  content: "Figure number " counter(image) ": ";
  counter-increment: image;
  }

What just happened here?

First, we tell the HTML to create the “image” counter. We could have added this property to the body of the page, for example. Also, we can call this counter whatever name we want to, as long as we always reference it by the same name: try it for yourself!

Then we say that we want to add, before every paragraph with the class “description”, this piece of content: “Figure number ” — notice that only what we wrote between quotes will be created on the page, so we need to add the spaces as well!

After that, we have counter(image): this will pick up the property we’ve already defined in the .post selector. It will by default start with the number one (1).

The next property is there so that the counter knows that for each p.description, it needs to increment the image counter by 1 (counter-increment: image).

It’s not as complicated as it looks, and it can be quite useful.

The ::before and ::after pseudo-elements are often only used with the content property, to add small sentences or typographical elements, but here it’s shown how we can use it in a more powerful way in conjunction with the counter-reset and counter-increment properties.

Fun fact: the ::first-line and ::first-letter pseudo-elements will match the content added by the ::before pseudo-element, if present.

Notes on browser support
These pseudo-elements are supported by IE8 (not IE7 or 6), if the single colon format is used (for example, :first-letter, not ::first-letter). All the other major browsers support these selectors.

Conclusion

Enough with the boring talk, now it’s time for you to grab the information on this post and go try it for yourself: start by creating an experimental page and test all of these selectors, come back here when in doubt and make sure to always refer to the W3C specs, but don’t just sit there thinking that because these selectors aren’t yet widely supported you might as well ignore them.

If you’re a bit more adventurous, or if you’re not afraid of letting go of the past filled with useless and non-semantic classes and ids, why not sneak one or two of these powerful CSS selectors into your next project? We promise you’ll never look back.

References

Further Resources

(al)

Footnotes

  1. 1 http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/
  2. 2 http://meyerweb.com/eric/css/link-specificity.html
  3. 3 http://www.stuffandnonsense.co.uk/archives/css_specificity_wars.html
  4. 4 http://www.w3.org/TR/CSS2/cascade.html
  5. 5 http://www.w3.org/TR/CSS2/selector.html
  6. 6 http://www.w3.org/TR/css3-selectors/
  7. 7 http://en.wikipedia.org/wiki/Comparison_of_layout_engines_%28Cascading_Style_Sheets%29
  8. 8 http://www.w3.org/TR/CSS2/generate.html
  9. 9 http://www.alistapart.com/articles/keepelementskidsinlinewithoffspring/
  10. 10 http://css.maxdesign.com.au/selectutorial/index.htm
  11. 11 http://inspectelement.com/tutorials/a-look-at-some-of-the-new-selectors-introduced-in-css3/
  12. 12 http://www.456bereastreet.com/archive/200509/css_21_selectors_part_1/
  13. 13 http://www.456bereastreet.com/archive/200510/css_21_selectors_part_2/
  14. 14 http://www.456bereastreet.com/archive/200601/css_3_selectors_explained/
  15. 15 http://kimblim.dk/css-tests/selectors/
  16. 16 http://www.impressivewebs.com/10-useful-css-properties-not-supported-by-internet-explorer/
  17. 17 http://webdesignernotebook.com/css/styling-a-poem-with-advanced-css-selectors/
CSS

↑ Back to topShare on Twitter

Inayaili de León is a London-based Portuguese web designer, specialist in cross-browser, semantic HTML and CSS, and clean, functional design. She writes frequently for well-known online and print publications and also on her own web design blog, Web Designer Notebook. In May 2011, she published a book, Pro CSS for High Traffic Websites, and she speaks frequently at local and international web conferences and meetups. She is currently working as a web designer on Canonical's Design team.

Advertising
  1. 1

    Great post, i was literally “JUST” looking for somthing like this right now haha! THANKS!

    1st post, get in!

    -2
  2. 2

    Great article, bookmarkworthy!

    1
  3. 3

    Nice compilation of advanced CSS selectors. But most of them are not useable in Internet Explorer 6.

    Maybe interesting:
    I’ve written an article in my blog (in german), which explains how to use this great selectors with php (Simple XML DOM Parser): http://tinyurl.com/mb9ku7

    1
  4. 4

    very good article, i like the nth child selectors!!!! they are just great!!

    1
  5. 5

    great for link to file !

    1
  6. 6

    Great post, it is already in my bookmarks. Thank you.

    0
  7. 7

    Amazing post, I finished a big cup of coffee until I got to the end.

    Very useful information, thanks a lot Inayaili !

    0
  8. 8

    Amazing post!
    Bookmarkworthy it is…!
    Keep it up!

    0
  9. 9

    Very useful even for those who write a lot of CSS!

    1
  10. 10

    Of course these selectors have been around for quite a while, but they have, as the article states, hitherto been taken up by designers in a less-than ubiquitous fashion. The blame for that must inevitably lie with the perceived need to support browsers that did not recognise the more advanced CSS selectors. Yes, IE6, we’re all looking at you, you godless pig of an application.

    I think that the big issue for many designers was that if you used advanced css selectors to do things the ‘right way’ then you inevitably had to jump through a load of extra hoops and introduce irksome extra workarounds if you wanted the page to render the same way in IE6, whereas by adopting a not-quite-as-right-way (extra classes, ids, etc) you generally only had to do the job once. Not 100% standardista mentality perhaps, but largely understandable.

    With these legacy browsers now beginning to fade away (or, at least, with a growing acceptance that users of older browsers probably are not going to receive all the visual niceties that their more up-to-date compadrés enjoy), more designers will hopefully now be able to offer these more advanced selector options the wholehearted embrace that they deserve.

    1
  11. 11

    Very informative post… Thanks!!!

    0
  12. 12

    Wow! SS how you can always make an interesting and very useful articles?? Thx!!

    0
  13. 13

    Love it! More than makes up for the dodgy TV shows post.

    0
  14. 14

    wow…that’s a really big post i’ll take some more time to read :) very interesting and useful information so far. big reference. gj.

    0
  15. 15

    Thanks so much for this. Really advanced stuff. Thanks!

    0
  16. 16

    This is a huge post, as many of you guys have already said, but it’s huge in many ways. This is a “Have to read” if you’re in webdesign business.

    -1
  17. 17

    Very interesting article, but is it me, or does alot of this not work in -any- version of IE?
    Wouldn’t it be a good idea to mention this in our post?

    0
  18. 18

    Great Article!!!

    In the first para of the article you wrote “But despite the fact that we are all well aware of its usefulness, CSS selectors are still not used to their full potential and we sometimes have the tendency to litter our HTML with excessive and unnecessary classes and ids, divs and spans.”

    This is due to IE

    0
  19. 19

    Nice article

    Check this page (not mine) for an overview of selectors:
    http://kimblim.dk/css-tests/selectors/

    0
  20. 20

    no earthly reason. kill all IE – use css3.

    0
  21. 21

    Inayaili de León great post, but what would have made this even better would be a link to browser support for CSS Selector. Thanks for all the hard work.

    0
  22. 22

    Counter?

    How did I miss it?

    CSS rules and IE sucks!!!

    -1
  23. 23

    Great Post! One point of order, however. Under the first point “1. Attribute Selectors”, there are two erroneous descriptions…

    >> [att$=value]
    >> The attribute’s value contains the specified value.

    The “$=” actually represents “ends with”, not “contains”. This is adopted from Regular Expressions, which use the “$” meta-character to identify the end of a given string.

    Furthermore, the following is also incorrect:

    >> [att*=value]
    >> The attribute’s value ends with the specified value.

    The “*=” selector represents “contains”, not “ends with”. Again, this convention was borrowed from RegEx implementations. Please note that the example following these explanations also implements this mistake.

    As always, keep up the great work!

    2
  24. 24

    really great CSS resource, thanks!

    0
  25. 25

    I’ve to say, that I still never have commented on SmashingMagazine before, so that’s my first comment and this article describes exactly what I’m looking for: Advanced CSS Techniques and their HowTo’s. Great Job, León!

    0
  26. 26

    You made a mistake with the Attribute Selectors:
    [att$=value] selects elements where the attribute ENDS with the value
    [att*=value] selects elements where the attribute CONTAINS the value

    Good article otherwise, I never realised there were so many pseudo selectors!

    @Jason: If you like you can test them yourself using . Prepare for lots of red in Internet Explorer!

    0
  27. 27

    Great article. Brings a whole new perspective to styling. The sooner we all move on and forget about the historical browsers the better…

    0
  28. 28

    @Keegan: Thanks for noticing that! I’ll see that it gets corrected ;)

    0
  29. 29

    Very helpful tips for writing CSS. Thanks!

    0
  30. 30

    doorlopenmanloopdoorman

    August 17, 2009 7:27 am

    I don’t care for these selectors at all. It might be the sugar on the cake, but if IE6 starts coughing when it takes a small bite, it is best to forget about them.

    These selectors are not viable for use in a commercial application yet. Just give the anchors that link to pdf a class of “pdf-link”. No loss in semantics and works instantly in most browsers.

    Wake me up when IE6 is dead enough, right now this is just not practical, apart from some experimentation. Everything you can do with these selectors you can select by other means + some classy markup. Heavy use of these selectors, does not only make your stylesheets more complex, it removes usability/layout/functionality layers from IE6 when this not neccessary.

    Heavy use of these css selectors is bordering bad practice when you have to cater to IE6 too.

    1
  31. 31

    Nice Post! Thanx for sharing!!

    1
  32. 32

    @doorlopenmanloopdoorman – You are totally correct that it’ll be some time yet before these selectors can be used in enterprise apps, as the need to support IE6 is still large for most organizations. With that being said….

    >> Therefor I go as far as calling use of css selectors as these: bad practice

    Ridiculous. Knowing WHEN and HOW advanced features can be used is absolutely critical, but learning new standards and the direction the web is taking in general can never be bad. That is a crazy thing to say. Furthermore, most Javascript libraries (YUI, jQuery, Dojo, etc) all support these selectors, so learning them now can only make using those libraries easier. Would I recommend using these features sparingly, and only after thorough testing? Of course. Would I call the use of these features “bad practice”? Not at all, unless you are content to let the browser’s implementations determine how much you learn.

    0
  33. 33

    Love this article. Really pushing the boundaries of CSS. Keep more articles like these coming. MUST get Smashing Magazine book!

    0
  34. 34

    Are there any large petitions out there to get the major websites (Google, Amazon, etc.) to block IE6? That way lazy IT departments will be forced to update, and we can all quit worrying about whether or not something works in IE6. It is just downright ridiculous that designers and developers are being forced to hold back their potential because people refuse to update their software. Continued support of old software will just make people continue to not upgrade.

    0
  35. 35

    This is straight up THE most useful CSS article I’ve ever read. Awesome, awesome stuff.

    0
  36. 36

    I’d love an :n-th-line selector to underlay every second line in a long text to make it easier to read.

    0
  37. 37

    Did it again Smashing!! Awesome.

    0
  38. 38

    Very useful. Nice!

    0
  39. 39

    i’ve seen these in jquery first xP

    0
  40. 40

    Another useless pile of CSS3 and 2.1 “tricks” that
    I could NEVER use until all IE6 users are gone…

    Its like a trust fund that makes you rich on paper,
    but cannot be used until 30 years from now

    0
  41. 41

    FF3.5 understands this

    h1:first-letter

    but when I try with

    h1 a:first-letter

    then the style is not applied
    Can somebody help me with this please

    0
  42. 42

    Wow! This is going in my next project. Hats off, SM.

    0
  43. 43

    I had the opportunity to code something that wasn’t required to work in IE6. Even the limited number of these selectors that work with IE7 opened up a whole world of new css. Here was my favorite:
    div#EditBox form fieldset.Address > label:first-child+input

    I can’t wait until I can really embrace these. Sadly, it will probably be a very long time.

    0
  44. 44

    I love it so much! Thanks for sharing about this! :)

    0
  45. 45

    “…you should make sure that IE6 users will still be provided with a usable site…”

    No you shouldn’t. I believe any web designer worth his/her salt should ignore IE6 altogether. Why would you even suggest pandering to such an obsolete and unsupported browser? All you’re doing is perpetuating a broken web. Personally I gave up all support for IE6 18 months ago and actually block it from all my sites. I have this written into my client contract as a fundamental requirement of taking on the job. If a client demands IE6 support I suggest, in the nicest possible way, that they find someone else. They soon come around when they realise the time/cost implications of debugging and the lack of design flexibility inherent in catering to such redundant software. This strategy has actually gained me clients.

    Other than that, good article!

    1
  46. 46

    Amazing. Never seen that complete before.

    0
  47. 47

    Wow that’s incredible. This is going to take a while to digest, but then… wow, we’ll be unstoppable

    0
  48. 48

    Amazingly well explained. Wish there are some photodiagram showing how it works.

    Wherever there is CSS tips and tricks, there always ie6 :D

    Just hope ie6 users would Switch (or upgrade) to the latest web browser.

    0
  49. 49

    Thoroughly agree with aj – it’s time to draw the line in the sand and stop supporting IE6. Every site I have built in the last 6 months has had an upgrade notice in a big obnoxious box conditionally displayed for IE6 users. IE6 users are also informed that they are not receiving the intended experience, and the site may not work as expected. The onus is on them to upgrade.

    There’s really no excuse to use it today – if you’re bound to IE6 to support old intranet applications behind a corporate firewall, it’s the responsibility of your IT staff to roll out an alternative browser for everything else. Choosing not to support IE6 is the right choice – the more sites that actively direct people to upgrading to a contemporary browser the better.

    0
  50. 50

    I always forget about advanced selectors when coding sites, which of course leads to a ridiculous amount of IDs and classes. I’m going to challenge myself to make a fairly complex website layout using no IDs and sticking to semantic elements. That should be fun :D

    0
  51. 51

    WOW! Great! It took me a while to read all, but it’s worth it! Thanks for sharing!

    0
  52. 52

    thanks for your usefull tip, its will help alot for my work :) my first time read about this

    0
  53. 53

    Very complete. Thanks a lot !

    0
  54. 54

    WoW, I Love this Article.
    I use this methods ine my projects

    0
  55. 55

    Very well thought out article; Inayaili is awesome! This is a great article for those of us who are well versed in CSS but only use half of its capabilities in the trenches every day. I had actually never heard of the counter-reset: method, that is wicked cool, almost like a PHP function built into CSS! Keep up the good work SM!

    0
  56. 56

    Browser compatibility is one thing. Now I would love to know about performance.

    Some of the more advanced selectors must surely require more browser thinking than simple ones. When used alone, or in conjunction, how do they affect the browser’s rendering speed? (vs slightly more verbose markup with simpler CSS)

    Also, IE is not the only scapegoat. Many mobile browsers barely handle CSS at all. (Although if you’re designing for mobile handsets, the negation pseudo-class is probably the least of your worries.)

    0
  57. 57

    Can’t them Windows making evil people just send out a line of code to ALL machines running Windows and have it automatically DELETE IE6 of every single machine in the world?

    ….just a though ;-)

    Awesome article… bookmarked for many a future referencing. Thank mate

    0
  58. 58

    Very good post! Txs

    0
  59. 59

    i always look forward to them n using them.

    0
  60. 60

    Hope everything will be in the Smashing books !! Can’t wait to receive it…!
    You guys rock, most of your article are gold to me !

    0
  61. 61

    Till IE won’t be dead, we have no chance to use this treasure properly… :(

    0
  62. 62

    Fantastic. Thank you

    0
  63. 63

    VERY, VERY useful post, thank you!

    0
  64. 64

    Nice

    IE6 is a problem, but have you tried opening YouTube with it.
    Good decision YouTube. I am sure many will follow.

    0
  65. 65

    If these do not work in IE6 then most of them will not be usable, is easy enough to say “block” IE6 but then you block a large majority of users in corporate environments that still use it.

    It’s a start but until IE6 dies, then our lives will still be filled with late nights getting sites to work with IE6. I hate it as much as the next person.

    0
  66. 66

    @James (55):
    You are right, advanced selectors can impact browser performance. I am not sure how bad the pseudo classes are compared to other selectors, but a general rule for good performing selectors is to be as specific as possible – ideally start all your rules with an id selector. Yes, this contradicts this article to a certain extent, but it really helps performance, especially if you have large pages with many elements and deep nesting.
    There is a good writeup in the for Google Page Speed at http://code.google.com/intl/de-DE/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors

    0
  67. 67

    Brilliant post! Unfortunately, I can’t use any of these selectors because I’m still designing websites for IE6. I have to keep my CSS simple and avoid any hacks. It’s rubbish!

    0
  68. 68

    I believe any web designer worth his/her salt should ignore IE6 altogether. Why would you even suggest pandering to such an obsolete and unsupported browser? All you’re doing is perpetuating a broken web. Personally I gave up all support for IE6 18 months ago and actually block it from all my sites.

    I don’t agree with those who say that web designers should adopt a blanket policy to exclude IE6 from our consideration. I think that those who take such a stance simply have in mind those individual users who can’t be bothered to upgrade browsers or whatever. The reality is that this is not the user base that is stopping IE6 from dying the natural death that is so overdue; the real bottleneck, as digisam correctly points out, is those large corporate bodies.

    Remember that IE6 is linked to the operating system; for many corporate IT networks upgrading to a later version of IE would mean upgrading the operating system globally across the entire company (possibly across many countries). That is not something that they are going to undertake lightly, especially when they are likely to use a lot of proprietary software that requires the existing OS and/or browser to function correctly.

    Experience of my own clients has shown that many of the biggest XP/IE6 diehards are government bodies with responsibility for the safety and well-being of millions of people. They are certainly not going to put that responsibility at risk simply in order to appease web designers who want to use some fancy tricks to streamline their markup.

    Nobody likes IE6. Many of those who use it would not do so if they had a choice; even the company that built it wishes it could make the damn thing’s legacy disappear. But as jobbing web designers our job is to build sites that work for those people who wish to visit them, and if they are stuck with an ancient rubbishy browser because of their job then we should accommodate them. I’m not saying that we need to jump through all the hoops to get IE6 to look the same as a ‘real’ browser, but I think that we should, currently, avoid making the site dependant upon such advanced CSS selectors and reserve them for progressive enhancement purposes. IE6 users will perhaps see a visually-poorer site, but they will still be able to see it and it will make sense.

    Design is about providing solutions to real-world problems, not grandstanding about how things ‘should be’ (unless we’re building a site specifically to show-off our cutting-edge, code-ninja prowess). By all means charge more for IE6-related development, and leave out the progressive enhancement whistles and bells. But don’t exclude it altogether unless the client really understands the implications. And please don’t just take the easy option of saying that everybody who uses IE6 is stupid and wrong, because the truth is more complicated than that.

    1
  69. 69

    Nice post Rick (post 66).

    I too find it amusing to see all these posts saying to hell with IE6 and yet the point of any designer is to achieve maximum visits and sales for the client. I would love to use these and I would love to use CSS3 curved borders but the simple fact is in every single job I’ve ever had (bar none) require a pixel perfect design which will look exactly the same across all browsers.

    I admit I only work for very large companies (employees above 10,000) but the designs can take months to get approval and once they do that is exactly what they want, regardless of how old the browsers are.

    Most sites I work on have IE6 users between 20% and 65% so it will be many years yet before I can lay IE6 in the grave it so rightly deserves…

    0
  70. 70

    @Rick
    Agreed. Unfortunately, as we all know (but most seem to ignore) many company intranets & apps are tied into IE6. Upgrades to FF may be free but the cost of re-working internal software to work with newer browsers is something that no MD should be able to justify in this current climate. Youtube may have blocked IE6 but that’s not exactly a website anyone should be visiting at work.

    @AJ
    It might be worth pointing out that browser stats can be very, very grey and vary from industry to industry/country to country. Of course most of my own sites visitors will be running Firefox/IE7-8/Chrome/Safari but not all websites attract the same demographic. I’ve recently finished a site for a UK/Italian Logistics company. Just over 35% of their hits were from machines running IE6, (in 2009!), the vast majority of which were from Italy. I’d have lost a nice chunk of business if I’d held my hands up and told my client that since it’s old technology, IE6 shouldn’t be supported. It’s up to us as Designers and Dev’s to make sure that the web is accessible to EVERYBODY, regardless of their tools. If it takes a little longer to fix a bug in IE6, charge for it. If you want to do a little trickery that works in newer browsers but not in IE6, do it anyway. Who said that websites have to look the same in every browser?

    0
  71. 71

    Great post!! Thank you!!!

    I agree with those who explained why is not that “easy” the migration of IE to a newest version. However, as a web developer I believe we might do some “tricks” to “force” people (and companies) to change (I liked Pete’s idea of charging an extra fee for bug fixes). As a matter of fact is our responsibility.

    Cheers :)

    0
  72. 72

    A handy article, I’ve bookmarked and will be referencing in the near future. Shame IE6 will be a throne our side for a while yet.

    0
  73. 73

    Moral of the story. Stop supporting IE6.

    0
  74. 74

    Very great post! Thanks a bunch.

    0
  75. 75

    May be in 20 years this post will be very useful for commercial use. But great post

    0
  76. 76

    So I understand how to calculate specificity (I have a different method). But this method is explained very poorly:

    From the following selectors, the first one is the one who will be applied to the element, because it has the higher specificity:
    • #sidebar p#first { color: red; } — 0, 2, 0, 1
    • #sidebar p:first-line { color: blue; } — 0, 1, 0, 2

    How does it have a higher specificity? There’s no explanation. From my understanding of specificity, I can conjecture that the author means to take the two numbers, smush them together, remove the spaces and commas, and compare them: 201 > 102 and therefore has a higher specificity and that rule will take precedence. OK, that makes sense to me, but this is not explained and some people may not understand what’s going on here.

    It’s really just simpler to say the following:

    1,000 points for inline CSS
    100 points for each ID
    10 points for each class and/or attribute selector
    1 point for each html element

    Add up the points and whatever rule has the most points, wins. I think that’s a little easier for people to wrap their head around, after all, it’s easier to keep one number in your head than to try to memorize four different numbers.

    0
  77. 77

    @Ryan
    That was the first thing that came to my mind. U r just one post faster ^^
    The point system is much better/more easy in my eyes.

    1
  78. 78

    This is a great article. Used the attribute selector tip to make a highlight color for required fields in a web form. Good stuff! (IE6, just DIEEEEE already you hellacious browser!)

    0
  79. 79

    A fantastic article! I had no idea you could do most of that stuff and it would all be really useful were it not for the fact that IE doesn’t support most of these methods…

    0
  80. 80

    I find it surprising that so many comments talk about ignoring IE6′s prevalence. Cross-browser testing is extremely important, especially when it comes to looking at your target audience. If you’re say, a non-profit that helps poorer communities, your audience may be working on an older version of Windows OS and not upgrading their browser. I’ve found that global users are also more likely to be on IE6 or even Netscape. From thecounter.com stats (the last set is from March), IE6 is shown to be the 2nd most popular browser to IE7 (Firefox is #3, Safari #4). I’m sure the stats are different now that IE8′s been out, but IE6 is really hanging on and I don’t see it going away as quickly as some expect.

    Aside from that, I agree that it’s frustrating to work with these CSS techniques when you have to create workarounds for nearly every single one. So, you have two choices– use them and create workarounds or not use them. I think most clients and companies you’re working for would not see the “screw IE6″-attitude as an option. I do agree with those that are saying that a less snazzy version of the design can be implemented for IE6, if need be, though I don’t think it should necessarily be bad design as punishment for the user. There’s enough bad design out in the world, why add to it?

    0
  81. 81

    Thanks, perfect article.

    0
  82. 82

    @Sarah
    I agree that an IE6 version of a site should not be a ‘bad design’, but I do think that there is a debate to be had regarding what constitutes bad design in this context. To my mind the IE6 version must be fully functional so as not to exclude the audience, and should at least come close to the ‘proper’ design in look and feel. But when it comes to the designer jumping through all those hoops to make the site as ‘pretty’ (or as close as possible) in IE6 as in a standards-compliant browser, I think that is a budgeting issue for the client to decide. It can be done, but the client has to acknowledge that is is not generally possible without extra work, and that work needs to be paid for.

    0
  83. 83

    There are great css selectors, but a lot of them are for CSS level 3, and there are not admited by some ( old ) browsers, so you cannot use it for now

    0
  84. 84

    Amazing post! Very usefull. Thanks a lot for sharing this!

    0
  85. 85

    Kartlos Tchavelachvili

    August 22, 2009 10:45 am

    very Interesting, I have bookmarked this page. thanks!

    0
  86. 86

    Andreas Lagerkvist

    August 27, 2009 4:35 am

    Nice write-up. I can recommend Dean Edward’s IE7.js: Link which makes IE5 and 6 understand all of the selectors that 7 understands. It also fixes PNG-transparency and a few other bugs. Can be a bit slow on large documents but it’s really nice to be able to use :last-child, ::before, ::after and so on cross-browser.

    0
  87. 87

    I love this article

    However, I also agree with digisam (#65)
    IE6 is a nightmare. We could hardly get a sound sleep until it dies.

    0
  88. 88

    Good article.

    This type of CSS is almost at the level of programming.

    0
  89. 89

    Nice post, although the portion on specificity was pretty worthless and didn’t explain much at all, especially for anyone who doesn’t know what it is exactly.

    0
  90. 90

    Very useful post. I first read it when it was published but must have been back 50 times since for reference.

    0
  91. 91

    greetings, I can’t find your contact information but your layout design looked off on firefox and opera. Anyways, i just suscribd to your rss.

    0
  92. 92

    I just purchased a new coffee roaster and I love it. I liked your post and I am going to try out some of your tips. Thanks!

    0
  93. 93

    This article is awesome….

    2
  94. 94

    I don’t think I saw this post when it was first published (or I just forgot about it), but I just rediscovered it and it’s totally awesome!

    It’s great how much more you can achieve with CSS when you dig a little deeper.

    0
  95. 95

    I absolutely love CSS3 advanced selectors! I wish IE would get on board so we could let go of conditional comments and hacks.

    0
  96. 96

    This really saved me. Thanks!

    0
  97. 97

    Pretty amazing article. I never knew about many of these but I’m not so sure about using these especially for CSS3 because it will take some time for other browsers to catch up. Well done!

    0
  98. 98

    Inayaili,
    a very detailed description of CSS selectors, my compliments. It is a very helpful resource for those approaching this stuff.

    Those saying this is not useful because of IE’s lack of support are wrong. The reason is any browser older than 1 year has the same kind of problems (and with different selectors). An “accurate” enough selector engine allow using CSS3 selectors cross-browser today (even for very old browsers like IE6).

    The problem is that popular frameworks contains inadequate selector engines returning different result sets in different browsers and are inconsistent across different versions of the same browsers. Frameworks were meant to fix bugs and inconsistencies and help developers making things work cross-browser, so they just failed their primary objective.

    2

↑ Back to top