Skip to content

Releases: Todari/react-pixel-ui

@react-pixel-ui/react@2.2.0

Choose a tag to compare

@Todari Todari released this 15 Jun 01:33
537c64a

Minor Changes

  • #6 911fbb0 Thanks @Todari! - React layer fixes: stable DOM across re-measures, resize awareness, StrictMode-safe observers, working responsive mode, and API hardening.

    <Pixel>

    • The drop-shadow wrapper no longer remounts the child DOM node on every re-measure (theme toggle, style change). Once a shadow wrapper exists it stays in the tree (display: contents while measuring), so uncontrolled <input> values, focus, and selection survive.
    • A ResizeObserver now watches the child: responsive/percentage-width elements re-measure when their size changes (stale absolute-px clip-paths are regenerated), and elements that mount at zero size (inside display: none tabs/accordions) get pixel art as soon as they become visible — previously they never did.
    • Backgrounds the engine can't pixelate (url() images, conic-gradient()) are left untouched instead of being replaced with background: none + a transparent PNG (elements no longer go invisible or flash solid red).
    • Author box-shadow is only cleared when it is actually replaced by a pixel shadow — inset-only shadows are preserved.

    usePixelRef

    • The style observer is created inside an effect instead of the ref callback. Under React 18 StrictMode (the Vite/Next dev default) the simulated remount previously disconnected the observer permanently, freezing all hover/focus/resize updates in development.
    • Borderless elements with a box-shadow get their pixelated drop-shadow again — the hook only read wrapperStyle.filter, which the composer only sets when a border wrapper is needed, so shadows were silently deleted. When the element has no clip-path the filter now applies to the element itself instead of its parent.
    • Same graceful-degradation behavior as <Pixel> for unparseable backgrounds.

    <PixelBox>

    • responsive mode actually works now: the component no longer pins its own measured fallback (200×100) as inline px width/height, which made the ResizeObserver only ever read back the forced size. Size the box with your own CSS (style={{ width: '100%', height: 120 }}) and the pixel art follows it. Measurement uses the border box.
    • className, style, and other HTML props now consistently land on the root element (previously style landed on the inner content div when a border was used, so margin etc. silently did nothing).

    <PixelButton>

    • Unknown variant values fall back to primary with a dev warning instead of crashing with a TypeError.
    • The rendered <button> defaults to type="button" so dropping it into a form no longer submits the form; an explicit type prop still wins.

    Misc

    • PixelConfigProvider no longer crashes when rendered without a config prop (it is now optional).
    • usePixelArt memoizes by the radius values, so inline per-corner arrays (borderRadius={[8,8,0,0]}) no longer regenerate the art on every parent re-render; it also skips generating the unused composite PNG.
    • PixelBox/PixelButton expose proper displayNames in React DevTools (no more PixelBox2).
    • ESM consumers under moduleResolution: node16/nodenext get the ESM declaration file via per-condition exports types, and the "use client" banner no longer shifts source maps by one line.

Patch Changes

  • Updated dependencies [911fbb0]:
    • @react-pixel-ui/core@2.2.0

@react-pixel-ui/core@2.2.0

Choose a tag to compare

@Todari Todari released this 15 Jun 01:33
537c64a

Minor Changes

  • #6 911fbb0 Thanks @Todari! - Core engine overhaul: real PNG compression, full CSS color support, gradient correctness, and graceful degradation.

    Performance

    • The PNG encoder now uses a built-in dependency-free DEFLATE (fixed Huffman + LZ77) instead of uncompressed stored blocks — generated data URLs are 30–100× smaller (e.g. a 600×400 card went from ~78 KB to ~2.4 KB), with a stored-block fallback for incompressible data.
    • generatePixelArt accepts options.want: 'styles' | 'composite' | 'both' so consumers skip generating the PNG they discard (~2× faster for gradient cases). Default 'both' keeps backward compatibility.
    • generatePixelArt results are cached in a small LRU keyed by inputs — hover/theme/re-render churn no longer regenerates identical art. Treat returned objects as immutable.

    Color parsing

    • All 148 CSS named colors are now supported (previously 14 — lime, navy, teal, tomato, gold, rebeccapurple, … no longer fail to parse).
    • New: color(srgb … ), color(srgb-linear …), and color(display-p3 …) — Chromium serializes computed color-mix() results as color(srgb …), so color-mix() now works end-to-end through <Pixel>/usePixelRef.
    • rgb()/rgba() patterns are anchored, so colors embedded in unsupported values (e.g. conic-gradient(rgb(255,0,0), …)) are no longer misparsed as a solid color.

    Gradients

    • Tailwind v4 interpolation hints are handled: linear-gradient(to right in oklab, …) no longer silently falls back to to bottom.
    • Angle units turn, rad, and grad are converted correctly (previously only deg worked).
    • Corner keywords (to top right, …) now resolve to the aspect-ratio-dependent "magic corner" angle per CSS spec via the new resolveGradientAngle(gradient, width, height) export, instead of a fixed 45° multiple.
    • Double-position stops (red 0% 50%) expand into two stops, preserving hard color edges.
    • Stops with px positions or one unparseable stop no longer corrupt the whole gradient (the -1 position sentinel can no longer leak into sampling).

    Correctness & safety

    • generatePixelShadow no longer forces zero offsets to pixelSizebox-shadow: 0 4px 0 stays a straight shadow instead of turning diagonal. Offsets snap to the nearest grid line; all-zero shadows are skipped entirely.
    • generateCompositePixelImage keeps the interior transparent for border-only elements (previously it filled the content area with the border color), and returns null when a background is present but unparseable (url(), conic-gradient(), …) so consumers can leave the original styling untouched.
    • parseComputedStyles resolves percentage border-radius (e.g. border-radius: 50% avatars) against the element size via a new optional size parameter, and handles elliptical two-value radii.
    • parseBoxShadow detects the inset keyword anywhere in the serialization — real browsers put it at the END in computed styles, so inset shadows were previously converted into outer drop-shadows. Multiple shadows are split correctly and the first non-inset one is used.
    • Every exported generator sanitizes pixelSizepixelSize: 0 no longer hangs the tab (infinite loop) or throws RangeError.

    Packaging

    • ESM consumers under moduleResolution: node16/nodenext now get the ESM declaration file (index.d.mts) via per-condition exports types — fixes the attw "Masquerading as CJS" failure and wrong CJS-interop type errors.

