@react-pixel-ui/react
v2.2.1
Published
Wrap any element with <Pixel> and your CSS becomes pixel art. Tailwind, inline styles, CSS modules — all supported. No Canvas, SSR compatible.
Maintainers
Readme
React Pixel UI
Turn supported CSS into pixel art. Wrap one rendered HTML element with <Pixel> — Tailwind, inline styles, and CSS modules work without Canvas and remain SSR compatible.
Install
npm install @react-pixel-ui/react
# or
pnpm add @react-pixel-ui/react
# or
yarn add @react-pixel-ui/reactRequires React 18+. @react-pixel-ui/core is installed automatically.
Quick Start
import { Pixel } from '@react-pixel-ui/react';
function App() {
return (
<Pixel size={6}>
<div style={{
background: 'linear-gradient(135deg, #ff6b6b, #4ecdc4)',
borderRadius: 16,
border: '3px solid #333',
padding: 20,
}}>
Pixel Art!
</div>
</Pixel>
);
}That's it. <Pixel> reads your CSS and converts background, border-radius, border, and box-shadow into pixel art.
APIs
<Pixel> — Wrap any element (Recommended)
import { Pixel } from '@react-pixel-ui/react';
// Tailwind
<Pixel size={6}>
<div className="bg-gradient-to-r from-red-500 to-blue-500 rounded-xl border-2 border-black">
Works with Tailwind
</div>
</Pixel>
// Inline styles
<Pixel size={6}>
<div style={{ background: '#ff6b6b', borderRadius: 12, border: '2px solid #333' }}>
Works with inline styles
</div>
</Pixel>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| size | number | 4 | Pixel block size in CSS px. Larger = blockier. |
| enabled | boolean | true | Toggle pixelation on/off |
| children | ReactElement | required | Single HTML element, or a component that forwards its ref to one |
Supported CSS properties:
background/background-color— solid colors and gradients (linear-gradient,radial-gradient,repeating-*). Alpha-preserving.border-radius— converted to staircase corners (supports per-corner[tl, tr, br, bl])border— pixel art border with staircase corners. Box size is preserved viaborder-color: transparent(no layout shift, even withbox-sizing: content-box).box-shadow— converted to hard drop-shadow (no blur)- Reactive updates: the child's
className/styleprops and theme classes on<html>/<body>(Tailwind dark mode, etc.) are automatically observed — no manual re-render needed.
usePixelRef — Ref-based hook
Attach to any element without wrapping. Best for third-party components or when you can't use a wrapper.
When a pixel shadow is present, the hook composes the generated
drop-shadow() on the element's parent so the child's staircase clip does
not cut it off. Existing parent filters are preserved, and multiple managed
children share the parent safely.
import { usePixelRef } from '@react-pixel-ui/react';
function MyComponent() {
const pixelRef = usePixelRef({ pixelSize: 6 });
return (
<div
ref={pixelRef}
style={{
background: 'linear-gradient(135deg, #fd79a8, #e84393)',
borderRadius: 20,
border: '3px solid #b8256e',
padding: 16,
}}
>
Pixelated via ref
</div>
);
}| Option | Type | Default | Description |
|--------|------|---------|-------------|
| pixelSize | number | 4 | Pixel block size |
| enabled | boolean | true | Toggle pixelation |
| observeHover | boolean | true | Re-compute on :hover |
| observeFocus | boolean | true | Re-compute on :focus |
| observeActive | boolean | true | Re-compute on :active |
PixelConfigProvider — Global defaults
Set default pixelSize for all <Pixel> and usePixelRef instances in the tree.
import { PixelConfigProvider } from '@react-pixel-ui/react';
function App() {
return (
<PixelConfigProvider config={{ pixelSize: 6 }}>
{/* All <Pixel> components default to size 6 */}
<MyPage />
</PixelConfigProvider>
);
}| Config Key | Type | Default | Description |
|------------|------|---------|-------------|
| pixelSize | number | 4 | Default pixel block size |
| borderColor | string | — | Default for PixelBox; CSS-reading APIs use the computed border color |
PixelBox — Explicit props
Use when you want direct control instead of auto-reading CSS.
import { PixelBox } from '@react-pixel-ui/react';
<PixelBox
width={280}
height={120}
pixelSize={6}
borderRadius={16}
borderWidth={3}
borderColor="#333"
background="linear-gradient(45deg, #ff6b6b, #4ecdc4)"
shadow={{ x: 4, y: 4, color: 'rgba(0,0,0,0.3)' }}
>
Content
</PixelBox>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| width | number | 200 | Element width in px |
| height | number | 100 | Element height in px |
| pixelSize | number | 4 | Pixel block size |
| borderRadius | number \| [number, number, number, number] | — | Corner radius. Array = [topLeft, topRight, bottomRight, bottomLeft] |
| borderWidth | number | — | Border thickness (auto-snapped to pixelSize grid) |
| borderColor | string | — | Any CSS color |
| background | string | — | CSS color or gradient string |
| shadow | { x: number, y: number, color: string } | — | Hard pixel shadow |
| responsive | boolean | false | Follow the size your CSS gives the box (detected via ResizeObserver) instead of width/height props. Size it with style/className (e.g. style={{ width: '100%', height: 120 }}). |
className, style, and other HTML props always land on the root
element — the wrapper <div> when a border is used.
PixelButton — Pre-styled button
import { PixelButton } from '@react-pixel-ui/react';
<PixelButton variant="primary" width={160} height={48}>Click me</PixelButton>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| variant | 'primary' \| 'secondary' \| 'danger' | 'primary' | Color theme |
| width | number | 160 | Button width |
| height | number | 48 | Button height |
| borderRadius | number | 8 | Corner radius |
| pixelSize | number | from context | Pixel block size |
| shadow | { x, y, color } | auto | Pixel shadow |
The rendered <button> defaults to type="button" (it won't submit a
surrounding form); pass type="submit" explicitly when you want that.
When to use what
| Use case | API | Why |
|----------|-----|-----|
| Existing styled elements | <Pixel> | Reads CSS automatically, zero config |
| Third-party components | usePixelRef | Attach via ref, no wrapper div |
| Full manual control | PixelBox | Explicit props, no CSS reading |
| Pre-built buttons | PixelButton | Ready-to-use with variants |
How It Works
| Feature | CSS Technique |
|---------|---------------|
| Staircase corners | clip-path: polygon() — Bresenham circle algorithm generates stepped polygon |
| Pixel gradients | Composite PNG data URL + image-rendering: pixelated — 2D grid sampling per block with full RGBA alpha |
| Pixel borders | Border color + gradient baked into single PNG with staircase shapes |
| Hard shadows | filter: drop-shadow(blur=0) — follows clip-path contour |
| Auto-detection | getComputedStyle() reads any CSS → converted to pixel art config |
Recipes
Dynamic pixel size
function PixelSlider() {
const [size, setSize] = useState(6);
return (
<>
<input type="range" min={2} max={16} value={size} onChange={e => setSize(+e.target.value)} />
<Pixel size={size}>
<div style={{ background: '#ff6b6b', borderRadius: 12, border: '2px solid #333' }}>
Size: {size}px
</div>
</Pixel>
</>
);
}Per-corner radius
<Pixel size={6}>
<div style={{
background: '#ffeaa7',
borderRadius: '24px 4px 24px 4px', // TL TR BR BL
border: '3px solid #e17055',
width: 200, height: 80,
}}>
Asymmetric corners
</div>
</Pixel>Modern color spaces (oklch / hsl)
// Gradient stops can use any supported color form.
<Pixel size={6}>
<div style={{
background: 'linear-gradient(135deg, oklch(0.75 0.2 30), oklch(0.6 0.25 280))',
borderRadius: 16,
border: '3px solid hsl(220 40% 20%)',
padding: 20,
}}>
oklch + hsl
</div>
</Pixel>Translucent gradients
// Alpha is preserved end-to-end via the RGBA composite PNG.
<Pixel size={6}>
<div style={{
background: 'linear-gradient(to right, rgba(255,107,107,0.2), rgba(78,205,196,1))',
borderRadius: 12,
}}>
Fades from translucent to opaque
</div>
</Pixel>Tailwind dark mode
// <Pixel> watches <html> / <body> class changes automatically.
// Toggle a `.dark` class on <html> and the pixel art re-renders
// with the new computed colors.
<Pixel size={6}>
<div className="bg-white dark:bg-gray-900 border-2 border-black dark:border-white rounded-xl px-4 py-3">
Auto-adapts to theme
</div>
</Pixel>Next.js (App Router)
// app/page.tsx — server component importing <Pixel> works out of the box
import { Pixel } from '@react-pixel-ui/react';
export default function Page() {
return (
<Pixel size={6}>
<div style={{ background: '#6c5ce7', borderRadius: 12, padding: 20, color: '#fff' }}>
SSR compatible
</div>
</Pixel>
);
}The published bundle starts with
"use client", so Next.js treats@react-pixel-ui/reactas a client module automatically — you don't need to add the directive yourself.<Pixel>renders its child on the server and upgrades to pixel art after hydration.
FAQ
Q: Why does my gradient look smooth instead of pixelated?
A: Check that pixelSize is large enough to see distinct blocks. At size={2}, blocks are 2x2 CSS pixels — very small on high-DPI screens. Try size={6} or higher.
Q: Why is the border missing at diagonal corners?
A: Make sure you're using <Pixel> or usePixelRef (v2.0.1+). These use composite PNG rendering where border + gradient are baked together with correct staircase shapes.
Q: Does it work with Tailwind CSS?
A: Yes. <Pixel> reads getComputedStyle which resolves Tailwind classes into final CSS values. Tailwind dark mode toggling a class on <html> is detected automatically and the pixel art re-renders.
Q: What CSS properties are supported?
A: background-color, background-image (linear/radial/repeating gradients), border-radius, border, box-shadow. Other properties (color, font, padding, etc.) are preserved as-is.
Q: Is it SSR compatible? A: Yes. The core package uses pure math (no Canvas, no DOM APIs). Elements render normally on the server and get pixelated on hydration.
Supported CSS values
- Colors: all 148 CSS named colors,
#rgb[a]/#rrggbb[aa],rgb[a]()(comma or modern slash syntax),hsl[a]()(comma or slash),oklch()/oklab(), andcolor(srgb | srgb-linear | display-p3 ...)are parsed natively.color-mix()andvar(--token)are resolved by the browser viagetComputedStyleon the<Pixel>/usePixelRefpath (Chromium serializescolor-mix()results ascolor(srgb ...), which is supported). - Gradients:
linear-gradient,radial-gradient, and theirrepeating-*variants — including Tailwind v4's interpolation hints (to right in oklab),turn/rad/gradangle units, double-position stops, and aspect-ratio-correct corner keywords (to top right). Stops may use any supported color form includingoklch(). box-shadow: the first non-inset shadow is converted into a hard pixeldrop-shadow. Additional shadows and inset shadows are ignored by design (pixel art uses a single hard shadow).- Alpha: translucent colors and gradient stops are preserved end-to-end via the composite PNG RGBA encoder (compressed with a built-in dependency-free deflate — data URLs stay in the low-KB range).
- Graceful degradation: backgrounds the engine can't pixelate
(
url()images,conic-gradient(), unresolvedvar()) are left completely untouched — the element keeps its original styling and only the staircase clip-path is applied.
Known limitations
<PixelBox>explicitbackgroundprop: unlike<Pixel>which reads computed styles,<PixelBox>takes the raw string you pass. It understands hex, named,rgb(),hsl(),oklch(), andcolor()but notcolor-mix()orvar(--token)(there's no DOM resolution step).- Dynamic children via ancestor selectors:
<Pixel>observes the child's React props (className,style) and the<html>/<body>theme classes. If an unrelated middle ancestor toggles a class that changes the child via descendant selectors, trigger a parent re-render or useusePixelRef, which listens tostylemutations on the managed element directly in addition to hover / focus / active / resize. usePixelRefshadows and parent filters: hard shadows are composed on the managed element's parent to avoid clipping. The hook preserves existing filters and coordinates sibling instances, but CSS selectors that expect the parent'sfilterto be exactlynonemay still need an isolated wrapper.
Browser Compatibility
| Feature | Chrome | Firefox | Safari | Edge |
|---------|--------|---------|--------|------|
| clip-path: polygon() | 55+ | 54+ | 10+ | 79+ |
| image-rendering: pixelated | 41+ | 56+ (crisp-edges) | 10+ | 79+ |
| filter: drop-shadow() | 18+ | 35+ | 6+ | 79+ |
TypeScript
Fully typed. All components, hooks, and config objects have TypeScript definitions.
import type {
PixelArtConfig,
PixelArtStyles,
PixelShadowConfig,
BorderRadii,
} from '@react-pixel-ui/react';Project Structure
packages/
core/ # Framework-agnostic style generators (zero browser dependency, SSR safe)
react/ # React hooks & components
apps/
demo/ # Interactive demo + documentation siteDevelopment
pnpm setup # Install + build
pnpm dev --filter=@react-pixel-ui/demo # Run demo at localhost:3000
pnpm build && pnpm type-check # Build & verifyContributing
PRs welcome. Please open an issue first to discuss larger changes.
License
React Pixel UI (한국어)
CSS 스타일을 자동으로 픽셀아트로 변환하는 React 라이브러리.
npm install @react-pixel-ui/reactimport { Pixel } from '@react-pixel-ui/react';
// 어떤 스타일이든 <Pixel>로 감싸면 픽셀 아트로 변환
<Pixel size={6}>
<div style={{
background: 'linear-gradient(135deg, #ff6b6b, #4ecdc4)',
borderRadius: 16,
border: '3px solid #333',
}}>
자동으로 픽셀화!
</div>
</Pixel>Tailwind, 인라인 스타일, CSS 모듈 모두 지원. Canvas 없음, SSR 호환.
자세한 API 문서는 영어 섹션을 참고하세요.
