From bcdcf79ebc7261a1e6caa5f5bdd44df87eaf79db Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 18:39:01 +0000 Subject: [PATCH 1/2] Fix 0px being converted to unitless 0 or 0rem Zero pixel values (0px) are semantically equivalent to 0rem and 0 in CSS, so converting them provides no benefit. More importantly, converting 0px to a unitless 0 breaks CSS custom properties such as Tailwind's --tw-ring-offset-width when they are used in contexts that require units. https://claude.ai/code/session_01RNkSDiLSYfS94qrFXguRF2 --- index.js | 39 +++++++++++++++++++++++---------------- spec/pxtorem-spec.js | 9 +++++++++ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 538c1db..33612ff 100644 --- a/index.js +++ b/index.js @@ -6,12 +6,18 @@ const defaults = { rootValue: 16, unitPrecision: 5, selectorBlackList: [], - propList: ["font", "font-size", "line-height", "letter-spacing", "word-spacing"], + propList: [ + "font", + "font-size", + "line-height", + "letter-spacing", + "word-spacing" + ], replace: true, mediaQuery: false, minPixelValue: 0, exclude: null, - unit: "px", + unit: "px" }; const legacyOptions = { @@ -20,7 +26,7 @@ const legacyOptions = { selector_black_list: "selectorBlackList", prop_white_list: "propList", media_query: "mediaQuery", - propWhiteList: "propList", + propWhiteList: "propList" }; function convertLegacyOptions(options) { @@ -36,7 +42,7 @@ function convertLegacyOptions(options) { delete options["prop_white_list"]; delete options.propWhiteList; } - Object.keys(legacyOptions).forEach((key) => { + Object.keys(legacyOptions).forEach(key => { if (Reflect.has(options, key)) { options[legacyOptions[key]] = options[key]; delete options[key]; @@ -48,6 +54,7 @@ function createPxReplace(rootValue, unitPrecision, minPixelValue) { return (m, $1) => { if (!$1) return m; const pixels = parseFloat($1); + if (pixels === 0) return m; if (pixels < minPixelValue) return m; const fixedVal = toFixed(pixels / rootValue, unitPrecision); return fixedVal + "rem"; @@ -61,12 +68,12 @@ function toFixed(number, precision) { } function declarationExists(decls, prop, value) { - return decls.some((decl) => decl.prop === prop && decl.value === value); + return decls.some(decl => decl.prop === prop && decl.value === value); } function blacklistedSelector(blacklist, selector) { if (typeof selector !== "string") return; - return blacklist.some((regex) => { + return blacklist.some(regex => { if (typeof regex === "string") { return selector.indexOf(regex) !== -1; } @@ -85,31 +92,31 @@ function createPropListMatcher(propList) { notExact: filterPropList.notExact(propList), notContain: filterPropList.notContain(propList), notStartWith: filterPropList.notStartWith(propList), - notEndWith: filterPropList.notEndWith(propList), + notEndWith: filterPropList.notEndWith(propList) }; - return (prop) => { + return prop => { if (matchAll) return true; return ( (hasWild || lists.exact.indexOf(prop) > -1 || - lists.contain.some(function (m) { + lists.contain.some(function(m) { return prop.indexOf(m) > -1; }) || - lists.startWith.some(function (m) { + lists.startWith.some(function(m) { return prop.indexOf(m) === 0; }) || - lists.endWith.some(function (m) { + lists.endWith.some(function(m) { return prop.indexOf(m) === prop.length - m.length; })) && !( lists.notExact.indexOf(prop) > -1 || - lists.notContain.some(function (m) { + lists.notContain.some(function(m) { return prop.indexOf(m) > -1; }) || - lists.notStartWith.some(function (m) { + lists.notStartWith.some(function(m) { return prop.indexOf(m) === 0; }) || - lists.notEndWith.some(function (m) { + lists.notEndWith.some(function(m) { return prop.indexOf(m) === prop.length - m.length; }) ) @@ -146,7 +153,7 @@ module.exports = (options = {}) => { pxReplace = createPxReplace( rootValue, opts.unitPrecision, - opts.minPixelValue, + opts.minPixelValue ); }, Declaration(decl) { @@ -177,7 +184,7 @@ module.exports = (options = {}) => { if (atRule.params.indexOf(opts.unit) === -1) return; atRule.params = atRule.params.replace(pxRegex(opts.unit), pxReplace); } - }, + } }; }; module.exports.postcss = true; diff --git a/spec/pxtorem-spec.js b/spec/pxtorem-spec.js index 9e0f0b7..7b36259 100644 --- a/spec/pxtorem-spec.js +++ b/spec/pxtorem-spec.js @@ -84,6 +84,15 @@ describe("pxtorem", function() { expect(processed).toBe(expected); }); + + it("should not convert 0px to unitless 0 for CSS custom properties", function() { + var input = ":root { --tw-ring-offset-width: 0px; }"; + var expected = ":root { --tw-ring-offset-width: 0px; }"; + var options = { propList: ["*"] }; + var processed = postcss(pxtorem(options)).process(input).css; + + expect(processed).toBe(expected); + }); }); describe("value parsing", function() { From 87123f39c7e138592ba2ac1d898f54245bf03d0a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 19:36:47 +0000 Subject: [PATCH 2/2] Use --* propList pattern in 0px custom property regression test More precise than * since the test is specifically about CSS custom properties being processed and zero pixel values being preserved. https://claude.ai/code/session_01RNkSDiLSYfS94qrFXguRF2 --- spec/pxtorem-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/pxtorem-spec.js b/spec/pxtorem-spec.js index 7b36259..3926dc9 100644 --- a/spec/pxtorem-spec.js +++ b/spec/pxtorem-spec.js @@ -88,7 +88,7 @@ describe("pxtorem", function() { it("should not convert 0px to unitless 0 for CSS custom properties", function() { var input = ":root { --tw-ring-offset-width: 0px; }"; var expected = ":root { --tw-ring-offset-width: 0px; }"; - var options = { propList: ["*"] }; + var options = { propList: ["--*"] }; var processed = postcss(pxtorem(options)).process(input).css; expect(processed).toBe(expected);