Normalise a structural path into the id segment used after the sc-
prefix. Whitelist-based: any run of characters outside [A-Za-z0-9_-]
collapses to a single hyphen, with trailing hyphens stripped.
Whitelist (not blacklist) so unexpected characters from free-text sources
— meta.description, label-derived suffixes, encoded JSON Pointers —
cannot leak into ids and break CSS selectors or aria associations.
Non-ASCII inputs (e.g. CJK property names like 名前, accented Latin
like café, emoji like 🦄) collapse under the whitelist to a short
or empty string and would silently collide on sc-. To keep ids
deterministic AND unique per input, the normaliser appends a short
hash suffix derived from the original string whenever the whitelisted
collapse:
produces an empty string, OR
dropped non-structural characters from the input (i.e. anything
besides the path joiners ., [, ] and ASCII whitespace).
Structural separator runs do NOT trigger the disambiguator so
canonical paths like user.preferences and tags[0] keep their
historic readable form (user-preferences, tags-0).
The hash is a 32-bit FNV-1a variant rendered in base-36. It is
deterministic (same input → same output), short (≤ 7 characters), and
non-cryptographic — collision resistance is good enough for DOM ids,
and a cryptographic primitive is unnecessary and not universally
available (no crypto global in every JS runtime that consumes the
library).
The leading character is guaranteed to be an ASCII letter so the full
sc-<segment> id is always a valid CSS identifier and querySelector
target. Empty-collapse inputs receive a synthetic u (for "unicode")
prefix on the hash so the id never starts with a digit.
Normalise a structural path into the id segment used after the
sc-prefix. Whitelist-based: any run of characters outside[A-Za-z0-9_-]collapses to a single hyphen, with trailing hyphens stripped.Whitelist (not blacklist) so unexpected characters from free-text sources —
meta.description, label-derived suffixes, encoded JSON Pointers — cannot leak into ids and break CSS selectors or aria associations.Non-ASCII inputs (e.g. CJK property names like
名前, accented Latin likecafé, emoji like🦄) collapse under the whitelist to a short or empty string and would silently collide onsc-. To keep ids deterministic AND unique per input, the normaliser appends a short hash suffix derived from the original string whenever the whitelisted collapse:.,[,]and ASCII whitespace).Structural separator runs do NOT trigger the disambiguator so canonical paths like
user.preferencesandtags[0]keep their historic readable form (user-preferences,tags-0).The hash is a 32-bit FNV-1a variant rendered in base-36. It is deterministic (same input → same output), short (≤ 7 characters), and non-cryptographic — collision resistance is good enough for DOM ids, and a cryptographic primitive is unnecessary and not universally available (no
cryptoglobal in every JS runtime that consumes the library).The leading character is guaranteed to be an ASCII letter so the full
sc-<segment>id is always a valid CSS identifier andquerySelectortarget. Empty-collapse inputs receive a syntheticu(for "unicode") prefix on the hash so the id never starts with a digit.