PXLKIT DOCUMENTATION

Three steps to your first rendered component. Then dig into icons, animation, and the rest of the kit.

Install · Wrap with provider · Use components

Getting Started

Step 1 — Install. One command pulls the components for your framework — @pxlkit/core for React or, new in 2.2, @pxlkit/vue for Vue 3 and @pxlkit/angular for Angular — and whichever icon packs you actually need. The packs are plain data, the same in every framework: pure SVG, no font loading, tree-shakeable — every pack is its own npm module under the @pxlkit scope. The code below follows the framework you pick, here and across the site.

Install the components and the icon packs
npm install @pxlkit/core @pxlkit/gamification @pxlkit/feedback @pxlkit/social @pxlkit/weather @pxlkit/ui @pxlkit/effects @pxlkit/parallax

Step 2 — Add the UI kit (optional). Install @pxlkit/ui-kit for React — or, new in 2.2, @pxlkit/ui-kit-vue or @pxlkit/ui-kit-angular — set up Tailwind CSS v4 and import the kit's stylesheet (see UI Kit Components below). Then, if you want, set the default surface once at the root — it is "pixel", the retro 8-bit aesthetic, unless you pick "linear", a flat modern one — with <PxlKitSurfaceProvider> in React and Vue, or providePxlKitSurface() in Angular. Every UI Kit component reads it, and any component can override it locally with its own surface prop.

Set the surface once at the root
// app/providers.tsx — Next.js App Router: the kit renders in Client Components
'use client';

import { PxlKitSurfaceProvider } from '@pxlkit/ui-kit';

export function Providers({ children }: { children: React.ReactNode }) {
  return <PxlKitSurfaceProvider surface="pixel">{children}</PxlKitSurfaceProvider>;
}

// app/layout.tsx — stays a Server Component;
// app/globals.css: @import "@pxlkit/ui-kit/styles.css"; (in place of @import "tailwindcss")
import './globals.css';
import { Providers } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Step 3 — Use components. Import any icon and any UI Kit primitive and render. Icons accept palette (default), tinted, and solid appearance modes — same prop, three looks.

Render an icon
import { PxlKitIcon } from '@pxlkit/core';
import { Trophy } from '@pxlkit/gamification';
import { CheckCircle } from '@pxlkit/feedback';
import { Heart } from '@pxlkit/social';

function App() {
  return (
    <div>
      {/* Palette colors (default) — uses the icon's original palette */}
      <PxlKitIcon icon={Trophy} size={32} />

      {/* Solid mode — every pixel flattened to one color */}
      <PxlKitIcon icon={CheckCircle} size={32} appearance="solid" color="#00FF88" />

      {/* Tinted mode — palette + colour overlay, preserves luminance */}
      <PxlKitIcon icon={Heart} size={48} appearance="tinted" color="#FF0000" />
    </div>
  );
}
trophygamification
check-circlefeedback
heartsocial
sunweather
trophysolid
hearttinted

Available Packs

Pxlkit ships with 7 icon packs (6 standard 16×16 packs plus the 3D parallax pack documented below). Each standard pack can contain both static and animated icons. Install only the ones you need — they're fully tree-shakeable.

trophy

@pxlkit/gamification+ANIMATED

Icons for game mechanics, achievements, rewards, and RPG elements

51 icons

check-circle

@pxlkit/feedback+ANIMATED

Icons for notifications, alerts, status messages, and toasts

33 icons

heart

@pxlkit/social+ANIMATED

Icons for social media, community, and user interaction

43 icons

sun

@pxlkit/weather+ANIMATED

Icons for weather conditions, seasons, and natural phenomena

36 icons

pencil

@pxlkit/ui+ANIMATED

Icons for interface controls, editor tools, and common actions

41 icons

explosion-burst

@pxlkit/effects+ANIMATED

Animated visual effects — explosions, signals, particles, glows, and VFX

12 icons

Install a single pack
npm install @pxlkit/core @pxlkit/social

Icon Format

Icons are defined using a simple, human-readable format: a grid of characters where each character maps to a color via a palette. The . character is always transparent.

This format is designed to be easy to read, write by hand, and generate with AI. Each row is a string of exactly N characters (where N matches the grid size).

PxlKitData Type
type GridSize = 8 | 16 | 24 | 32 | 48 | 64;

interface PxlKitData {
  name: string;        // kebab-case identifier
  size: GridSize;      // grid dimensions (NxN)
  category: string;    // pack/category name
  grid: string[];      // N rows of N characters each
  palette: Record<string, string>;  // char → hex color (#RGB / #RRGGBB / #RRGGBBAA)
  tags: string[];      // searchable tags
  author?: string;     // attribution
}
Example Icon Definition
export const Trophy: PxlKitData = {
  name: 'trophy',
  size: 16,
  category: 'gamification',
  grid: [
    '................',
    '..GGGGGGGGGGGG..',
    '.GG.YYYYYYYY.GG.',
    '.G..YYYYYYYY..G.',
    '.G..YYYWYYYY..G.',
    '.GG.YYYYYYYY.GG.',
    '..GGGGGGGGGGGG..',
    '....GGGGGGGG....',
    '.....GGGGGG.....',
    '......GGGG......',
    '......GGGG......',
    '.....DDDDDD.....',
    '....DDDDDDDD....',
    '....BBBBBBBB....',
    '...BBBBBBBBBB...',
    '................',
  ],
  palette: {
    'G': '#FFD700',  // Gold
    'Y': '#FFF44F',  // Yellow
    'D': '#B8860B',  // Dark gold
    'B': '#8B4513',  // Brown
    'W': '#FFFFFF',  // White
  },
  tags: ['trophy', 'achievement'],
};

Key insight: This same format can be output by an AI model. The grid is essentially ASCII art with a legend. You can ask ChatGPT, Claude, or any LLM to generate it.

Opacity / Alpha

Each palette color supports per-pixel opacity via the #RRGGBBAA format. The last two hex digits represent the alpha channel (00 = fully transparent, FF = fully opaque).

Palette with Opacity
palette: {
  'A': '#FF000080',  // Red at 50% opacity
  'B': '#00FF00CC',  // Green at 80% opacity
  'C': '#0000FF',    // Blue at 100% (default)
  'D': '#FFD70040',  // Gold at 25% opacity
}

Supported hex formats:

  • #RGB — shorthand (expanded to #RRGGBB, fully opaque)
  • #RRGGBB — standard 6-digit hex (fully opaque)
  • #RRGGBBAA — 8-digit hex with alpha channel

The <PxlKitIcon /> component, SVG utilities, and Builder UI all handle opacity automatically. When generating SVG, pixels with opacity < 1 get a fill-opacity attribute.

Utility Functions
import { parseHexColor, encodeHexColor } from '@pxlkit/core';

// Parse: extract color and opacity from hex string
const { color, opacity } = parseHexColor('#FF000080');
// color = '#FF0000', opacity = 0.502

// Encode: combine color and opacity back to hex
const hex = encodeHexColor('#FF0000', 0.5);
// hex = '#FF000080'

// If opacity is 1 (or omitted), returns 6-digit hex
const solid = encodeHexColor('#FF0000');
// solid = '#FF0000'

Icon Component

The <PxlKitIcon /> component — <pxl-icon> in Angular — renders pixel art as an <img> element whose src is an inline SVG document encoded as a data URI (MIME image/svg+xml). The source format stays SVG end-to-end — vector, hi-DPI sharp, exportable — but the wrapping <img> lets us use image-rendering: pixelated, the only browser-honoured directive that guarantees true nearest-neighbour scaling for pixel art at any size from 8 px to 512 px.

IconAppearance Type
type IconAppearance =
  | 'palette'   // (default) original artwork colours
  | 'tinted'    // palette + colour overlay (preserves luminance & detail)
  | 'solid';    // every pixel flattened to a single colour
PxlKitProps
interface PxlKitProps {
  icon: PxlKitData;            // Icon data (required)
  size?: number;               // Visual size in px (default: 32)
  appearance?: IconAppearance; // Colour mode (default: 'palette')
  color?: string;              // Tint hue / flat colour for 'tinted' & 'solid'
  className?: string;          // CSS class names (applied to the <img>)
  'aria-label'?: string;       // Accessible name (the <img alt>); unset or empty → the icon's name
  decorative?: boolean;        // Beside text that says the same: alt="", skipped by screen readers
  style?: React.CSSProperties; // Inline styles (merged onto the <img>)

  /** @deprecated since v1.3 — use `appearance="palette" | "solid"` instead. */
  colorful?: boolean;
  /** @deprecated since v1.3 — use `appearance="solid"` instead. */
  solid?: boolean;
  /** @deprecated since v1.3 — use `appearance="tinted" color="..."` instead. */
  tint?: string;
}

Vue's PxlKitIcon (@pxlkit/vue) and Angular's <pxl-icon> (@pxlkit/angular) take the same props as inputs — icon, size, appearance, color, the accessible name (aria-label, ariaLabel in Angular) and decorative — and render the same image; class and style apply to their root. The deprecated props are React only.

Colour modes in action
// Default — original artwork palette
<PxlKitIcon icon={Trophy} size={32} />

// Tinted — keep the palette luminance, shift the hue
<PxlKitIcon icon={Trophy} size={32} appearance="tinted" color="#00FF88" />

// Solid — every pixel becomes one colour
<PxlKitIcon icon={Trophy} size={32} appearance="solid" color="#FF0000" />

Screen readers announce an icon by its aria-label, else by the icon's name; an empty label counts as none. When visible text beside the icon already says what it means — a button or link label, a heading, a list item — mark it decorative: it renders alt="", so it is skipped instead of read before the label, and its name stays out of the page's text. An icon that is a button's only content is its label: give it an aria-label instead.

Decorative icons
import { PxlKitIcon } from '@pxlkit/core';
import { Download, Trash } from '@pxlkit/ui';

// The visible label names the button: the icon beside it is decorative.
<button>
  <PxlKitIcon icon={Download} size={16} decorative /> Download
</button>

// The icon is the button's only content: it needs a label.
<button>
  <PxlKitIcon icon={Trash} size={16} aria-label="Delete" />
</button>

MIGRATION FROM v1.2.x

  • <PxlKitIcon icon={X} colorful /> → omit the prop (palette is the default)
  • <PxlKitIcon icon={X} /> (legacy mono) → <PxlKitIcon icon={X} appearance="solid" />
  • <PxlKitIcon icon={X} color="#FF0000" /> → <PxlKitIcon icon={X} appearance="solid" color="#FF0000" />
  • <PxlKitIcon icon={X} tint="#FF0000" /> → <PxlKitIcon icon={X} appearance="tinted" color="#FF0000" />

The deprecated colorful / solid / tint aliases silently resolve to the new appearance axis at runtime so existing code keeps working. TypeScript marks them as @deprecated; plan to migrate before v2.

Animated Icons

Animated icons extend the pixel art format with frame-based playback. Each frame shares a base palette and can optionally override individual colors. Playback behavior is controlled by the trigger prop.

AnimatedPxlKitData Type
interface AnimatedPxlKitData {
  name: string;
  size: GridSize;                      // 8 | 16 | 24 | 32 | 48 | 64
  category: string;
  palette: Record<string, string>;    // shared base palette across all frames
  frames: AnimationFrame[];            // array of frames (in display order)
  frameDuration: number;               // ms per frame (e.g. 150)
  loop: boolean;                       // @deprecated — use trigger instead
  trigger?: AnimationTrigger;          // controls playback behavior
  tags: string[];
  author?: string;
}

interface AnimationFrame {
  grid: string[];                      // same grid format as PxlKitData
  palette?: Record<string, string>;    // optional per-frame palette overrides
}

type AnimationTrigger = 'loop' | 'once' | 'hover' | 'appear' | 'ping-pong';
AnimatedPxlKitIcon Props
interface AnimatedPxlKitProps {
  icon: AnimatedPxlKitData;
  size?: number;               // size in px (default: 32)
  appearance?: IconAppearance; // colour mode (default: 'palette')
  color?: string;              // tint / solid colour
  trigger?: AnimationTrigger;  // override icon.trigger
  speed?: number;              // multiplier: 2 = 2× faster, 0.5 = half (0.1–10)
  fps?: number;                // fixed FPS — takes priority over speed (1–60)
  playing?: boolean;           // manual play/pause override
  className?: string;
  style?: React.CSSProperties;
  'aria-label'?: string;       // accessible name of the frames (default: the icon's name)
  decorative?: boolean;        // beside text that says the same: every frame alt=""

  /** @deprecated since v1.3 — use `appearance` instead. */
  colorful?: boolean;
  /** @deprecated since v1.3 — use `appearance="solid"` instead. */
  solid?: boolean;
  /** @deprecated since v1.3 — use `appearance="tinted" color="..."` instead. */
  tint?: string;
}

Vue's AnimatedPxlKitIcon and Angular's <pxl-animated-icon> take the same props as inputs and play the same frames at the same times; the deprecated props are React only.

Basic Usage
import { AnimatedPxlKitIcon } from '@pxlkit/core';
import { FireSword } from '@pxlkit/gamification';

// Loop forever in palette mode (default)
<AnimatedPxlKitIcon icon={FireSword} size={48} />

// Solid colour loop — flatten every pixel to one colour
<AnimatedPxlKitIcon icon={FireSword} size={32} appearance="solid" color="#00FF88" />

// Tinted loop — keep luminance, shift hue
<AnimatedPxlKitIcon icon={FireSword} size={32} appearance="tinted" color="#FF00AA" />
Trigger Modes
// 'loop'      — plays continuously (default)
<AnimatedPxlKitIcon icon={FireSword} trigger="loop" />

// 'once'      — plays one time, stops on last frame
<AnimatedPxlKitIcon icon={FireSword} trigger="once" />

// 'hover'     — plays only while the user hovers
<AnimatedPxlKitIcon icon={FireSword} trigger="hover" />

// 'appear'    — plays once when it enters the viewport
<AnimatedPxlKitIcon icon={FireSword} trigger="appear" />

// 'ping-pong' — loops forward then backward alternating
<AnimatedPxlKitIcon icon={FireSword} trigger="ping-pong" />
Speed & FPS Control
// 2× speed — double frame rate
<AnimatedPxlKitIcon icon={FireSword} speed={2} />

// Half speed
<AnimatedPxlKitIcon icon={FireSword} speed={0.5} />

// Fixed FPS — takes priority over speed and icon.frameDuration
<AnimatedPxlKitIcon icon={FireSword} fps={6} />

// Paused manually
<AnimatedPxlKitIcon icon={FireSword} playing={false} />
Export as Animated SVG
import { generateAnimatedSvg } from '@pxlkit/core';
import { FireSword } from '@pxlkit/gamification';

// Colorful animated SVG with CSS keyframe animation (default)
const svg = generateAnimatedSvg(FireSword);

// Monochrome animated SVG
// (note: generateAnimatedSvg is a utility — its options use the legacy
//  colorful/monoColor shape; the React component uses the new appearance API)
const monoSvg = generateAnimatedSvg(FireSword, {
  colorful: false,
  monoColor: '#00FF88',
});
fire-sword

loop

fire-sword

hover

fire-sword

ping-pong

LIVE PREVIEW

FireSword — 6 frames · 120ms

hover center icon to trigger it

Parallax 3D Icons

The @pxlkit/parallax pack introduces multi-layer 3D parallax pixel icons — interactive depth-based icons where each layer floats at a different Z-depth. Mouse movement across the viewport rotates the entire scene, creating a true 3D effect. Click interactions trigger particle bursts, layer explosions, and color shifts.

Install
npm install @pxlkit/core @pxlkit/parallax
Basic Usage
import { ParallaxPxlKitIcon } from '@pxlkit/core';
import { GhostFriend, CoolEmoji } from '@pxlkit/parallax';

// Basic 3D parallax icon (palette is the default — every layer keeps its colours)
<ParallaxPxlKitIcon icon={GhostFriend} size={64} />

// Large interactive icon with custom strength + a tint applied to every layer
<ParallaxPxlKitIcon
  icon={CoolEmoji}
  size={128}
  strength={20}
  interactive
  appearance="tinted"
  color="#FFD700"
/>

How it works: Each parallax icon is composed of multiple animated layers stacked at different Z-depths using CSS perspective + preserve-3d + per-layer translateZ. The component tracks mouse position across the entire viewport and applies rotateX/rotateY transforms to the scene with smooth lerp interpolation.

ParallaxPxlKitData Type
interface ParallaxPxlKitData {
  name: string;              // kebab-case identifier
  size: GridSize;            // grid dimensions (e.g. 16)
  category: string;          // always 'parallax'
  layers: ParallaxLayer[];   // ordered back→front (first = deepest)
  tags: string[];            // searchable tags
  author?: string;
}

interface ParallaxLayer {
  icon: PxlKitData | AnimatedPxlKitData;  // static or animated
  depth: number;  // authoring metadata: 0 = anchor, positive = behind, negative = in front;
                  // the renderers stack layers in array order, deepest first
  // depth: 3.0  → far background (shadow, trail)
  // depth: 0    → center baseline (body)
  // depth: -2.0 → near foreground (face details, blush)
}
ParallaxPxlKitIcon Props
interface ParallaxPxlKitProps {
  icon: ParallaxPxlKitData;    // The parallax icon data (required)
  size?: number;               // Container size in px (default: 64)
  strength?: number;           // Mouse reaction intensity (default: 18)
  appearance?: IconAppearance; // Colour mode applied to every layer (default: 'palette')
  color?: string;              // Tint / solid colour applied to every layer
  smoothing?: number;          // Lerp factor 0–1 (default: 0.06)
  perspective?: number;        // CSS perspective in px (default: max(200, size×2.5))
  layerGap?: number;           // Z spacing between layers (default: max(12, size×0.2))
  shadow?: boolean;            // Depth shadow between layers (default: true)
  interactive?: boolean;       // Click effects enabled (default: true)
  onActivate?: (active: boolean) => void;  // Click toggle callback
  className?: string;
  style?: React.CSSProperties;
  'aria-label'?: string;       // Name of the role="img" container (default: the icon's name)
  decorative?: boolean;        // Container aria-hidden, every layer alt="" (default: false)

  /** @deprecated since v1.3 — use `appearance` instead. */
  colorful?: boolean;
  /** @deprecated since v1.3 — use `appearance="solid"` instead. */
  solid?: boolean;
  /** @deprecated since v1.3 — use `appearance="tinted" color="..."` instead. */
  tint?: string;
}

Vue's ParallaxPxlKitIcon and Angular's <pxl-parallax-icon> take the same props as inputs; onActivate is the activate event in Vue (@activate) and the activate output in Angular ((activate)). The deprecated props are React only.

Key features:

  • True 3D Depth — CSS perspective + preserve-3d + per-layer translateZ for real depth
  • Page-Wide Tracking — Mouse rotation works across the entire viewport, not just the icon
  • Click Interactions — Particle bursts, layer explosions, random rotation jolt, color hue-shift on click
  • Animated Layers — Each layer is a static or animated icon; animated layers play their frames (e.g., blinking eyes, flickering shadows)
  • Peel-Apart Intro — Layers spread out in a dramatic animation on mount
  • Depth Shadows — Soft CSS shadows between layers for visual depth
Example: GhostFriend Layers
// GhostFriend has 5 animated layers at different depths:
const GhostFriend: ParallaxPxlKitData = {
  name: 'ghost-friend',
  size: 16,
  category: 'parallax',
  layers: [
    { icon: GhostShadow, depth: 3.0 },   // far background shadow
    { icon: GhostTrail,  depth: 1.5 },   // wispy trail behind
    { icon: GhostBody,   depth: 0 },     // main body (center)
    { icon: GhostFace,   depth: -1.0 },  // eyes + mouth (closer)
    { icon: GhostBlush,  depth: -2.0 },  // blush cheeks (nearest)
  ],
  tags: ['ghost', 'cute', 'friendly', '3d', 'parallax'],
};
Available Icons (10)
import { ParallaxPack } from '@pxlkit/parallax';
import {
  CoolEmoji,     // Sunglasses emoji with sparkle layers
  PixelHeart,    // Beating heart with glow + particles
  RetroTV,       // CRT monitor with scan lines
  PixelRocket,   // Rocket ship with exhaust + stars
  GhostFriend,   // Cute ghost with blush + trail
  NeonSkull,     // Glowing skull with neon effects
  MagicOrb,      // Floating orb with energy rings
  PixelCrown,    // Crown with sparkles
  RetroJoystick, // Gamepad with button highlights
  CyberEye,      // Cyberpunk eye with scan effects
} from '@pxlkit/parallax';
ghost-shadow
ghost-trail
ghost-body
ghost-face
ghost-blush

GhostFriend

cool-emoji-glow
cool-emoji-chain
cool-emoji-face
cool-emoji-glasses
cool-emoji-glare

CoolEmoji

LIVE 3D PREVIEW

10 parallax icons

move mouse to rotate · click to interact

UI KIT PARALLAX COMPONENTS

The UI Kit also includes scroll-based and cursor-tracking parallax wrapper components:PixelParallaxLayer (scroll-based), PixelParallaxGroup (clipped container), and PixelMouseParallax (cursor-tracking). See the UI Kit Parallax docs for details.

Toast Notifications

Toast notifications come in two layers:

  • The UI kit's toasts — its <PxlKitToastProvider> with useToast() (React, Vue) or injectToast() (Angular), which stacks toasts, handles their timers, announces them to screen readers and exposes success / error / info / warning / loading / promise shortcuts. Use this in app code: try it in the /ui-kit playground, or read its reference.
  • The icon components' PixelToast — @pxlkit/core, @pxlkit/vue and @pxlkit/angular ship a single toast pinned to a corner of the screen, whose visibility you control: for a page that needs one notification without the UI kit. Its props and code are below.
Quick usage — inside the toast provider
import { PixelButton, PxlKitToastProvider, useToast } from '@pxlkit/ui-kit';

function SaveButton() {
  const { toast } = useToast();
  return (
    <PixelButton tone="green" onClick={() => toast.success('Saved', 'Your changes have been saved')}>
      Show Toast
    </PixelButton>
  );
}

export function App() {
  return (
    <PxlKitToastProvider>
      <SaveButton />
    </PxlKitToastProvider>
  );
}

Shortcuts: success, error, warning, info and loading, plus promise for work in flight. Each tone has its colours; pass an icon (any Pxlkit icon, animated ones included) in the toast's options to show one.

TRY THE UI KIT'S TOASTS

Push toasts live — every shortcut, position, duration, an animated icon and a promise:

Open the toast playground →

THE ICON COMPONENTS' PIXELTOAST

One toast at a time, pinned to a corner: you show it with visible, and it asks to be closed when its duration runs out or the reader presses its close button — onClose in React, the close event in Vue (@close), the closed output in Angular ((closed)).

PixelToast props
interface PixelToastProps {
  /** Whether the toast is shown. */
  visible: boolean;
  title: string;
  message?: string;
  /** A Pxlkit icon, shown on the left. */
  icon?: PxlKitData;
  /** The icon in its palette colours (default), or flat in accentColor. */
  colorfulIcon?: boolean;      // default true
  iconSize?: number;           // default 24
  bgColor?: string;            // default '#12121a'
  borderColor?: string;        // default '#2a2a3e'
  textColor?: string;          // default '#e8e6e3'
  /** Colour of the title, status dot and close button. */
  accentColor?: string;        // default '#00ff88'
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; // default 'top-right'
  /** Auto-close delay in ms; 0 disables it. */
  duration?: number;           // default 2200
  showClose?: boolean;         // default true
  onClose?: () => void;        // Vue: @close · Angular: (closed)
  className?: string;          // Vue and Angular: class
}
A single toast
import { useState } from 'react';
import { PixelToast } from '@pxlkit/core';
import { CheckCircle } from '@pxlkit/feedback';

export function SaveButton() {
  const [saved, setSaved] = useState(false);
  return (
    <>
      <button onClick={() => setSaved(true)}>Save</button>
      <PixelToast
        visible={saved}
        title="Saved"
        message="Your changes were saved."
        icon={CheckCircle}
        onClose={() => setSaved(false)}
      />
    </>
  );
}

This PixelToast is styled with Tailwind utilities that ship inside @pxlkit/core — the UI kit's stylesheet does not cover them. With Tailwind CSS v4, add @source "../node_modules/@pxlkit/core/dist"; to your stylesheet (adjust the relative path to where your stylesheet sits).

SVG Generation

You can generate standalone SVG files from icon data using the utility functions:

Generate SVG
import { gridToSvg } from '@pxlkit/core';
import { Trophy } from '@pxlkit/gamification';

// Colorful SVG string
const colorSvg = gridToSvg(Trophy, { mode: 'colorful' });

// Monochrome SVG
const monoSvg = gridToSvg(Trophy, {
  mode: 'monochrome',
  monoColor: '#333333',
});

// With XML declaration (for standalone files)
const fileSvg = gridToSvg(Trophy, {
  mode: 'colorful',
  xmlDeclaration: true,
});
Pixel ↔ grid ↔ SVG conversion
import {
  gridToPixels,     // PxlKitData            → Array<{ x, y, color, opacity? }>
  pixelsToGrid,     // pixels + size + palette → { grid, palette }
  pixelsToSvg,      // pixels + size + SvgOptions → SVG string
  svgToDataUri,     // SVG string → "data:image/svg+xml,..."
  svgToBase64,      // SVG string → "data:image/svg+xml;base64,..."
} from '@pxlkit/core';
Validation & parsing
import {
  validateIconData,  // (icon) => string[]   — array of error messages (empty = valid)
  isValidIconData,   // (icon) => boolean    — shorthand
  parseIconCode,     // (code: string) => PxlKitData | null    — strict
  parseAnyIconCode,  // (code: string) => AnyIcon | null       — handles Pxl / Animated / Parallax
  generateIconCode,  // (icon) => string     — PxlKitData → TS source
  isAnimatedIcon,    // (icon) => icon is AnimatedPxlKitData   — type guard
  isParallaxIcon,    // (icon) => icon is ParallaxPxlKitData   — type guard
} from '@pxlkit/core';
Animated SVG export
import {
  generateAnimatedSvg,    // (icon, { colorful?, monoColor?, pixelSize?, xmlDeclaration? }) => string
  animatedToFrameIcons,   // (icon) => Array<{ icon: PxlKitData; duration: number }>
} from '@pxlkit/core';

// Drive your own raf/canvas loop from animatedToFrameIcons():
const frames = animatedToFrameIcons(FireSword);
frames.forEach(({ icon, duration }) => {
  // icon is a static PxlKitData snapshot of the frame
  // duration is the ms to display it before advancing
});
Colour utilities
import {
  parseHexColor,            // ('#RRGGBBAA') => { color: string; opacity: number }
  encodeHexColor,           // (hex, opacity?) => string                — inverse of parseHexColor
  adjustBrightness,         // (hex, amount: -1..1) => string           — lighten/darken
  hexToRgb,                 // ('#RRGGBB') => { r, g, b }
  rgbToHex,                 // (r, g, b) => '#RRGGBB'
  getPerceivedBrightness,   // ('#RRGGBB') => number (0..255 luma)      — useful for picking dark/light text
  RETRO_PALETTES,           // Record<name, string[]>                   — the curated retro palettes used across the site
} from '@pxlkit/core';

// Pick a readable text colour automatically:
const luma = getPerceivedBrightness(bgHex);
const textColor = luma > 140 ? '#000' : '#fff';

AI Generation

The grid format was specifically designed to work well with AI code generation. Here's a prompt template you can use with any LLM:

AI Prompt Template
Generate a 16x16 pixel art icon in this JSON format:

{
  "name": "icon-name-here",
  "size": 16,
  "category": "your-category",
  "grid": [
    "................",
    // 16 rows, each with exactly 16 characters
    // "." = transparent pixel
    // Letters map to colors via palette
  ],
  "palette": {
    "A": "#HEX001",
    "B": "#HEX002"
  },
  "tags": ["tag1", "tag2"]
}

Rules:
- Grid: exactly 16 rows of 16 chars
- "." is always transparent
- Use 3-6 colors max for clean pixel art
- Each non-"." character must have a palette entry

Create a [YOUR DESCRIPTION] icon.

After the AI generates the code, you can paste it into the Builder to preview it, or use parseIconCode() programmatically:

Parse AI Output
import { parseIconCode, validateIconData } from '@pxlkit/core';

const aiOutput = `{ "name": "fire", ... }`;
const icon = parseIconCode(aiOutput);

if (icon) {
  const errors = validateIconData(icon);
  if (errors.length === 0) {
    // Valid icon! Use it or save it
    console.log('Icon parsed successfully:', icon.name);
  }
}

UI Kit Components

Pxlkit's UI kit is a React library, @pxlkit/ui-kit: 111 production-ready components — buttons, inputs, cards, selects, modals, toasts, tables, badges, avatars, skeletons, layout primitives and animation wrappers. New in 2.2, the same components ship for Vue 3 (@pxlkit/ui-kit-vue) and Angular (@pxlkit/ui-kit-angular, standalone, signal-based components that work with ngModel and reactive forms), with the same markup, theme and behaviour on one framework-neutral core (@pxlkit/ui-kit-core). Live demos and the walkthrough are on /ui-kit.

Install the UI kit
npm install @pxlkit/ui-kit @pxlkit/core @pxlkit/gamification
Tailwind CSS v4 in your build
# Next.js — create-next-app --tailwind sets this up. Otherwise:
npm install -D tailwindcss @tailwindcss/postcss postcss

// postcss.config.mjs
export default { plugins: { '@tailwindcss/postcss': {} } };

# Vite
npm install -D tailwindcss @tailwindcss/vite

// vite.config.ts
import tailwindcss from '@tailwindcss/vite';
// …
plugins: [react(), tailwindcss()],

The kit's stylesheet is a Tailwind CSS v4 entry point, so Tailwind has to process it. Without that step the build still succeeds, but the CSS keeps @theme, @source and @apply as written and the components render unstyled.

CSS setup — in place of @import "tailwindcss"
/* app/globals.css (Next.js) or src/index.css (Vite) */
@import "@pxlkit/ui-kit/styles.css";

/* The kit styles the components, not the page: theme <body> yourself */
@layer base {
  body {
    background-color: var(--color-retro-bg);
    color: var(--color-retro-text);
    font-family: var(--font-sans);
  }
}

The kit's stylesheet imports Tailwind CSS itself, so do not import tailwindcss as well. It styles the components, not the page: give <body> the theme yourself, and remove a starter template's own theme rules — create-next-app's globals.css sets a white body background, font-family: Arial and an @theme inline that remaps --font-sans and --font-mono, all of which override the kit's.

Use the components
import { PixelButton } from '@pxlkit/ui-kit';
import { PxlKitIcon } from '@pxlkit/core';
import { Trophy } from '@pxlkit/gamification';

