icss-replace-symbols
A tiny, single-purpose helper for the linking stage of Interoperable CSS. Give it a PostCSS syntax tree and a map of token names to replacement strings, and it rewrites matching tokens inside declaration values and media-query parameters. It deliberately does not touch selectors or property names. The package is mostly encountered as an old transitive dependency in CSS Modules toolchains; for new code, its maintained successor is the replaceSymbols export from icss-utils.
Keep it when an old CSS Modules toolchain already depends on its exact behavior. Do not choose it for new work; icss-utils is the direct successor and avoids adopting a package last released in 2017.
Use it if
- You maintain an older PostCSS or CSS Modules plugin that already imports this exact package
- You need to replace ICSS tokens in declaration values and @media parameters while leaving selectors and property names alone
- You receive a PostCSS tree from another tool and want a synchronous mutation with no runtime dependencies
- You need behavior compatible with the historical CSS Modules linking implementation
- You are starting new code: the package's GitHub URL now redirects to icss-utils, which exposes the same replaceSymbols operation alongside the rest of the current ICSS helpers
- You expect an ordinary PostCSS plugin export: the default function accepts a PostCSS root and a translations object directly, so you must wrap it when adding it to a processor
- You need selectors, property names, non-media at-rules, or arbitrary text rewritten: the published function walks declaration values and @media parameters only
- You need TypeScript declarations or an ESM-native package: version 1.1.0 ships four files, CommonJS output, and no types field
- You want an actively released direct dependency: npm 1.1.0 was published in May 2017, and the old repository was folded into icss-utils
Setup reality
Installation is only npm install icss-replace-symbols, and the package has no runtime dependencies, native build, credentials, configuration file, or peer dependency declaration. That simplicity hides the main integration work: it does not parse CSS for you. Your code must already have a PostCSS Root with walkDecls and walkAtRules methods, then call the function during the linking phase. The README uses Babel-style default-import syntax. The published file is CommonJS compiled by Babel, so require('icss-replace-symbols') returns an object and CommonJS callers need .default. The function mutates the supplied tree in place and returns undefined. Replacements happen only in declaration values and parameters of @media rules; selectors, declaration property names, and other at-rules are untouched. Token matching is lexical rather than CSS-value-aware: the regular expression recognizes word characters plus $, #, hyphen, and dot, which permits keys such as --brand, #f00, and 0.5em but also means dotted tokens are considered whole symbols. Replacement values are not recursively reprocessed in the same match. There are no bundled TypeScript types, no documented error model, and no modern PostCSS peer range to tell package managers whether your version is supported. In a new plugin, install icss-utils and import replaceSymbols from there instead; this package mainly makes sense when preserving an existing dependency graph.
Patterns
Replace a token in declaration valuesreplace-declaration-token
import postcss from 'postcss';
import replaceSymbols from 'icss-replace-symbols';
const root = postcss.parse('.button { color: brandColor }');
replaceSymbols(root, { brandColor: '#2563eb' });
console.log(root.toString());The root is mutated in place; replaceSymbols does not return the transformed CSS.
Replace several tokens in one valuereplace-multiple-values
const root = postcss.parse('.card { box-shadow: x y blur spread shadowColor }');
replaceSymbols(root, {
x: '0',
y: '2px',
blur: '8px',
spread: '0',
shadowColor: 'rgb(0 0 0 / 20%)',
});
console.log(root.toString());Every matching token in the declaration value is handled in the same synchronous tree walk.
Expand a token in a media queryreplace-media-token
const root = postcss.parse('@media small { .grid { display: grid } }');
replaceSymbols(root, { small: '(max-width: 599px)' });
console.log(root.toString());Only @media parameters are visited. Tokens in @supports, @container, and other at-rules are not replaced.
Rename a custom property referencerewrite-custom-property-reference
const root = postcss.parse('.alert { color: var(--danger) }');
replaceSymbols(root, { '--danger': '--color-error' });
console.log(root.toString());This changes the reference inside the value, not the custom-property declaration name itself.
Wrap it for a PostCSS processorwrap-postcss-plugin
const translations = { gapToken: '1rem' };
const icssLinker = {
postcssPlugin: 'local-icss-linker',
Once(root) {
replaceSymbols(root, translations);
},
};
const result = await postcss([icssLinker]).process(
'.stack { gap: gapToken }',
{ from: undefined },
);The package export is a tree helper, not a PostCSS 8 plugin object, so it needs this adapter when used in a processor list.
Load the default export from CommonJSuse-commonjs
const postcss = require('postcss');
const replaceSymbols = require('icss-replace-symbols').default;
const root = postcss.parse('.badge { background: accent }');
replaceSymbols(root, { accent: 'tomato' });The published Babel-generated CommonJS module does not assign the function directly to module.exports; .default is required.
Limit replacements to valuespreserve-selectors-and-properties
const root = postcss.parse('.brand { brand: brand }');
replaceSymbols(root, { brand: 'renamed' });
console.log(root.toString());
// .brand { brand: renamed }The class selector and property name remain unchanged because the implementation walks declaration values, not the whole CSS text.
Replace hash and dimension tokensreplace-literal-css-tokens
const root = postcss.parse('.box { border: 1px solid #f00; margin: 0.5em }');
replaceSymbols(root, { '#f00': 'rebeccapurple', '0.5em': '8px' });
console.log(root.toString());The matcher accepts #, $, hyphen, dot, and word characters, so keys are not limited to JavaScript-style identifiers.
Account for dotted tokensavoid-partial-dotted-token
const root = postcss.parse('.item { color: palette.red; background: red }');
replaceSymbols(root, { red: 'green' });
console.log(root.toString());
// .item { color: palette.red; background: green }Dots are part of a matched symbol. A mapping for red does not replace the red portion of palette.red.
Use one-pass replacement semanticsavoid-recursive-replacement
const root = postcss.parse('.item { color: primary; background: secondary }');
replaceSymbols(root, { primary: 'secondary', secondary: 'black' });
console.log(root.toString());
// .item { color: secondary; background: black }Inserted replacement text is not processed again at the same position, so mapping order does not create a replacement chain.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| icss-utils | npm | New ICSS tooling that needs the current replaceSymbols export plus import and export extraction helpers |
| postcss-modules-values | npm | You want a complete PostCSS plugin for defining and importing values between CSS Modules files |
| postcss-modules | npm | You need the whole CSS Modules transformation pipeline rather than a low-level token mutator |
| postcss-custom-properties | npm | Your values are standard CSS custom properties and should be transformed through a maintained PostCSS plugin |