Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

prevOpen Source→string-extract-class-namesnext

string-extract-class-names8.4.0

Extracts class and ID names from isolated CSS selector fragments

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • Installation
  • Quick Take
  • Examples
  • API — EXTRACT()
  • API — EXTRACTCSSSE…
  • API — READCSSSELEC…
  • API — DECODECSSSEL…
  • API — READCSSTOKEN…
  • API — VERSION
  • API — TYPES
  • THE USE
  • BRACKET NOTA…
  • Changelog

No 3rd party dependencies. All dependencies and devDependencies, checked recursively, are Codsen packages.

Permalink to InstallationInstallation

Permalink to Quick TakeQuick Take

Permalink to ExamplesExamples

  • Extract selectors from HTML-style attribute selectors
  • Non-parsing algorithm can tackle really dodgy CSS
  • Decode CSS selector escapes
  • Extract raw escaped selectors
  • Isolate a selector prelude before extraction
  • Read lexical tokens from an isolated CSS region, retaining original offsets.
  • Read one selector token at an exact index
  • Inspect the source ranges of extracted selectors
  • Read semantic attribute selectors
  • Processes whole CSS selectors

API — extract()

The main function extract() is imported like this:

It’s a function which takes one input argument:

Input argumentTypeObligatoryDescription
str
Type: String
Obligatory: yes
strStringyesAn isolated selector prelude or already-tokenized selector fragment

The function will return a plain object (a Result type above):

Key in a returned objectTypeDescription
res
Type: Array of strings
resArray of stringsRaw class and ID selector spellings
ranges
Type: Array of ranges or null (compatible with Ranges)
rangesArray of ranges or null (compatible with Ranges)Locations of extracted strings

Accepted input

extract() is a context-free scanner. Pass an isolated CSS selector prelude or an already-tokenized selector fragment, not a complete stylesheet or HTML document.

The function does not recognise declarations, comments, strings outside attribute values, URLs, or HTML character references as outer syntax. If those constructs are included in the input, their dots and hashes can be returned as apparent selectors. For example, a colour such as #fff, a URL fragment such as #icon, or a quoted string such as ".example" can all look like selector tokens to a context-free scan.

Let a CSS parser isolate the selector prelude before calling extract(). The slice below is only a short illustration for a single flat rule:

import { extract } from "string-extract-class-names";

const rule =
  '.real { color: #fff; background: url("/icon.svg#mark"); content: ".not-a-selector"; }';
const selectorPrelude = rule.slice(0, rule.indexOf("{"));

console.log(extract(selectorPrelude).res);
// => [".real"]

HTML character-reference decoding belongs to the HTML parsing layer. Decode references there before constructing selector input; extract() does not turn a literal spelling such as © into a character. CSS escapes are a separate CSS feature: extract() preserves their raw spelling. Use decodeCssSelector() for identifier spellings, or extractCssSelectorTokens() for canonical values that also account for quoted CSS strings and string continuations.

API — extractCssSelectorTokens()

Use this function to compare class and ID names while keeping the original spelling and source ranges:

function extractCssSelectorTokens(str: string): CssSelectorToken[];

It accepts the same selector fragments as extract() and returns an empty array when it finds no names. Each entry contains a canonical value, the original raw source slice, and its UTF-16 range with an exclusive end. The value starts with . for a class or # for an ID. For an attribute selector, this prefix is synthetic and is absent from raw and the range.

import { extractCssSelectorTokens } from "string-extract-class-names";

const tokens = extractCssSelectorTokens(String.raw`[cl\61 ss="a,b"]`);
console.log(tokens);
// => [{ value: ".a,b", raw: "a,b", range: [11, 14] }]

Attribute names are CSS identifiers: [cl\61 ss=foo] has the same meaning as [class=foo]. Quoted values are CSS strings, so numeric values, punctuation, and backslash-newline continuations are accepted. For example, [class="123"] identifies class 123, and a continuation between a and b identifies class ab while retaining the continuation in raw.

Exact class values produce individual HTML class tokens. A [class~=value] value containing HTML ASCII whitespace produces no tokens. Exact ID values remain complete strings: [id=" x "] produces value: "# x ". For compatibility, extract() retains its older trimming behavior for this ID form and returns "#x".

These entries are an inventory, not a selector evaluator. In particular, [class="a b"] still compares the complete attribute value in CSS; extracting .a and .b does not establish that the selector matches an element. The helper does not HTML-decode character references or parse an entire stylesheet. Invalid non-string input throws a TypeError.

API — readCssSelectorToken()

The function readCssSelectorToken() is imported like this:

Reads a single class or ID selector starting at an exact index. Use it when you’re already walking a string and know where a . or # sits — extract() scans a whole string, while this function reads one token:

Input argumentTypeObligatoryDescription
str
Type: String
Obligatory: yes
strStringyesA string to read from.
start
Type: Natural number or zero
Obligatory: yes
startNatural number or zeroyesIndex of the leading . or #.

It returns a plain object (a CssSelectorToken type), or null if start doesn’t point at a . or #:

KeyTypeDescription
value
Type: String
valueStringThe selector with any CSS escapes decoded.
raw
Type: String
rawStringThe selector exactly as it appears in the source.
range
Type: Array of two numbers
rangeArray of two numbersWhere the token starts and ends in str.
import { readCssSelectorToken } from "string-extract-class-names";

console.log(readCssSelectorToken(".a-b .c", 0));
// => { value: ".a-b", raw: ".a-b", range: [0, 4] }

console.log(readCssSelectorToken(".a-b .c", 2));
// => null

API — decodeCssSelector()

The function decodeCssSelector() is imported like this:

Decodes the CSS escapes in one already-extracted class or ID selector, keeping its leading dot or hash:

Utility-first CSS frameworks lean on escapes heavily — .md\:w-1\/2 is one class name, not three tokens. This function turns the source form into the class name a browser sees:

import { decodeCssSelector } from "string-extract-class-names";

console.log(decodeCssSelector(".a\\:b"));
// => ".a:b"

console.log(decodeCssSelector(".plain"));
// => ".plain"

It’s what readCssSelectorToken() uses to fill the value key.

API — readCssToken()

Reads one lexical token at an exact UTF-16 index in an isolated CSS region. Advance to range[1] to read the next token. An index outside the string returns null.

readCssToken(str: string, start: number): CssToken | null;
import { readCssToken } from "string-extract-class-names";

readCssToken(String.raw`@m\65 dia all`, 0);
// => {
//      kind: "at-keyword",
//      value: "media",
//      raw: String.raw`@m\65 dia`,
//      range: [0, 9],
//    }

Each token has kind, value, raw, and range keys. raw is the original source slice, and range contains its start and exclusive end indexes. The reader recognizes identifiers, at-keywords, functions, strings, bad strings, unquoted URLs, bad URLs, comments, whitespace, and delimiters.

Identifier, at-keyword, function, string, and URL values are CSS-decoded. At-keyword values omit @; function values omit (; string and URL values omit their surrounding syntax. Whitespace values keep their raw spelling. Comment values contain the raw comment body. Bad strings expose their decoded prefix, and bad URLs have an empty value. Quoted URL arguments produce separate function, whitespace, and string tokens.

This is a bounded lexical reader, not a complete CSS tokenizer or parser. Numbers, hashes, and other grammar-specific tokens use individual delimiters and identifiers. Its token sequence alone cannot determine whether removing a separator preserves CSS meaning.

Isolate CSS using HTML boundaries before calling this helper. For an inline style attribute, decode HTML character references first and retain a mapping to the original HTML if you need source ranges. Style-element text is already CSS and must not be HTML-decoded.

API — version

You can import version:

API — types

This package is written in TypeScript and exports the following types:

TypeDescription
CssToken
Type: CssToken
CssTokenA lexical token returned by readCssToken() — kind, value, raw, and range.
Result
Type: Result
ResultWhat extract() returns — res and ranges.
CssSelectorToken
Type: CssSelectorToken
CssSelectorTokenA token returned by readCssSelectorToken() or extractCssSelectorTokens() — value, raw, and range.
import type { CssSelectorToken, CssToken, Result } from "string-extract-class-names";

The use

The email-comb library uses string-extract-class-names after it has isolated selector heads from a stylesheet. It then compares those extracted class and ID names with the email’s HTML while detecting and deleting unused CSS styles.

Bracket notation

Normally, class and ID selectors in CSS include dots or hashes, for example, div.first-class.second-class. The ranges include those dots and hashes:

import { extract } from "string-extract-class-names";
const res = extract("div.first-class.second-class");
console.log(res);
// => {
//      res: [".first-class", ".second-class"],
//      ranges: [
//        [3, 15],
//        [15, 28],
//      ],
//    }

Dots above are at indexes 3 and 15.

The function also supports the exact [class=value], whitespace-list [class~=value], and exact [id=value] attribute-selector forms. Attribute names are ASCII-case-insensitive after CSS escape decoding. Values can use CSS identifier or string syntax, and an exact class string can contain multiple HTML class tokens. Partial-match operators such as ^=, $=, and *= do not produce selectors.

For example, instead of td.croodles, CSS can use td[class="croodles"]. This notation was historically useful in email templates because of Yahoo rendering behaviouropens in a new tab:

import { extract } from "string-extract-class-names";
const str = `td[id=" abc-def "]`;
const res = extract(str);
console.log(res);
// => {
//      res: ["#abc-def"],
//      ranges: [[8, 15]],
//    }

// However:

console.log(str.slice(8, 15));
// => "abc-def"
// There is no hash in the source slice because the attribute value did not
// contain one. The hash in `res` is synthetic.

For dot and hash selectors that appear literally in the source, res matches the corresponding String.prototype.slice() calls:

assert.deepEqual(
  res,
  ranges.map(([from, to]) => str.slice(from, to)),
);

Permalink to changelogChangelog

Open Changelog
↑ back to top
prev next

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab