CSS :nth-of-class selector

One thing that bothers me with CSS :nth-of-type/:nth-child selectors is that you can’t combine them with CSS classes. The selector below for example won’t work as you’d first expect it to work:

.bar:nth-child(2) {
  color: red;
}

No, the selector above won’t color the second .bar element red, something I had somehow expected it to do. What the selector will target instead, is an element that is both .bar and :nth-child(2):

See the Pen .class:nth-of-type by Bramus (@bramus) on CodePen.

🐛 This article previously wrongly assumed that the browser would somehow know that .bar happens to be an article element. That assumption was incorrect and this post has been adjusted to no longer contain this error. Thanks to @simevidas for pointing this out.

~

To solve my problem, I’d be needing something like a :nth-of-class selector. Unfortunately no such selector exists nor is it currently proposed. What is proposed though, in the upcoming CSS Level 4 Selectors specification (aka “not CSS4” 😜), is an extension to the nth-child/nth-last-child pseudo selectors: the ability to add an extra "of S" part into those selectors.

The :nth-child(An+B [of S]?) pseudo-class notation represents elements that are among An+Bth elements from the list composed of their inclusive siblings that match the selector list S. If S is omitted, it defaults to *|*.

Given that, our wanted :nth-of-classselector becomes a reality:

article:nth-child(2 of .bar) {
  color: red;
}

Here’s a pen with the result:

See the Pen .class:nth-of-type by Bramus (@bramus) on CodePen.

If you’re using Firefox or Chrome you’ll see that the demo above is broken, as browser support for it is bad. Only Safari supports it at the time of writing.

💡 Shown above is a dynamic CanIUse.com image, showing an always up-to-date support table. By the time you are reading this browser support might have become better.

Other browser that currently don’t support it have meta-issues to implement CSS Level 4 selectors:

Please vote on those bugs mentioned above if you want those features to land in the browsers.

🗳 By massively voting on browser bugs we can “tell” a browser vendor that we’d like to see a certain feature land. Go forth and vote.

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

A Complete Guide to Data Attributes

I like using data attributes in my markup (data-*), CSS ([data-*]), and JS (el.dataset). This post on CSS-Tricks writes down exactly how I use them.

Especially the different type of attribute selectors are really powerful:


// Seven different types of CSS attribute selectors

// This attribute exists on the element
[value]

// This attribute has a specific value of cool
[value='cool'] 

// This attribute value contains the word cool somewhere in it
[value*='cool'] 

// This attribute value contains the word cool in a space-separated list
[value~='cool'] 

// This attribute value starts with the word cool
[value^='cool'] 

// This attribute value starts with cool in a dash-separated list
[value|='cool'] 

// This attribute value ends with the word cool
[value$='cool'] 

The contains selector becomes really powerful when building a JavaScript filter: such a CSS selector can be passed directly into document.querySelectorAll. The yielded result will immediately contain to correct selection, instead of looping over all items that and checking them one-by-one.

A Complete Guide to Data Attributes →

CSS-Only Multiple Choice Quiz

Matthew Somerville:

I followed a link to one of those Guardian end-of-year quizzes on my phone, and had answered a few questions before realising that it was working entirely without JavaScript (I have JavaScript disabled on my phone).

I found this very impressive, well done whoever worked on that, and so I thought I would take a look at how exactly they did it.

The code starts off with a basic set of radio buttons. Using the :checked and :valid attribute selectors (along with some combinators) the right/wrong answers are highlighted once an answer has been given. Changing your answer is also prevented that way. Using CCS Counters – one of CSS’s under-utilised features – the score is kept.

⚠️ Do note that you’re leaking the correctness of answers through your markup this way … so don’t you go making exams with these!

I’ve put all the code together in the pen embedded at the top of this post.

CSS-only multiple choice quizzing →

🔥 Using the same technique, you can also create custom CSS checkboxes or even create interactive emails!

How the CSS :is() selector will simplify things

One of the selectors in CSS Level 4 is :is(). It is the successor to :any() and :matches() (which are supplanted by :is()):

The :is() CSS pseudo-class function takes a selector list as its argument, and selects any element that can be selected by one of the selectors in that list. This is useful for writing large selectors in a more compact form.

/* Without :is */
article > h1,
article > h2,
article > h3,
article > h4,
article > h5 {
  /* … */
}

/* With :is() */
article > :is(h1, h2, h3, h4, h5) {
  /* … */
}

Browser Support isn’t quite there yet though, as they’re either all behind feature flags or use the outdated :matches() or (prefixed) :any()

MDN Web Docs; :is() (:matches(), :any())

Video via @argyleink

The future of CSS: Nesting Selectors

Early March the first draft for the CSS Nesting Module was published. The draft outlines a future mechanism by which we’ll be able to nest CSS selectors natively (e.g. in pure CSS, without the use of any preprocessors)

This module describes support for nesting a style rule within another style rule, allowing the inner rule’s selector to reference the elements matched by the outer rule. This feature allows related styles to be aggregated into a single structure within the CSS document, improving readability and maintainability.

Using the & selector (read this as “the nesting selector”), you can refer to the elements matched by the parent rule:

