Skip to main content
whitep4nth3r logo

Why is CSS ::first-letter not working?

I had some misconceptions about this sneaky pseudo element.

I recently had a use for the CSS ::first-letter pseudo element. I was dealing with some inconsistently formatted strings from the back end, and wanted to make sure that on the front end, all strings were shown with lowercase letters except for the first, which needed to be uppercase. I thought it would be trivial to use the following CSS:

.parentElement {
text-transform: lowercase;
}

.parentElement::first-letter {
text-transform: uppercase;
}

In my case, however, the first letter was not being uppercased, and I was confused about why. Here is a modified version of the HTML I was attempting to style:

<div class="parentElement">
<div class="child1">
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<path>...</path>
</svg>
</div>
<span class="child2">
inconsistently FormattED string
</span>
</div>

Naïvely, I thought I would be able to apply the styles on the div with the class parentElement, and the styles would cascade down to the span with the class child2. However, it turns out that the ::first-letter pseudo element selector can only target text under a certain set of conditions.

The ::first-letter spec

The ::first-letter CSS pseudo-element applies styles to the first letter of the first line of a block container, but only when not preceded by other content (such as images or inline tables).

When drilling down into what exactly defines a “block container”, there is definitely some ambiguity across the CSS spec: “In specifications, block boxes, block-level boxes, and block containers are all referred to as block boxes in certain places. These things are somewhat different and the term block box should only be used if there is no ambiguity.” I won’t go deep into the specifics of block boxes and block containers in this post, but I will attempt to provide a summary:

An element is a block container only if it contains block-level or inline-level boxes. The following values for the CSS display property produce block containers:

  • block

  • inline-block

  • list-item

  • table-cell

  • table-caption

  • flow-root

The following values for the CSS display property produce block-level boxes, but not block containers, given how their child elements are formatted:

  • flex

  • grid

The key issue in my case was that I was attempting to target a non-block container; the parentElement class specified a display property of flex and attempting to cascade the styles for ::first-letter down to a child element.

The rules of ::first-letter

To summarise, using CSS ::first-letter will not work in the following conditions:

  • The element you are targeting is set to display: flex or display: grid

  • The element you are targeting is preceded by other content, such as a ::before pseudo element, images or inline tables

  • You are attempting to cascade ::first-letter styles from a parent element, even if the elements are block containers

To make sure CSS ::first-letter works for you:

  • Apply the ::first-letter styles directly to a block container (and those block containers are specified above)

  • Ensure the content you are attempting to target is not preceded by other content, such as a ::before pseudo element, images or inline tables

A note on browser support

CSS ::first-letter may have limited browser support: caniuse.com reports this pseudo element selector is not supported in Firefox, but Baseline reports that "This feature is well established and works across many devices and browser versions. It’s been available across browsers since ⁨July 2015⁩."

Using ::first-letter in action

To better understand how ::first-letter works, I threw together some examples of when this pseudo element can be correctly targeted, and when it fails to work. You can view the examples at CSS first-letter demo, and you can view the code here.

Like weird newsletters?

Join 497+ subscribers in the Weird Wide Web Hole to find no answers to questions you didn't know you had.

Subscribe

I stand on stage at what the stack as if it is 1995, which is indicated by the vintage 90s shirt I am wearing. I am holding the mic. I am looking into the distance. I am gesticulating. I am powerful. I command the stage. I am one with the presentation.

Salma Alam-Naylor

I'm a software engineer and developer educator, and I make stuff on the internet. I help developers build cool stuff by writing blog posts, making videos, coding live on the internet, and publishing open source projects. Head of Developer Education at nordcraft.com.

Did this post help you?

☕️ Buy me a coffee

(thank you!)

Related posts

31 Aug 2022

What's the difference between : and :: in CSS?

I spent years Googling this question before the information stayed in my brain. Sound familiar? Then this post is for you.

CSS 3 min read →

3 May 2023

The universal CSS * selector isn't actually universal

For my ENTIRE career I have been living with an enormous misconception: the universal CSS selector doesn't actually select EVERYTHING.

CSS 1 min read →