style-search
style-search is a tiny CommonJS scanner for finding text inside CSS and CSS-like source while tracking whether each match sits in a string, comment, function argument, function name, or any parenthesized section. Unlike indexOf or a regular expression, it skips strings, comments, and function names by default and reports start and end indexes through a callback. It is a narrow lexical utility, not a CSS parser: it does not build an AST, validate syntax, understand declarations, or apply edits for you.
Keep it only for a narrow legacy scanning job where literal indexes and context flags are enough. New CSS tooling should usually pay the small complexity cost for a maintained parser whose output represents syntax rather than character-state guesses.
Use it if
- You maintain an older CommonJS CSS tool and need the same lightweight scanner used by parts of the Stylelint ecosystem
- You need character indexes for literal tokens while excluding comments and quoted values without constructing a PostCSS AST
- You are scanning CSS-like syntaxes such as Sass and need awareness of parenthesized sections and function arguments
- You can pin a tiny, dependency-free package whose 0.1.0 API already does exactly what the project needs
- You need syntactic meaning such as selectors, declarations, at-rules, or parsed values; the README explicitly sends those jobs to PostCSS and its selector or value parsers
- You need TypeScript declarations or an ESM export; version 0.1.0 ships a CommonJS index.js with no types field or exports map
- You require an actively released dependency; npm 0.1.0 was published in June 2016 and the GitHub repository was last pushed in August 2021
- You need reliable parsing of modern CSS escapes or malformed input; the implementation is a character scanner with simple quote, comment, and parenthesis state rather than a grammar
- You want returned results, promises, or an iterator; every match is delivered synchronously to a callback and the library returns no collection
Setup reality
Installation is only npm install style-search, and there are no runtime dependencies, peer dependencies, native modules, configuration files, or build steps. The friction is in the old API contract. Version 0.1.0 is CommonJS, so modern ESM code may need createRequire or a default-style interop import supplied by its bundler. There are no bundled TypeScript declarations, which means typed projects must add a local declaration or keep the call behind a typed wrapper. Both source and target are required in practice, but the function performs no friendly argument validation; bad inputs fail inside the scanner. Matches arrive synchronously through a callback and endIndex is the exclusive boundary, so collect them yourself if later code expects an array. Defaults matter: comments, strings, and function names are skipped, while function arguments and other parenthesized content are checked. Each syntax option accepts skip, check, or only, and the code throws if more than one option uses only. The scanner recognizes slash-star comments plus double-slash comments, and it decides that a parenthesis starts a function when the preceding character is an ASCII letter. Those deliberately simple rules can misclassify modern or unusual CSS. Pin the version and test representative source before using it in a formatter, codemod, or lint rule.
Patterns
Collect matches with the default exclusionsfind-default-matches
const styleSearch = require('style-search');
const matches = [];
styleSearch({
source: 'a { color: pink; content: \"pink\"; } /* pink */',
target: 'pink',
}, (match, count) => {
matches.push({ ...match, count });
});
console.log(matches);Only the declaration value is reported because strings and comments default to skip. The count argument starts at 1.
Stop after the first eligible matchstop-after-first
let first = null;
styleSearch({
source: 'a { color: red; border-color: red; }',
target: 'red',
once: true,
}, (match) => {
first = match;
});once stops scanning after the first match that passes all context filters; it does not return the match from styleSearch.
Search for several literal targetssearch-multiple-targets
const found = [];
styleSearch({
source: 'a { color: red; background: blue; }',
target: ['red', 'blue'],
}, (match) => {
found.push({ token: match.target, at: match.startIndex });
});Array order matters when targets overlap: at each index the scanner accepts the first target that matches.
Include matches inside commentsinclude-comments
styleSearch({
source: '/* TODO: replace px */ a { width: 10px; }',
target: 'px',
comments: 'check',
}, (match) => {
console.log(match.startIndex, match.insideComment);
});check includes both comment and non-comment matches. The scanner treats double-slash text as a comment too, even though that is not standard CSS.
Restrict a search to commentssearch-comments-only
const todos = [];
styleSearch({
source: '/* TODO: remove */ a { content: \"TODO\"; }',
target: 'TODO',
comments: 'only',
}, (match) => todos.push(match));Only one syntax option can be set to only. Combining comments: 'only' with strings: 'only' throws an error.
Include matches inside quoted stringsinclude-strings
styleSearch({
source: 'a::before { content: \"draft\"; color: draft; }',
target: 'draft',
strings: 'check',
}, (match) => {
console.log(match.startIndex, match.insideString);
});Strings default to skip. The scanner handles single and double quotes but only checks the immediately preceding backslash when deciding whether a quote is escaped.
Include function names in the searchsearch-function-names
styleSearch({
source: 'a { color: rgb(10 20 30); }',
target: 'rgb',
functionNames: 'check',
}, (match) => console.log(match));Function names default to skip. Detection uses ASCII letters immediately before an opening parenthesis, not a complete CSS identifier grammar.
Ignore matches inside function argumentsskip-function-arguments
styleSearch({
source: 'a { color: var(--brand); --brand: red; }',
target: '--brand',
functionArguments: 'skip',
}, (match) => console.log(match.startIndex));Function arguments default to check, so set skip explicitly when a var(), calc(), or similar call should be excluded.
Search only inside parenthesized sectionssearch-parentheticals-only
styleSearch({
source: '$map: (brand: red); a { color: red; }',
target: 'red',
parentheticals: 'only',
}, (match) => console.log(match.startIndex, match.insideParens));parentheticals is broader than functionArguments and is useful for Sass-like maps. Nested parentheses are tracked with one depth counter.
Replace matches safely using reported indexesreplace-from-indexes
const source = 'a { color: red; border-color: red; }';
const matches = [];
styleSearch({ source, target: 'red' }, (match) => matches.push(match));
let output = source;
for (const match of matches.reverse()) {
output = output.slice(0, match.startIndex) + 'blue' + output.slice(match.endIndex);
}
console.log(output);endIndex is exclusive. Apply edits from the end toward the start so earlier indexes remain valid.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| postcss | npm | Use it when you need a real CSS AST, source locations, transformations, and syntax-aware plugins |
| postcss-value-parser | npm | Use it when the search is confined to declaration values and functions need proper tokenization |
| postcss-selector-parser | npm | Use it when selectors, pseudo-classes, attributes, and selector rewrites are the actual problem |