@uindow/css - npm
npm
Search<br>Sign UpSign In
@uindow/css
1.0.0 • Public • Published 4 hours ago<br>Readme<br>Code Beta<br>0 Dependencies<br>0 Dependents<br>2 Versions<br>@uindow/css - The Smarter CSS Selector Generator
Generate one - or many - unique CSS selectors for any DOM element. Clean, human-readable output. A configurable penalty model that ranks results from most semantic to most specific. Built for performance, designed to stay out of your way.
Works everywhere JavaScript runs: Node.js, browsers, and any environment with a DOM.
Installation
npm
npm install @uindow/css
const { findOne, findAll } = require("@uindow/css");
Browser (standalone)
script src="https://uindow.github.io/css/selector.js">script><br>script><br>const { findOne, findAll } = Uindow_CSS;<br>script>
Quick start
const { findOne, findAll } = require("@uindow/css");
// The single best selector<br>const { selector, penalty } = findOne(document.querySelector(".hero"));<br>// → '[data-id="42"] .hero'
// Every viable selector, ranked from best to most specific<br>const results = findAll(document.querySelector(".hero"), { maxResults: 5 });<br>// → [<br>// { selector: '[data-id="42"] .hero', penalty: 1.3 },<br>// { selector: '.hero', penalty: 1.3 },<br>// { selector: 'section [data-id="42"] .hero', penalty: 2.6 },<br>// { selector: 'main .hero', penalty: 2.75 },<br>// { selector: 'main .hero:nth-of-type(1)', penalty: 4.4 },<br>// ]
Why @uindow/css?
Most CSS selector generators treat the problem as a lookup: walk up the DOM, find something unique, done. One selector, take it or leave it.
@uindow/css treats it as a search problem . It explores the space of possible selectors across every ancestor level, scores each candidate against a configurable penalty model, prunes aggressively to stay fast, and surfaces a ranked list of results - so you always get the shortest, most readable, most stable selector first, with fallbacks already computed.
Comparison with @medv/finder
Feature<br>@uindow/css<br>@medv/finder
Custom root element<br>✅ Any HTMLElement, Document, or ShadowRoot
✅ Supported
ID filter<br>✅ idFilter filter<br>✅ idName filter
Tag filter<br>✅ tagFilter filter<br>✅ tagName filter
Class filter<br>✅ Excludes is-*, has-*, js-*, css-* by default<br>⚠️ Less opinionated defaults
Attribute filter<br>✅ Excludes style, width, height, URLs, values ≥ 32 chars by default<br>⚠️ Less opinionated defaults
Search timeout<br>✅ timeout with graceful fallback<br>⚠️ May quit before an exhaustive search
Returns multiple selectors<br>✅ Up to maxResults, ranked by penalty<br>❌ Single selector only
Per-type penalty tuning<br>✅ idPenalty, tagPenalty, attrPenalty, attrMatchPenalty, classPenalty, nthOfTypePenalty, nthChildPenalty, lengthPenaltyThreshold
❌ Not configurable
Candidate/path caps<br>✅ maxCandidatesPerLevel, maxPathsPerLevel, maxPathsTotal
❌ Not supported
Prefix/suffix attribute matching<br>✅ [attr^="start"], [attr$="end"]
❌ Not supported
Human-readable attribute selectors<br>✅ Always emits [attr="123"]
❌ Uses CSS.escape()
Fuzziness<br>✅ Trade exclusivity for shorter selectors (0% to 100%)<br>❌ Not supported
Compound selectors<br>✅ Attempts to merge tag, classes, and attributes at each level: input.check[type="checkbox"][value="2"]
❌ Simple selectors only
Features
Multiple selectors, ranked by quality
findAll() returns up to maxResults selectors sorted by ascending penalty - from the most semantic to the most specific. Pick the best one programmatically, or hand the whole list to your users.
const { findAll } = require("@uindow/css");
const results = findAll(el, { maxResults: 10 });
results.forEach(({ selector, penalty }) => {<br>console.log(`${selector} (penalty: ${penalty})`);<br>});
Prefix and suffix attribute matching
When an exact attribute match would be brittle or unavailable, @uindow/css can generate selectors using the CSS ^= (prefix) and $= (suffix) operators. They carry their own separate penalty so they only surface when genuinely useful.
/* Exact match - always preferred */<br>[data-id="42"]
/* Prefix match - used when it uniquely identifies the element */<br>[data-id^="prod-"]
/* Suffix match */<br>[data-id$="-active"]
Tune how eagerly these appear with attrMatchPenalty (default 1.2).
Clean, human-readable output
Every selector is emitted in the plain [attr="value"] format. No CSS-escaped sequences, no surprises when you read, copy, or paste the result.
/* ✅ @uindow/css */<br>[data-id="123"] .nav-item
/* ❌ Other tools */<br>[data-id="\31 23"] .nav-item
Sensible default filters
Unstable and noisy parts of the DOM are excluded before the search begins:
Attributes - style, width, height, URL values, and anything over 32 characters are ignored by default.
Classes - is-*, has-*, js-*, and css-* are ignored by default. These are state and behaviour hooks - they change at runtime and make brittle selectors.
IDs - all IDs are allowed by default. Provide your own idFilter to exclude auto-generated or dynamic ones.
Every filter is a plain function, so your own rules are one line of code.
Fine-grained penalty model
Each...