Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@uindow/css

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@uindow/css

CSS Selector Generator

latest
Source
npmnpm
Version
1.0.1
Version published
Weekly downloads
384
Maintainers
1
Weekly downloads
 
Created
Source

@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>
<script>
    const { findOne, findAll } = Uindow_CSS;
</script>

Quick start

const { findOne, findAll } = require("@uindow/css");

// The single best selector
const { selector, penalty } = findOne(document.querySelector(".hero"));
// → '[data-id="42"] .hero'

// Every viable selector, ranked from best to most specific
const results = findAll(document.querySelector(".hero"), { maxResults: 5 });
// → [
//     { selector: '[data-id="42"] .hero',         penalty: 1.3  },
//     { selector: '.hero',                        penalty: 1.3  },
//     { selector: 'section [data-id="42"] .hero', penalty: 2.6  },
//     { selector: 'main .hero',                   penalty: 2.75 },
//     { selector: 'main .hero:nth-of-type(1)',    penalty: 4.4  },
//   ]

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@uindow/css@medv/finder
Custom root element✅ Any HTMLElement, Document, or ShadowRoot✅ Supported
ID filteridFilter filteridName filter
Tag filtertagFilter filtertagName filter
Class filter✅ Excludes is-*, has-*, js-*, css-* by default⚠️ Less opinionated defaults
Attribute filter✅ Excludes style, width, height, URLs, values ≥ 32 chars by default⚠️ Less opinionated defaults
Search timeouttimeout with graceful fallback⚠️ May quit before an exhaustive search
Returns multiple selectors✅ Up to maxResults, ranked by penalty❌ Single selector only
Per-type penalty tuningidPenalty, tagPenalty, attrPenalty, attrMatchPenalty, classPenalty, nthOfTypePenalty, nthChildPenalty, lengthPenaltyThreshold❌ Not configurable
Candidate/path capsmaxCandidatesPerLevel, maxPathsPerLevel, maxPathsTotal❌ Not supported
Prefix/suffix attribute matching[attr^="start"], [attr$="end"]❌ Not supported
Human-readable attribute selectors✅ Always emits [attr="123"]❌ Uses CSS.escape()
Fuzziness✅ Trade exclusivity for shorter selectors (0% to 100%)❌ Not supported
Effort✅ Trade processing time for shorter selectors (0% to 100%)❌ Not supported
Compound selectors✅ 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 }) => {
    console.log(`${selector}  (penalty: ${penalty})`);
});

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 */
[data-id="42"]

/* Prefix match - used when it uniquely identifies the element */
[data-id^="prod-"]

/* Suffix match */
[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 */
[data-id="123"] .nav-item

/* ❌ Other tools */
[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 selector type has an independent penalty weight. Lower means it shows up more often in results; higher means it only appears when nothing better is available.

OptionDefaultControls
idPenalty1#id selectors
tagPenalty1.1div, span, …
attrPenalty1.25[name], [value="1"]
attrMatchPenalty1.2[name^="x"], [value$="5"]
classPenalty1.3.button, .nav-item, …
nthOfTypePenalty3div:nth-of-type(2)
nthChildPenalty6:nth-child(3)
lengthPenaltyThreshold32Extra penalty for selectors longer than N chars

Performance controls

For large or deeply nested DOMs, hard caps keep the search bounded:

const { findAll } = require("@uindow/css");

findAll(el, {
    maxCandidatesPerLevel: 2500, // Candidates evaluated per ancestor level
    maxPathsPerLevel:        50, // Unique paths kept per level
    maxPathsTotal:         1000, // Unique paths across the entire search
    timeout:               1500, // Return best found so far after N ms
});

Fuzziness

By default, @uindow/css only returns selectors that match the target element exclusively. Set fuzziness > 0 to allow selectors that match the target first while potentially matching other elements too - trading strict uniqueness for shorter, cleaner output.

findAll(el, { fuzziness: 0   }); // Strict - only exclusive selectors (default)
findAll(el, { fuzziness: 5   }); // Up to 5% non-exclusive matches allowed
findAll(el, { fuzziness: 20  }); // Relaxed - prioritise brevity
findAll(el, { fuzziness: 100 }); // Fuzzy - only non-exclusive (first-match) selectors

Effort

Controls how much effort is spent optimizing partial results before returning the final list. This setting trades speed for result quality: higher values spend more time exploring candidates before producing the final result set.

findAll(el, { effort: 0 });   // Low effort - quickly returns the first `maxResults`
findAll(el, { effort: 50 });  // Medium effort - evaluates 25 × `maxResults` candidates before returning the final results
findAll(el, { effort: 100 }); // Maximum effort - evaluates 50 × `maxResults` candidates before returning the final results

Compound selectors at every level

Most CSS selector generators pick a single token per ancestor level - a class, or an attribute, or a tag - and move on. This produces selectors that are either fragile (too generic) or bloated (too many levels deep).

@uindow/css builds compound selectors at each level by default, combining the tag, relevant classes, and attributes into a single, precise token:

/* ✅ @uindow/css - compound, precise, readable */
input.check[type="checkbox"][value="2"]

/* ❌ Other tools - simple, one token at a time */
input
.check
[type="checkbox"]

The result is selectors that are shorter, more stable, and immediately understandable at a glance.

API

findOne(element, options?)

Returns the single best selector for element, or throws if none can be generated.

const { findOne } = require("@uindow/css");

const { selector, penalty } = findOne(document.getElementById("main"));
// → { selector: '#main', penalty: 1 }

findAll(element, options?)

Returns up to maxResults selectors sorted by ascending penalty, or throws if none can be generated.

const { findAll } = require("@uindow/css");

const results = findAll(document.getElementById("main"), {
    maxResults: 10,
});

Configuration reference

All configuration items are optional. Any omitted option falls back to its default.

const { findAll } = require("@uindow/css");

findAll(el, {
    // Scope - nothing above this element appears in any selector
    root: document.querySelector("#app"),  // Default: document.documentElement

    // Filters - return false to exclude a candidate from the search
    idFilter:    (name)        => !name.startsWith("auto-"),
    tagFilter:   (name)        => name !== "div",
    classFilter: (name)        => !name.startsWith("is-"),
    attrFilter:  (name, value) => name.startsWith("data-") && value.length < 32,

    // Penalties - lower value → type appears more often in results
    idPenalty:              1,
    tagPenalty:             1.1,
    attrPenalty:            1.25,
    attrMatchPenalty:       1.2,
    classPenalty:           1.3,
    nthOfTypePenalty:       3,
    nthChildPenalty:        6,
    lengthPenaltyThreshold: 32,

    // Performance caps
    maxCandidatesPerLevel:  2500,
    maxPathsPerLevel:         50,
    maxPathsTotal:          1000,
    maxResults:               25,
    timeout:                1500,

    // Percentage of shorter, first-match selectors
    fuzziness: 0,

    // Effort spent on optimizing candidates
    effort: 50
});

License

MIT © 2026 Uindow™

Keywords

css

FAQs

Package last updated on 30 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts