magic-crayon is a framework-agnostic Web Component for freehand drawing on a <canvas>.
It ships as a native custom element: <magic-crayon>.
- Native custom element (
magic-crayon) with Shadow DOM encapsulation - 16:9 drawing surface with automatic resize handling
- Configurable vertical anchor within host area:
anchor:top,center, orbottom(default)
- Undo/redo/clear controls
- Three color picker experiences:
crayon(default)swatchinput
- Configurable canvas background:
canvas-background:white(default) orblack- in
blackmode, the built-in black crayon/swatch is remapped to white for contrast
- Configurable selected crayon presentation:
full(default)clipped
- Optional outer boundary cue for host layout delineation:
on(default)off
- Configurable stroke sizing:
stroke-width(base width for drawing)eraser-scale(eraser width multiplier)
- Optional built-in width slider controls:
width-controls(offdefault)- replaceable via
slot="width-controls"
- Optional action control presentation:
control-style:icon(default) ortext
- Configurable canvas cursors by mode:
draw-cursor(default:crosshair)erase-cursor(default:cell)
- Export drawing data as:
blobdataurl
- Public API + custom events for host integration
- CSS custom properties for theming and typography overrides
npm install magic-crayonimport 'magic-crayon/defined'After importing, you can use:
<magic-crayon></magic-crayon>import { MagicCrayon, TAG_NAME } from 'magic-crayon'
if (!customElements.get(TAG_NAME)) {
customElements.define(TAG_NAME, MagicCrayon)
}<magic-crayon
id="pad"
serialization="blob"
color-picker="crayon"
selected-crayon="full"
anchor="bottom"
boundary="on"
canvas-background="white"
control-style="icon"
draw-cursor="crosshair"
erase-cursor="cell"
width-controls="off"
stroke-width="5"
eraser-scale="1"
></magic-crayon>
<script type="module">
import 'magic-crayon/defined'
const pad = document.getElementById('pad')
pad.addEventListener('save', event => {
const { data, serialization, meta, timestamp } = event.detail
console.log({ data, serialization, meta, timestamp })
})
</script>serialization:blob | dataurl(default:blob)color-picker:crayon | swatch | input(default:crayon)selected-crayon:full | clipped(default:full)anchor:top | center | bottom(default:bottom)boundary:on | off(default:on)canvas-background:white | black(default:white)control-style:text | icon(default:icon)draw-cursor: any valid CSS cursor string (default:crosshair)erase-cursor: any valid CSS cursor string (default:cell)width-controls:on | off(default:off)stroke-width: positive number (default:5)eraser-scale: positive number (default:1)
serialization: 'blob' | 'dataurl'colorPicker: 'crayon' | 'swatch' | 'input'selectedCrayon: 'full' | 'clipped'anchor: 'top' | 'center' | 'bottom'boundary: 'on' | 'off'canvasBackground: 'white' | 'black'controlStyle: 'text' | 'icon'drawCursor: stringeraseCursor: stringwidthControls: 'on' | 'off'strokeWidth: number(must be positive)eraserScale: number(must be positive)drawing: Blob | string | null
getDrawingData(serialization?: 'blob' | 'dataurl'): Promise<Blob | string>setDrawingData(data: Blob | string): Promise<void>clearDrawingData(): void
savedetail:{ data, serialization, meta, timestamp }
undoavailabilitychangedetail:{ available, size }
redoavailabilitychangedetail:{ available, size }
widthchangedetail:{ strokeWidth, eraserScale, eraserWidth, source }
width-controls- Replaces only the width-controls sub-UI while keeping default tools/actions.
- Built-in fallback UI is rendered when no assigned content is provided.
magic-crayon supports host-level CSS custom property overrides without replacing its
Shadow DOM structure.
Use the component CSS variables (--magic-crayon-*) as the stable theming API.
- For a complete token reference, design-system mapping, theme recipes, and integration recommendations, see docs/theming.md.
- For structural customization of only the width controls UI, use
slot="width-controls".
All events bubble and are composed.
Type declarations are published with the package and mapped via exports.
For local development, build, testing, and Storybook workflows, see docs/development.md.