<PixelButton tone="green" iconLeft={<PxlKitIcon icon={Trophy} size={16} decorative />}>
  Create Quest
</PixelButton>

Next.js (App Router). The components use state, effects and context, so they render in Client Components. Keep app/layout.tsx a Server Component and put the providers in a 'use client' file of your own (Step 2 above has both files); start the files that use the components' hooks or event handlers with 'use client'. In Server Components, import helpers such as buildGoogleFontsUrl, toLocaleUpper or cn from @pxlkit/ui-kit-core. Of the icon components, PxlKitIcon renders in a Server Component; AnimatedPxlKitIcon, ParallaxPxlKitIcon and PixelToast need a Client Component.

Server rendering. The three kits render on the server and hydrate — Next.js, Nuxt, @angular/ssr. A calendar shows the current month and marks today, which depends on the reader's clock: give PixelCalendarGrid a month (or a value) for a stable server render, or render a current-month calendar in the browser only — next/dynamic with ssr: false in Next.js, <ClientOnly> in Nuxt, @defer in Angular.

Dark mode. The theme follows a class: .dark on <html> (or any ancestor) selects the dark palette, .light the light one. useDarkMode() (React, Vue) and injectDarkMode() (Angular) switch it and remember the reader's choice. To re-skin, redefine any --retro-* variable on :root (light) and .dark, after the kit's import.

The UI Kit page includes live previews, props tables, and copy-ready code examples for each element. Use the sidebar to browse everything by category, including PixelToast and animation utilities.

Open Full UI Kit Documentation →

Component Reference

Per-component reference for every component of the kit — description, accessibility contract, keyboard map and the examples in React, Vue and Angular, from each component's manifest, and its API in each framework (props, events, slots and bindings), read from the kits' sources. Click an entry to expand it, open its own page, or deep-link with /docs#pixel-button. For live demos, head to the UI Kit showcase.

ACTIONS (4)

page →
page →
page →
page →

FORMS (24)

page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →

DATA DISPLAY (19)

page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →

CARDS (8)

page →
page →
page →
page →
page →
page →
page →
page →

HERO (2)

page →
page →

FEEDBACK (7)

page →
page →
page →
page →
page →
page →
page →

NAVIGATION (8)

page →
page →
page →
page →
page →
page →
page →
page →

OVERLAYS (7)

page →
page →
page →
page →
page →
page →
page →

OVERLAY FOUNDATION (4)

page →
page →
page →
page →

LAYOUT (14)

page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →

ANIMATIONS (11)

page →
page →
page →
page →
page →
page →
page →
page →
page →
page →
page →

PARALLAX (3)

page →
page →
page →

Contributing

Pxlkit is community-driven, with MIT code packages and source-available asset packs. Here's how you can contribute:

  • Add icons to existing packs — Create new icons using the Builder, copy the TypeScript code, and submit a PR
  • Create new icon packs — See the section below for the pack structure
  • Improve the Builder — Add new tools, improve UX, fix bugs
  • Improve core utilities — Optimize SVG generation, add new export formats
  • Documentation — Fix typos, add examples, improve guides

NAMING CONVENTIONS

  • Icon names: kebab-case (e.g., fire-sword)
  • Export names: PascalCase (e.g., FireSword)
  • Pack names: kebab-case (e.g., fantasy-rpg)
  • Tags: lowercase, comma-separated
  • Colors: uppercase hex with # (e.g., #FF0000)
  • Maximum 6 colors per icon for clarity

Creating Packs

A pack is a separate npm package under @pxlkit/. Here's the structure:

Pack Structure
packages/your-pack/
├── src/
│   ├── icons/
│   │   ├── icon-one.ts      # Individual icon files
│   │   ├── icon-two.ts
│   │   └── icon-three.ts
│   └── index.ts             # Export all icons + IconPack
├── package.json
├── tsconfig.json
└── tsup.config.ts
Pack index.ts
import type { IconPack } from '@pxlkit/core';
import { IconOne } from './icons/icon-one';
import { IconTwo } from './icons/icon-two';

export { IconOne } from './icons/icon-one';
export { IconTwo } from './icons/icon-two';

export const YourPack: IconPack = {
  id: 'your-pack',
  name: 'Your Pack Name',
  description: 'A collection of ... icons',
  icons: [IconOne, IconTwo],
  version: '0.1.0',
  author: 'your-name',
};

After creating your pack, add it to the monorepo workspaces, register it in the web app's icon registry, and submit a pull request.