@react-pixel-ui/react@2.1.1

Choose a tag to compare

@github-actions github-actions released this 10 Apr 09:41
1d1b45d

Patch Changes

  • d6078ef: fix(react): shadow wrapper collapses width when child uses min(Xpx, 100%)

    When a child element had both box-shadow and a responsive width
    (e.g., width: min(260px, 100%)), the pixel art would collapse to a
    tiny, nearly-zero width. Moving the shadow slider in the demo
    Playground was the easiest reproduction.

    Root cause: <Pixel> wraps the child in an extra
    display: inline-block div carrying the filter: drop-shadow(...) so
    the shadow is not clipped by the sibling clip-path. That wrapper
    has no explicit width, making it shrink-to-fit. The child's 100%
    then resolved against a shrink-to-fit parent, which under CSS rules
    can collapse to zero, creating a circular size-resolution loop where
    both wrapper and child ended up near-zero width.

    Fix: the shadow wrapper now pins its own width / height to the
    child's measured dimensions (from artState) and adds
    max-width: 100% so it still shrinks on narrow viewports. This
    breaks the circular resolution while preserving the
    shrink-to-container-width behavior on mobile.

    Added a regression test asserting the wrapper's width, height,
    and max-width inline style.

@react-pixel-ui/react@2.1.0

Choose a tag to compare

@github-actions github-actions released this 10 Apr 08:47
1b336cc

Minor Changes

  • 5732058: Robustness, correctness, and modern CSS support pass

    @react-pixel-ui/react

    Fixes

    • Next.js App Router: the published bundle now starts with "use client", so importing <Pixel> / usePixelRef from a server component works out of the box. Previously the package would throw "You're importing a component that needs useState...".
    • Dynamic style changes are now reflected: <Pixel> re-measures when the child's className or style prop changes, so state-driven color/radius/border updates actually repaint the pixel art (instead of being frozen at the first measurement).
    • Tailwind dark mode: <Pixel> observes class / data-theme toggles on <html> and <body>, re-rendering pixel art with the new computed colors automatically.
    • No more content-box layout shift: instead of stripping the border (border: none), we now preserve the border width and set border-color: transparent + background-origin: border-box. The element's outer box stays the same regardless of box-sizing.
    • usePixelRef parent filter leak: when usePixelRef writes filter: drop-shadow(...) to the parent element (to escape clip-path), the original value is snapshotted and restored on ref detach / unmount / option change. Previously the parent stayed permanently modified.
    • usePixelRef style observer: inline style attribute mutations are now observed too. A pause() primitive on the observer prevents self-mutation loops.
    • Function-component child warning: <Pixel> logs a dev-only warning when its child doesn't accept a ref (e.g., a function component without forwardRef), instead of silently doing nothing.
    • React 19 ref access: ref-merging now reads element.props.ref first (React 19) and falls back to element.ref (React 18). peerDependencies now accepts react ^19.

    Performance

    • BMP memoization: <Pixel> wraps generatePixelArt() in useMemo, eliminating the per-frame image regeneration that happened on unrelated parent re-renders.
    • PixelConfigProvider: context value is memoized so consumers don't re-render on every provider render.

    Packaging

    • "exports" map, "sideEffects": false, and proper dual ESM/CJS entry points.
    • peerDependencies: react ^18 || ^19.

    @react-pixel-ui/core

    Fixes

    • Alpha channel preservation: the internal image encoder switched from 24-bit BMP to 32-bit RGBA PNG. rgba(), transparent gradient stops, and translucent backgrounds now render correctly instead of being flattened to opaque (or worse, solid black).
    • Correct outside-shape pixels: pixels outside the staircase clip are now fully transparent instead of black, eliminating edge bleed at non-integer pixel alignments.
    • parseColor("#xyz") fix: invalid hex strings now return null instead of {r: NaN, g: NaN, b: NaN, a: 1}.

    Features

    • hsl() / hsla() parsing: comma, space, and slash syntax; deg/rad/grad/turn hue units; negative hue wrap-around.
    • oklch() / oklab() parsing: full CSS Color 4 perceptual color space support with the standard oklab → linear sRGB → sRGB gamma-corrected pipeline. Out-of-gamut values are clamped.
    • hsl() / oklch() / oklab() gradient stops: all accepted in linear-gradient, radial-gradient, and the repeating-* variants.

    Packaging

    • "exports" map, "sideEffects": false, proper dual ESM/CJS entry points.

    Test coverage

    • New vitest test suite: 122 tests across core (98) and react (24).
    • Covers PNG encoder round-trip via Node zlib, gradient parsing, color parsing (including hsl/oklch), composer, <Pixel> / usePixelRef behavior, and the new fixes above.

    CI

    • New .github/workflows/ci.yml runs lint, type-check, test, and build on every PR and push to main, on Node 20 and 22.

Patch Changes

  • Updated dependencies [5732058]
    • @react-pixel-ui/core@2.1.0

@react-pixel-ui/core@2.1.0

Choose a tag to compare

@github-actions github-actions released this 10 Apr 08:47
1b336cc

Minor Changes

  • 5732058: Robustness, correctness, and modern CSS support pass

    @react-pixel-ui/react

    Fixes

    • Next.js App Router: the published bundle now starts with "use client", so importing <Pixel> / usePixelRef from a server component works out of the box. Previously the package would throw "You're importing a component that needs useState...".
    • Dynamic style changes are now reflected: <Pixel> re-measures when the child's className or style prop changes, so state-driven color/radius/border updates actually repaint the pixel art (instead of being frozen at the first measurement).
    • Tailwind dark mode: <Pixel> observes class / data-theme toggles on <html> and <body>, re-rendering pixel art with the new computed colors automatically.
    • No more content-box layout shift: instead of stripping the border (border: none), we now preserve the border width and set border-color: transparent + background-origin: border-box. The element's outer box stays the same regardless of box-sizing.
    • usePixelRef parent filter leak: when usePixelRef writes filter: drop-shadow(...) to the parent element (to escape clip-path), the original value is snapshotted and restored on ref detach / unmount / option change. Previously the parent stayed permanently modified.
    • usePixelRef style observer: inline style attribute mutations are now observed too. A pause() primitive on the observer prevents self-mutation loops.
    • Function-component child warning: <Pixel> logs a dev-only warning when its child doesn't accept a ref (e.g., a function component without forwardRef), instead of silently doing nothing.
    • React 19 ref access: ref-merging now reads element.props.ref first (React 19) and falls back to element.ref (React 18). peerDependencies now accepts react ^19.

    Performance

    • BMP memoization: <Pixel> wraps generatePixelArt() in useMemo, eliminating the per-frame image regeneration that happened on unrelated parent re-renders.
    • PixelConfigProvider: context value is memoized so consumers don't re-render on every provider render.

    Packaging

    • "exports" map, "sideEffects": false, and proper dual ESM/CJS entry points.
    • peerDependencies: react ^18 || ^19.

    @react-pixel-ui/core

    Fixes

    • Alpha channel preservation: the internal image encoder switched from 24-bit BMP to 32-bit RGBA PNG. rgba(), transparent gradient stops, and translucent backgrounds now render correctly instead of being flattened to opaque (or worse, solid black).
    • Correct outside-shape pixels: pixels outside the staircase clip are now fully transparent instead of black, eliminating edge bleed at non-integer pixel alignments.
    • parseColor("#xyz") fix: invalid hex strings now return null instead of {r: NaN, g: NaN, b: NaN, a: 1}.

    Features

    • hsl() / hsla() parsing: comma, space, and slash syntax; deg/rad/grad/turn hue units; negative hue wrap-around.
    • oklch() / oklab() parsing: full CSS Color 4 perceptual color space support with the standard oklab → linear sRGB → sRGB gamma-corrected pipeline. Out-of-gamut values are clamped.
    • hsl() / oklch() / oklab() gradient stops: all accepted in linear-gradient, radial-gradient, and the repeating-* variants.

    Packaging

    • "exports" map, "sideEffects": false, proper dual ESM/CJS entry points.

    Test coverage

    • New vitest test suite: 122 tests across core (98) and react (24).
    • Covers PNG encoder round-trip via Node zlib, gradient parsing, color parsing (including hsl/oklch), composer, <Pixel> / usePixelRef behavior, and the new fixes above.

    CI

    • New .github/workflows/ci.yml runs lint, type-check, test, and build on every PR and push to main, on Node 20 and 22.