npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@substrate-system/image-editor

v0.0.5

Published

[![tests](https://img.shields.io/github/actions/workflow/status/nichoth/image-editor/nodejs.yml?style=flat-square)](https://github.com/substrate-system/package/actions/workflows/nodejs.yml) [![types](https://img.shields.io/npm/types/@substrate-system/imag

Downloads

668

Readme

image-editor

tests types module semantic versioning Common Changelog install size gzip size dependencies license

A web component for resizing images in the browser.

[See the live demo][demo-link].

Contents

Install

npm i -S @substrate-system/image-editor

Quick start

Import the element and its stylesheet, then use the tag in HTML.

import '@substrate-system/image-editor'
import '@substrate-system/image-editor/css'

Depends on a child image element.

<image-editor>
    <img
        src="/images/cinnamon-roll.jpg"
        width="640"
        height="480"
        alt="A cinnamon roll"
    >
</image-editor>

It captures the first img descendant when it connects. It replaces its children with the rendered editor UI. If no image is present, the element renders no children and writes a warning to the image-editor debug namespace.

How it works

  • Drag any corner handle to resize the image.
  • Resizing preserves the aspect ratio by default.
  • Add free-form to resize width and height independently.
  • The default minimum size is 50 by 50 pixels.
  • Focus a corner handle and use the arrow keys for keyboard resizing.
  • The ALT or +ALT button emits an event for your ALT-text UI.
  • The pencil button emits an event for your image-editing UI.
  • Completed resizes can produce a canvas-generated Blob.

The resize handles use pointer events and pointer capture, so a drag can continue if the pointer leaves the handle. The four handles are focusable and have corner-specific ARIA labels.

API

JavaScript API

The package exports the ImageEditor class and registers the image-editor custom element when imported.

ESM

import { ImageEditor } from '@substrate-system/image-editor'

CommonJS

const { ImageEditor } = require('@substrate-system/image-editor')

This extends web-component, so it has ImageEditor.TAG, ImageEditor.event('edit'), and on() and off() methods.

import { ImageEditor } from '@substrate-system/image-editor'

const editor = document.querySelector(ImageEditor.TAG)

// event type 'image-editor:edit'
editor.on('edit', event => {
    console.log(event.detail.img)
})

You can use addEventListener() with the full namespaced event names shown in the Events section.

Attributes

All attributes are reflected on the element. Kebab-case numeric attributes also have typed camel-case properties.

free-form

Boolean attribute. It is absent by default. When present, each resize axis is constrained independently instead of preserving the image aspect ratio.

<image-editor free-form>
    <img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>

The equivalent property is editor.freeForm:

editor.freeForm = true
editor.freeForm = false

The resize mode is captured when a pointer or keyboard resize starts. A mode change therefore applies to the next interaction.

min-width and min-height

Positive minimum dimensions in CSS pixels. Both default to 50.

<image-editor min-width="120" min-height="90">
    <img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>

The typed properties are editor.minWidth and editor.minHeight:

editor.minWidth = 120
editor.minHeight = 90

Missing, non-positive, non-finite, or otherwise invalid values resolve to the 50-pixel default when read. In aspect-ratio mode, both minimums are honored without distorting the image. In free-form mode, each axis is clamped independently.

visible

Controls when the resize outline and handles are disclosed. Accepted values are:

  • always keeps the outline and handles visible.
  • hover hides the handles until the editor is hovered or focused.
  • touch keeps them visible on touch devices and uses hover disclosure on other devices.

The default is touch. Missing or unsupported values also resolve to touch.

<image-editor visible="hover">
    <img src="/images/example.jpg" width="320" height="240" alt="Example">
</image-editor>

The typed property is editor.visible:

editor.visible = 'always'

Changing the attribute or property updates an already-rendered editor.

Events

Events bubble from the element, are cancelable, and use the image-editor:<name> naming convention. The examples below use the native event API.

image-editor:resize-start

Emitted when a pointer resize begins or when the first arrow key starts a keyboard resize sequence. The event has no detail payload.

editor.addEventListener('image-editor:resize-start', () => {
    console.log('resize started')
})

image-editor:resize

Emitted with the current dimensions. Keyboard resizing emits this event for each arrow-key change. Pointer resizing emits it once after a dragged pointer is released.

The detail contains rounded CSS-pixel dimensions:

editor.addEventListener('image-editor:resize', event => {
    const { width, height, blob } = event.detail
    console.log(width, height)

    if (blob) {
        // A canvas-generated Blob is available when canvas conversion worked.
        upload(blob)
    }
})

For pointer resizing, blob may be included in the detail after the canvas conversion completes. If canvas conversion cannot produce a blob, the detail still contains width and height.

image-editor:resize-end

Emitted when a keyboard resize sequence is committed on keyup and canvas conversion returns a blob. Its detail is always:

{
    blob: Blob,
    width: number,
    height: number
}

The blob is drawn at the same pixel dimensions reported in the detail. For images loaded from another origin, configure CORS on the image response and use an appropriate crossorigin value before loading the image if your application needs blob output.

image-editor:edit

Emitted when the pencil button is clicked. The event is cancelable and its detail contains the captured image element:

editor.addEventListener('image-editor:edit', event => {
    const image = event.detail.img
    openImageEditor(image)
})

The component does not open a dialog or modify the image for you.

image-editor:alt

Emitted when the ALT badge is clicked. The event is cancelable and its detail contains the current alt value and the captured image:

editor.addEventListener('image-editor:alt', event => {
    const { alt, img } = event.detail
    openAltTextEditor({ alt, img })
})

An absent alt attribute is reported as an empty string. The badge displays +ALT for an absent or empty value and ALT for a non-empty value. It updates when the image's alt attribute changes.

Keyboard resizing

After a handle receives focus:

  • Arrow keys change the size by 10 pixels.
  • Shift plus an arrow key changes the size by 50 pixels.
  • Escape restores the inline width and height from before the sequence.
  • Releasing an arrow key commits the current keyboard resize sequence.

Keyboard resizing uses the same aspect-ratio and minimum-size rules as pointer resizing. Escape cancels the sequence and does not produce a resize-end event.

Styling

The component renders in the light DOM. Import the package stylesheet to get the outline, controls, handles, and undefined-element guard:

import '@substrate-system/image-editor/css'

The minified stylesheet is available at:

import '@substrate-system/image-editor/min/css'

CSS custom properties

Defaults are defined globally on :root. Override them after importing the package stylesheet:

:root {
    --image-editor-outline-color: rebeccapurple;
    --image-editor-handle-bg: white;
    --image-editor-button-bg: rgb(0 0 0 / 65%);
}

Available properties are:

  • --image-editor-outline-color: visible outline color; default black.
  • --image-editor-outline-hidden-color: hidden outline color; default transparent.
  • --image-editor-outline-width: outline width; default 2px.
  • --image-editor-outline-style: outline style; default dashed.
  • --image-editor-handle-size: square handle size; default 10px.
  • --image-editor-handle-bg: handle fill color; default white.
  • --image-editor-handle-border: handle border color; default black.
  • --image-editor-handle-border-width: handle border width; default 1px.
  • --image-editor-button-size: button height and icon-button width; default 2rem.
  • --image-editor-button-icon-size: pencil icon size; default 1.125rem.
  • --image-editor-button-bg: button background; default 65% black.
  • --image-editor-button-bg-hover: hovered button background; default 80% black.
  • --image-editor-button-focus-color: focus-ring color; default #1d9bf0.
  • --image-editor-button-padding: edit-button padding; default 0.
  • --image-editor-button-icon-stroke-width: pencil stroke width; default 2px.
  • --image-editor-overlay-padding: overlay inset; default 8px.

Avoiding undefined-content flash

The stylesheet hides image-editor until its custom element definition is available. If your page has several custom elements, you can also hide the whole page until they are defined:

<html class="reduce-fouce">
    <head>
        <style>
            html.reduce-fouce { opacity: 0; }
        </style>
        <noscript>
            <style>
                html.reduce-fouce { opacity: 1 !important; }
            </style>
        </noscript>
    </head>
</html>

Reveal the page after the definition is ready, with a timeout so a failed definition cannot leave the page hidden forever:

await Promise.race([
    customElements.whenDefined('image-editor'),
    new Promise(resolve => setTimeout(resolve, 2000))
])

document.documentElement.classList.remove('reduce-fouce')

Pre-built files

The package publishes JavaScript and CSS files in dist/:

  • index.js and index.min.js are ESM.
  • index.cjs and index.min.cjs are CommonJS.
  • index.css and index.min.css are the stylesheets.

If your application does not bundle npm packages, copy the minified files to a directory served by your web server:

cp node_modules/@substrate-system/image-editor/dist/index.min.js public/
cp node_modules/@substrate-system/image-editor/dist/index.min.css public/

Then load the stylesheet and module in HTML:

<link rel="stylesheet" href="/index.min.css">
<script type="module" src="/index.min.js"></script>

Development

Install dependencies and start the Vite example app:

npm install
npm start

The example app runs on port 2222 by default. Useful project commands are:

npm test          # Run browser tests
npm run lint      # Run ESLint
npm run build     # Build JavaScript and CSS artifacts

The package build writes publishable artifacts to dist/. The example build uses npm run build-example and writes its output to public/.