Skip to content

Match React's unitless style properties when appending px in compat - #5280

Draft
JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/compat-unitless-styles
Draft

JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/compat-unitless-styles

Conversation

@JoviDeCroock

Copy link
Copy Markdown
Member

Note

Co-Authored with Claude

v11 moved the automatic px suffix from core into compat's IS_NON_DIMENSIONAL, and that regex misclassifies properties in both directions: fillOpacity, stopOpacity, strokeOpacity, strokeMiterlimit, aspectRatio and the Webkit-prefixed counts (WebkitFlexGrow, WebkitBoxFlex, WebkitColumnCount, …) got px appended, while containIntrinsicHeight, columnHeight, columnRuleInset* and animationRange* didn't. In each case the browser drops the declaration, where Preact 10 and React render it.

The regex is now case-insensitive behind an optional webkit prefix (so WebkitFlexGrow goes through the same branch as flexGrow), with the other branches narrowed or merged against every property Chrome exposes. Rendering all 1,546 style names (camelCase, Webkit-cased, kebab-case, --foo) with 2 and 0.5 through each build in Chrome:

dropped where React keeps dropped where Preact 10 keeps
main 20 39
#5233 15 33
this PR 1* 3*

* Lowercase webkitColumns is treated like columns/WebkitColumns (React only knows the capitalized spelling), and columns: 0.5 isn't a valid count — same as React.

Supersedes #5233 (thanks @dylanpulver), which covered the SVG opacities and aspectRatio. compat is 3 B smaller (brotli), core untouched.

IS_NON_DIMENSIONAL appended "px" to fillOpacity, stopOpacity,
strokeOpacity, strokeMiterlimit, aspectRatio and the Webkit-prefixed
flex/box/column/animation counts, and left containIntrinsicHeight,
columnHeight, columnRuleInset* and animationRange* unitless, so the
browser dropped all of those declarations where Preact 10 and React
render them.

The regex is now case-insensitive behind an optional webkit prefix, so
prefixed names share the unprefixed branches. Across every property
Chrome exposes this goes from 20 to 0 dropped declarations where React
keeps one (lowercase webkitColumns aside, now treated like columns).
compat.mjs is 3 B smaller (brotli).
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

📊 Tachometer Benchmark Results

Summary

