Skip to main content

On This Page

Implementing the Nonexistent ::nth-letter CSS Selector with JavaScript Shims

2 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Let’s Use the Nonexistent ::nth-letter Selector Now

Author Lee Meyer released an experimental npm library to shim the ::nth-letter selector after over two decades of developer requests. The package achieved 1,300 downloads in its first week by automating the translation of invalid CSS into valid :nth-child selectors.

Why This Matters

The technical reality of CSS lacks a native mechanism to style individual characters without polluting the DOM with manual markup. While ideal models like the CSS Parser API remain in draft stages or unmaterialized, developers must rely on shims that rewrite CSS and manipulate the DOM to bridge the gap. This approach highlights the tension between clean, presentational pseudo-elements and the current necessity of ‘gross’ markup for advanced typography.

Key Insights

  • The ::nth-letter selector has been a recurring developer request since 2003 to mimic print effects like drop caps.
  • Adobe attempted an experimental draft spec for ::nth-letter in WebKit back in 2012, but it was never standardized.
  • Philip Walton at Google concluded that perfect CSS polyfills are impossible because browsers discard invalid CSS before JavaScript can access it.
  • The ::nth-letter shim uses the get-css-data library to fetch raw CSS before the browser’s parser can invalidate non-standard syntax.
  • Modern effects like text vortexes leverage the sibling-index() function, which is currently limited to Chrome and Safari environments.

Working Examples

Hypothetical ::nth-letter syntax used for alternating character styles.

h1.fancy::nth-letter(even) {
  transform: skewY(15deg);
  background: #C97A7A;
}
h1.fancy::nth-letter(odd) {
  transform: skewY(-15deg);
  background: #8B3F3F;
}

Regex logic used to translate invalid ::nth-letter syntax into valid :nth-child selectors.

let rewrittenCss = cssText.replace(
  /([^,{\r\n]+?)::?nth-letter[ \t]*\(([^\n)]*)\)/gi,
  (full, selector, args) => {
    selector = selector.trim();
    return `${selector} .char:nth-child(${args})`;
  }
);

Practical Applications

  • Use case: Migrating direction-aware elastic hover effects to CSS by targeting nth-characters without manual span wrapping. Pitfall: Breaking accessibility if the underlying character-splitting library does not correctly implement ARIA roles.
  • Use case: Implementing complex typography like a text vortex using the shim and sibling-index(). Pitfall: Failure to process external stylesheets if Cross-Origin Resource Sharing (CORS) policies are not configured to allow JavaScript fetching.

References:

Continue reading

Next article

Meta AI Sapiens2: Scaling Human-Centric Vision Models to 5B Parameters and 4K Resolution

Related Content