table.colortable {
  & td {
    text-align: center;
    &.c { text-transform: uppercase }
    &:first-child, &:first-child + td { border: 1px solid black }
  }
  & th {
    text-align: center;
    background: black;
    color: white;
  }
}

As it’s an actual selector, you always need to use the & selector, so you won’t be able to do this:

/* INVALID */
.foo {
  .bar {
    color: hotpink;
  }
}

(The fix would be to write & .bar instead of .bar)

The & selector can also only be used when it’s first compound selector (e.g. it appears at the first position) of an inner selector.

/* INVALID */
.foo {
  .bar & {
    color: hotpink;
  }
}

To cater to these scenarios you’ll need to use the @nest rule, which is more lax:

/* VALID */
.foo {
  @nest .bar & {
    color: hotpink;
  }
}

To me it feels like this @nest rule should go. The interpreter should be smart enough to figure things out all by itself.

CSS Nesting Module (Draft) →

CSS attribute value less than / greater than / equals selectors

Yesterday Ana Tudor wondered if should could write CSS selectors using less-than and greater-than logic:

Unfortunately this kind of selectors don’t work (yet). Until then one could use JavaScript to tackling this problem, or use a pure HTML/CSS-based solution.

~

The HTML/CSS solution is a 2-step process/hack:

  1. Inject all the values that the item is not equal to using data-* attributes.
  2. Use the CSS Attribute “Value Contains” Selector (e.g. [attribute~=value]) on those data-* attributes.

1. Injecting extra data-* attributes

Say you have a list of 10 items (from 1 to 10):

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
</ul>

About the 7th item you can state that it:

  • it equals (eq) 7
  • it is greater than or equals (gte) 1, 2, 3, 4, 5, 6, and 7
  • it is greater than (gt) 1, 2, 3, 4, 5, and 6
  • it is less than or equals (lte) 7, 8, 9, and 10
  • it is less than (lt) 8, 9, and 10

Translated to new data-* attributes, the HTML for this 7th item becomes this:

<li data-val-eq="7"
    data-val-gte="1 2 3 4 5 6 7"
    data-val-gt="1 2 3 4 5 6"
    data-val-lte="10 9 8 7"
    data-val-lt="10 9 8">
7</li>

About all other items you can state the same things. Using a template engine, such as Jade, you can easily script this kind of stuff automatically and eventually end up with this markup:

<ul>
	<li data-val-eq="1" data-val-gte="1" data-val-gt="" data-val-lte="10 9 8 7 6 5 4 3 2 1" data-val-lt="10 9 8 7 6 5 4 3 2">1</li>
	<li data-val-eq="2" data-val-gte="1 2" data-val-gt="1" data-val-lte="10 9 8 7 6 5 4 3 2" data-val-lt="10 9 8 7 6 5 4 3">2</li>
	<li data-val-eq="3" data-val-gte="1 2 3" data-val-gt="1 2" data-val-lte="10 9 8 7 6 5 4 3" data-val-lt="10 9 8 7 6 5 4">3</li>
	<li data-val-eq="4" data-val-gte="1 2 3 4" data-val-gt="1 2 3" data-val-lte="10 9 8 7 6 5 4" data-val-lt="10 9 8 7 6 5">4</li>
	<li data-val-eq="5" data-val-gte="1 2 3 4 5" data-val-gt="1 2 3 4" data-val-lte="10 9 8 7 6 5" data-val-lt="10 9 8 7 6">5</li>
	<li data-val-eq="6" data-val-gte="1 2 3 4 5 6" data-val-gt="1 2 3 4 5" data-val-lte="10 9 8 7 6" data-val-lt="10 9 8 7">6</li>
	<li data-val-eq="7" data-val-gte="1 2 3 4 5 6 7" data-val-gt="1 2 3 4 5 6" data-val-lte="10 9 8 7" data-val-lt="10 9 8">7</li>
	<li data-val-eq="8" data-val-gte="1 2 3 4 5 6 7 8" data-val-gt="1 2 3 4 5 6 7" data-val-lte="10 9 8" data-val-lt="10 9">8</li>
	<li data-val-eq="9" data-val-gte="1 2 3 4 5 6 7 8 9" data-val-gt="1 2 3 4 5 6 7 8" data-val-lte="10 9" data-val-lt="10">9</li>
	<li data-val-eq="10" data-val-gte="1 2 3 4 5 6 7 8 9 10" data-val-gt="1 2 3 4 5 6 7 8 9" data-val-lte="10" data-val-lt="">10</li>
</ul>

2. Using [attribute~=value] selectors

With those extra data-* attributes in place, we can now make good use of [attribute~=value] selectors:

  • Say we want to select all items that are greater than 8. To do so we select all items where [data-val-gt] contains the value 8.
    → In CSS: [data-val-gt~=8].
  • Say we want to select all items that are less than or equal 4. To do so we select all items where [data-val-lte] contains the value 4.
    → In CSS: [data-val-lte~=4].
  • Say we want to select all items that are between 5 and 9. To do so we select all items where [data-val-gt] contains the value 5 and [data-val-lt] contains the value 9.
    → In CSS: [data-val-gt~=5][data-val-lt~=9].

~

Demo

Here’s a demo pen which puts it all together:

See the Pen CSS attribute value less than / greater than / equals by Bramus! (@bramus) on CodePen.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)