funky-shadow
A React component that renders dithered, gradient-colored shadows behind any element. Under the hood it draws to a hidden canvas using Bayer matrix thresholding and Oklab color interpolation, then composites the result behind your content.
npm install funky-shadowZero runtime dependencies. React 18+ is an optional peer dep if you want the component.
Quick start
Import the FunkyShadow component and wrap the element you want to shadow. You need to tell it the element's width, height, and border radius so it can size the canvas correctly. Everything else has sensible defaults.
Presets
The package ships with 12 built-in gradient palettes. Pass a preset name as a string or use a numeric index. Each preset defines a series of RGB color stops that the renderer interpolates through. You can also define your own stops with the colors prop.
Pixel scale
The pixelScale prop controls the size of each rendered dot. At 1 you get a smooth gradient. At 3 or 4 the individual pixels become visible and you start getting that retro texture. Higher values render faster because the canvas is smaller, so cranking it up is also a free performance win.
Dithering
Dithering uses a Bayer matrix to break up color banding. The matrix size determines how fine the dithering pattern is. 4x4 is the default and works well for most sizes. Use 8x8 for larger elements where you want finer grain, or turn it off entirely if you prefer clean color bands.
Shape
The gradient can be radial, spreading outward from the center, or linear along an angle you control. Radial tends to look more like a natural light source. Linear is useful when you want the color to sweep in a specific direction across the shadow.
Custom colors
If none of the presets fit, pass an array of RGB triplets to the colors prop. The renderer interpolates between them using Oklab color space by default, which produces smoother transitions than sRGB, especially through yellows and cyans. Set oklab={false} to fall back to sRGB interpolation.
Canvas API
If you are not using React, or you want to manage the canvas yourself, import renderShadow directly. It takes a canvas element, the element dimensions, border radius, and the same config object the React component uses. It draws the shadow and returns immediately. Call it again whenever the parameters change.
Props
Everything the FunkyShadow component accepts. Only width and height are required. Everything else falls back to sensible defaults.
| Prop | Type | Default | Description |
|---|---|---|---|
| width* | number | — | Width of the element in px |
| height* | number | — | Height of the element in px |
| radius | number | 0 | Border radius |
| preset | string | number | 'galaxy' | Gradient preset name or index |
| colors | [r,g,b][] | — | Custom RGB stops (overrides preset) |
| pixelScale | number | 3 | Dot size, higher = chunkier |
| dither | '8x8'|'4x4'|'2x2'|'off' | '4x4' | Bayer matrix size |
| shape | 'line'|'radial' | 'radial' | Gradient shape |
| angle | number | 45 | Angle in degrees (line mode) |
| offsetX | number | -15 | Shadow X offset |
| offsetY | number | 20 | Shadow Y offset |
| blur | number | 30 | Shadow blur radius |
| opacity | number | 0.7 | Shadow opacity (0–1) |
| spread | number | 120 | Gradient spread (0–200) |
| oklab | boolean | true | Oklab color interpolation |
| contrast | number | 130 | Contrast (50–200) |
| brightness | number | 0 | Brightness (-50–50) |
| quantLevels | number | 4 | Quantization levels (2–7) |
| className | string | — | Wrapper class name |
| style | CSSProperties | — | Wrapper styles |
| children | ReactNode | — | Content above the shadow |










