Using :first-child:nth-last-child() to apply styles based on number of items

In a recent project, I stumbled across the need to count items (and determine width based on number of siblings).

Normally in a situation like this, I'd turn to the server-side and simply counted the items and applied a class or data-* attribute on each item or a parent container. But far along the progress of the project, it became evident that server-side was not an option and I began searching towards a clientside solution. And since I didn't want to be fully dependent on Javascript, a solution using pure-CSS became desirable. And I found this brilliant solution.

The break down

This solution gets it's power from using two standard CSS-selectors; :first-child(n) and :nth-last-child(n).

:first-child

The :first-child selector applies only to the first child of parent.

:nth-last-child()

The :nth-last-child(n) selector let's us, just like :nth-child(n), select any nth child of parent - but counting from last-child.

The magic

This is pure logic; if the first item in list is positioned X positions by counting backwards, then we know how many items our list contains. I.e. if we'd target an only-child item in a list it would be written something like this:

li:first-child:nth-last-child(1) {
/* One item */
}

But, this only affects the first item. If, say, we'd like to target all items in a list containing two or more items we can take usage of the standard ~ selector to select elements preceded by our item. And since we're always proceeds from our :first-child, every item will be targeted.

For example:

li:first-child:nth-last-child(2) {
    /* This rule only affects our first item, if it's number two counting backwards */
    background-color: #e0e0e0;
}

li:first-child:nth-last-child(2) ~ li {
    /* This rule does not affect the first item, but every item that's preceded by the first item.. */
    background-color: #d0d0d0;
} 

(Fiddle)

Extension and refinements

As this solution's based purely on CSS3-selectors it's simple and very extensible. Since it uses standard selectors you simply can adapt and build upon this to match your needs. E.g. let's say you'd want to target first-child (or siblings) and apply styles if it's position odd or even:

li:first-child:nth-last-child(odd) {
    background-color: #d0d0d0;
}
li:first-child:nth-last-child(odd) ~ li {
    background-color: #e9e9e9;
} 

(Fiddle)

Use this solution by determine width on items

In my case, I wanted to set different widths on items based on number of items. This became my solution. (Not unlike the original code)

li:first-child:nth-last-child(n),
li:first-child:nth-last-child(n) ~ li {
    width: (100%/n)%;
}

(Fiddle)

Closing comments

As I strive towards clean and simple markup I constantly looking towards solutions where I'm not bound to use server-side output or DOM manipulation using Javascript, this solution stands as proof that CSS is powerful enough to act alone. And please note, this is not my original invention, this discovery just made my day and I wanted to share it.

Credits: