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.
- Keep real DOM and CSS — text, layout, events, and accessibility stay on the original element.
- Use existing styles — Tailwind, CSS modules, inline styles, gradients, borders, and shadows are read from computed CSS.
- No Canvas dependency — pixel art is generated with
clip-path, RGBA PNG data URLs, anddrop-shadow(). - SSR compatible — elements render normally on the server and are enhanced after hydration.
- Reactive by default — style, class, theme, hover, focus, and active changes can be observed automatically.
- Framework-independent core — use
@react-pixel-ui/corewhen you only need the CSS-to-pixel-art engine.
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.
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.
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.
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 |
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 |
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.
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.
| 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 |
| 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 |
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>
</>
);
}<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>// 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>// 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>// <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>// 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.
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.
- 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.
<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.
| 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+ |
Fully typed. All components, hooks, and config objects have TypeScript definitions.
import type {
PixelArtConfig,
PixelArtStyles,
PixelShadowConfig,
BorderRadii,
} from "@react-pixel-ui/react";packages/
core/ # Framework-agnostic style generators (zero browser dependency, SSR safe)
react/ # React hooks & components
apps/
demo/ # Interactive demo + documentation site
pnpm setup # Install + build
pnpm dev --filter=@react-pixel-ui/demo # Run demo at localhost:3000
pnpm build && pnpm type-check # Build & verifyPRs welcome. Please open an issue first to discuss larger changes.
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 문서는 영어 섹션을 참고하세요.