duration

  • create10k: unsure 🔍 -0% - +1% (-1.83ms - +7.89ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -0% - +0% (-0.03ms - +0.02ms)
    preact-local vs preact-main
  • hydrate1k: unsure 🔍 -2% - +1% (-1.07ms - +0.44ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 -0% - +3% (-0.07ms - +0.43ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 -2% - +4% (-1.31ms - +2.34ms)
    preact-local vs preact-main
  • text-update: unsure 🔍 -7% - +2% (-0.13ms - +0.04ms)
    preact-local vs preact-main
  • todo: unsure 🔍 -2% - +0% (-0.72ms - +0.16ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -4% - +5% (-1.33ms - +1.66ms)
    preact-local vs preact-main

usedJSHeapSize

  • create10k: unsure 🔍 +0% - +0% (+0.00ms - +0.00ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • hydrate1k: unsure 🔍 -5% - +2% (-0.24ms - +0.08ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • text-update: unsure 🔍 -3% - +2% (-0.03ms - +0.02ms)
    preact-local vs preact-main
  • todo: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -0% - +0% (-0.01ms - +0.00ms)
    preact-local vs preact-main

Results

create10k

duration

VersionAvg timevs preact-localvs preact-main
preact-local821.75ms - 828.13ms-unsure 🔍
-0% - +1%
-1.83ms - +7.89ms
preact-main818.24ms - 825.57msunsure 🔍
-1% - +0%
-7.89ms - +1.83ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local19.08ms - 19.08ms-unsure 🔍
+0% - +0%
+0.00ms - +0.00ms
preact-main19.08ms - 19.08msunsure 🔍
-0% - -0%
-0.00ms - -0.00ms
-
filter-list

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.53ms - 16.56ms-unsure 🔍
-0% - +0%
-0.03ms - +0.02ms
preact-main16.53ms - 16.57msunsure 🔍
-0% - +0%
-0.02ms - +0.03ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.55ms - 1.55ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main1.55ms - 1.56msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
hydrate1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local55.44ms - 56.49ms-unsure 🔍
-2% - +1%
-1.07ms - +0.44ms
preact-main55.74ms - 56.82msunsure 🔍
-1% - +2%
-0.44ms - +1.07ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local5.01ms - 5.22ms-unsure 🔍
-5% - +2%
-0.24ms - +0.08ms
preact-main5.07ms - 5.32msunsure 🔍
-2% - +5%
-0.08ms - +0.24ms
-
many-updates

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.47ms - 16.92ms-unsure 🔍
-0% - +3%
-0.07ms - +0.43ms
preact-main16.42ms - 16.61msunsure 🔍
-3% - +0%
-0.43ms - +0.07ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.74ms - 3.75ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main3.74ms - 3.75msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
replace1k
  • Browser: chrome-headless
  • Sample size: 110
  • Built by: CI #6016
  • Commit: 2380c1c

duration

VersionAvg timevs preact-localvs preact-main
preact-local65.06ms - 68.15ms-unsure 🔍
-2% - +4%
-1.31ms - +2.34ms
preact-main65.12ms - 67.07msunsure 🔍
-3% - +2%
-2.34ms - +1.31ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.01ms - 3.01ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main3.01ms - 3.02msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-

run-warmup-0

VersionAvg timevs preact-localvs preact-main
preact-local16.92ms - 17.71ms-unsure 🔍
-3% - +3%
-0.53ms - +0.55ms
preact-main16.94ms - 17.68msunsure 🔍
-3% - +3%
-0.55ms - +0.53ms
-

run-warmup-1

VersionAvg timevs preact-localvs preact-main
preact-local20.88ms - 21.99ms-unsure 🔍
-3% - +4%
-0.66ms - +0.88ms
preact-main20.79ms - 21.87msunsure 🔍
-4% - +3%
-0.88ms - +0.66ms
-

run-warmup-2

VersionAvg timevs preact-localvs preact-main
preact-local20.77ms - 21.36ms-unsure 🔍
-2% - +2%
-0.51ms - +0.52ms
preact-main20.64ms - 21.48msunsure 🔍
-2% - +2%
-0.52ms - +0.51ms
-

run-warmup-3

VersionAvg timevs preact-localvs preact-main
preact-local20.99ms - 21.91ms-unsure 🔍
-5% - +3%
-1.11ms - +0.63ms
preact-main20.96ms - 22.43msunsure 🔍
-3% - +5%
-0.63ms - +1.11ms
-

run-warmup-4

VersionAvg timevs preact-localvs preact-main
preact-local13.66ms - 14.01ms-unsure 🔍
-1% - +3%
-0.14ms - +0.35ms
preact-main13.56ms - 13.90msunsure 🔍
-3% - +1%
-0.35ms - +0.14ms
-

run-final

VersionAvg timevs preact-localvs preact-main
preact-local18.48ms - 19.90ms-unsure 🔍
-3% - +6%
-0.63ms - +1.16ms
preact-main18.37ms - 19.47msunsure 🔍
-6% - +3%
-1.16ms - +0.63ms
-
text-update
  • Browser: chrome-headless
  • Sample size: 210
  • Built by: CI #6016
  • Commit: 2380c1c

duration

VersionAvg timevs preact-localvs preact-main
preact-local1.81ms - 1.93ms-unsure 🔍
-7% - +2%
-0.13ms - +0.04ms
preact-main1.85ms - 1.98msunsure 🔍
-2% - +7%
-0.04ms - +0.13ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.00ms - 1.03ms-unsure 🔍
-3% - +2%
-0.03ms - +0.02ms
preact-main1.00ms - 1.04msunsure 🔍
-2% - +3%
-0.02ms - +0.03ms
-
todo

duration

VersionAvg timevs preact-localvs preact-main
preact-local31.45ms - 31.67ms-unsure 🔍
-2% - +0%
-0.72ms - +0.16ms
preact-main31.42ms - 32.27msunsure 🔍
-0% - +2%
-0.16ms - +0.72ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.27ms - 1.27ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main1.27ms - 1.27msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
update10th1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local33.18ms - 35.37ms-unsure 🔍
-4% - +5%
-1.33ms - +1.66ms
preact-main33.10ms - 35.13msunsure 🔍
-5% - +4%
-1.66ms - +1.33ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local2.97ms - 2.98ms-unsure 🔍
-0% - +0%
-0.01ms - +0.00ms
preact-main2.97ms - 2.98msunsure 🔍
-0% - +0%
-0.00ms - +0.01ms
-

tachometer-reporter-action v2 for CI

@github-actions

Copy link
Copy Markdown

Size Change: -8 B (-0.05%)

Total Size: 15.9 kB

📦 View Changed
Filename Size Change
compat/dist/compat.mjs 3.91 kB -8 B (-0.2%)
ℹ️ View Unchanged
Filename Size
debug/dist/debug.mjs 3.88 kB
devtools/dist/devtools.mjs 273 B
dist/preact.mjs 4.88 kB
hooks/dist/hooks.mjs 1.58 kB
jsx-runtime/dist/jsxRuntime.mjs 868 B
test-utils/dist/testUtils.mjs 473 B

compressed-size-action

@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 99.853%. remained the same — JoviDeCroock/compat-unitless-styles into main

@JoviDeCroock
JoviDeCroock marked this pull request as draft September 28, 2026 11:16
@JoviDeCroock

Copy link
Copy Markdown
Member Author

To land this we'd do a major version of preact-render-to-string which removes automatic suffixing

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants