An nth-letter selector in CSS

Variable fonts are a very exciting and powerful new addition to the toolbox of web design. They was very much at the centre of discussion at this year’s Ampersand conference.

A lot of the demonstrations of the power of variable fonts are showing how it can be used to make letter-by-letter adjustments. The Ampersand website itself does this with the logo. See also: the brilliant demos by Mandy. It’s getting to the point where logotypes can be sculpted and adjusted just-so using CSS and raw text—no images required.

I find this to be thrilling, but there’s a fly in the ointment. In order to style something in CSS, you need a selector to target it. If you’re going to style individual letters, you need to wrap each one in an HTML element so that you can then select it in CSS.

For the Ampersand logo, we had to wrap each letter in a span (and then, becuase that might cause each letter to be read out individually instead of all of them as a single word, we applied some ARIA shenanigans to the containing element). There’s even a JavaScript library—Splitting.js—that will do this for you.

But if the whole point of using HTML is that the content is accessible, copyable, and pastable, isn’t a bit of a shame that we then compromise the markup—and the accessibility—by wrapping individual letters in presentational tags?

What if there were an ::nth-letter selector in CSS?

There’s some prior art here. We’ve already got ::first-letter (and now the initial-letter property or whatever it ends up being called). If we can target the first letter in a piece of text, why not the second, or third, or nth?

It raises some questions. What constitutes a letter? Would it be better if we talked about ::first-character, ::initial-character, ::nth-character, and so on?

Even then, there are some tricksy things to figure out. What’s the third character in this piece of markup?

<p>AB<span>CD</span>EF</p>

Is it “C”, becuase that’s the third character regardless of nesting? Or is it “E”, becuase techically that’s the third character token that’s a direct child of the parent element?

I imagine that implementing ::nth-letter (or ::nth-character) would be quite complex so there would probably be very little appetite for it from browser makers. But it doesn’t seem as problematic as some selectors we’ve already got.

Take ::first-line, for example. That violates one of the biggest issues in adding new CSS selectors: it’s a selector that depends on layout.

Think about it. The browser has to first calculate how many characters are in the first line of an element (like, say, a paragraph). Having figured that out, the browser can then apply the styles declared in the ::first-line selector. But those styles may involve font sizing updates that changes the number of characters in the first line. Paradox!

(Technically, only a subset of CSS of properties can be applied to ::first-line, but that subset includes font-size so the paradox remains.)

I checked to see if ::first-line was included in one of my favourite documents: Incomplete List of Mistakes in the Design of CSS. It isn’t.

So compared to the logic-bending paradoxes of ::first-line, an ::nth-letter selector would be relatively straightforward. But that in itself isn’t a good enough reason for it to exist. As the CSS Working Group FAQs say:

The fact that we’ve made one mistake isn’t an argument for repeating the mistake.

A new selector needs to solve specific use cases. I would argue that all the letter-by-letter uses of variable fonts that we’re seeing demonstrate the use cases, and the number of these examples is only going to increase. The very fact that JavaScript libraries exist to solve this problem shows that there’s a need here (and we’ve seen the pattern of common JavaScript use-cases ending up in CSS before—rollovers, animation, etc.).

Now, I know that browser makers would like us to figure out how proposed CSS features should work by polyfilling a solution with Houdini. But would that work for a selector? I don’t know much about Houdini so I asked Una. She pointed me to a proposal by Greg and Tab for a full-on parser in Houdini. But that’s a loooong way off. Until then, we must petition our case to the browser gods.

This is not a new suggestion.

Anne Van Kesteren proposed ::nth-letter way back in 2003:

While I’m talking about CSS, I would also like to have ::nth-line(n), ::nth-letter(n) and ::nth-word(n), any thoughts?

Trent called for ::nth-letter in January 2011:

I think this would be the ideal solution from a web designer’s perspective. No javascript would be required, and 100% of the styling would be handled right where it should be—in the CSS.

Chris repeated the call in October of 2011:

Of all of these “new” selectors, ::nth-letter is likely the most useful.

In 2012, Bram linked to a blog post (now unavailable) from Adobe indicating that they were working on ::nth-letter for Webkit. That was the last anyone’s seen of this elusive pseudo-element.

In 2013, Chris (again) included ::nth-letter in his wishlist for CSS. So say we all.

Have you published a response to this? :

Responses

Shaw

Since building Splitting, I look forward to a day when it might become obsolete thanks to ::nth-letter selectors. Unfortunately it doesn’t look like that’ll be anytime soon, even though it was proposed back in 2003. Check out this entry by @adactio: adactio.com/journal/14408

# Posted by Shaw on Monday, October 8th, 2018 at 1:32pm

Robin Rendle

Considering we’ve been getting everything we’ve been harping on about for years when it comes to CSS, I would like nth-letter now please thank you good day I said good day adactio.com/journal/14408

Tom Morris

@Edent @adactio oh no, I’m now thinking about interleaved LTR/RTL text and ruby annotations and a bunch of other fun edge cases and I need to go have a cuppa tea and not look at a screen for a bit.

# Posted by Tom Morris on Wednesday, June 28th, 2023 at 12:21pm

Related posts

Progressive disclosure defaults

If you’re going to toggle the display of content with CSS, make sure the more complex selector does the hiding, not the showing.

Days of style and standards

CSS Day 2023 in Amsterdam.

Culture and style

Styling a document about The Culture novels of Iain M Banks.

Let’s get logical

Let me hear your blocky talk.

Talking about style

The transcript of a talk.

Related links

The quiet, pervasive devaluation of frontend - Josh Collinsworth blog

It’s like CSS exists in some bizarre quantum state; somehow both too complex to use, yet too simple to take seriously, all at once.

In many ways, CSS has greater impact than any other language on a user’s experience, which often directly influences success. Why, then, is its role so belittled?

Writing CSS seems to be regarded much like taking notes in a meeting, complete with the implicit sexism and devaluation of the note taker’s importance in the room.

Tagged with

CSS :has() Interactive Guide

This isn’t just a great explanation of :has(), it’s an excellent way of understanding selectors in general. I love how the examples are interactive!

Tagged with

Bill Oddie taught me how to make web sites - Hicks.design

I remember Jon telling me this lovely story when we first met in person. I love the idea that we had already met in a style sheet.

I also love the idea of hosting your own little internet archive—that Bill Oddie site still looks pretty great to me!

It’s a lot like an embarrassing family photo, but I’m owning it!

Tagged with

Offloading JavaScript With Custom Properties: HeydonWorks

With classes, we can send CSS static values but with custom properties we can send dynamic ones, which is a major shift in the way we can style state. This is something that has been true for some time—and is extremely well supported—but sometimes it takes solving a small real-world problem to make you appreciate the value of it.

I think we still haven’t come to fully appreciate the superpower of custom properties: dynamic values that are shared between CSS and JavaScript.

Tagged with

Designing better target sizes

This is a wonderfully in-depth interactive explainer on touch target sizes, with plenty of examples.

Tagged with

Previously on this day

18 years ago I wrote Melbourne calling

I gave a talk for the Web Standards Group in Melbourne.

22 years ago I wrote Pea throwing

Today, by happy chance, I found myself in Lewes.

23 years ago I wrote The Ig Nobel Prize Winners

The envelope please…

23 years ago I wrote The Turing Test

It’s Saturday. That means it’s Guardian reading day.

23 years ago I wrote What Apple Should Do to Increase Marketshare

Here’s an interesting little piece that could put one of those cartoon lightbulbs above Steve Jobs’ head.

23 years ago I wrote Biomorph breeder

Talking about The Blind Watchmaker reminded me of one of the things that’s so fascinating in the book.