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

@hozo/canvas

v0.4.0

Published

A declarative Canvas scene for Hozo, rendered by Canvas 2D on Web and Skia on React Native.

Readme

@hozo/canvas

Declarative retained-mode Canvas scenes for Hozo. The same scene is drawn with the browser's built-in Canvas 2D API on Web and @shopify/react-native-skia on React Native.

import { Canvas } from '@hozo/canvas'

export function Sparkline() {
  return (
    <Canvas
      width={320}
      height={120}
      viewBox={[0, 0, 100, 40]}
      accessibilityLabel="Revenue increased over the last six months"
    >
      <Canvas.Path
        path="M 0 36 L 20 30 L 40 32 L 60 18 L 80 20 L 100 4"
        stroke="#2563eb"
        strokeWidth={2}
        fill="none"
      />
    </Canvas>
  )
}

Indexed triangle meshes avoid turning already-projected geometry into SVG path strings. Consecutive vertex triples are used when indices is omitted; an incomplete or invalid triple is skipped as one unit on both renderers.

<Canvas.TriangleMesh
  vertices={[
    { x: 10, y: 10 },
    { x: 90, y: 10 },
    { x: 50, y: 70 },
  ]}
  colors={[
    { r: 1, g: 0, b: 0 },
    { r: 0, g: 1, b: 0 },
    { r: 0, g: 0, b: 1 },
  ]}
/>

Vertex colours use normalized sRGB channels with optional per-vertex alpha and replace the uniform fill. Skia interpolates them through Vertices; Canvas 2D composes three bounded barycentric ramps per face, avoiding contrast-dependent tessellation.

Meshes can instead map an image with one top-left texture coordinate per vertex, where one unit spans one image tile. Web loads a URL; Native accepts the same URI form or a local asset ID. Sampling clamps at image edges by default; wrap: 'repeat' tiles on both axes, wrap: 'mirror' alternates reflected tiles, while wrapX and wrapY can override either axis independently. Repeated axes permit coordinates outside 0..1. intensity and tint: { r, g, b } multiply linear RGB while preserving alpha. Web caches the processed image (at most eight colour variants per decoded source, fewer for large images), requiring same-origin or CORS-enabled images for non-identity modulation. Native uses Skia colour filters. All modes use affine interpolation per triangle. Textures do not yet combine with vertex colours or provide perspective-correct sampling.

<Canvas.TriangleMesh
  vertices={[
    { x: 10, y: 10 },
    { x: 90, y: 10 },
    { x: 50, y: 70 },
  ]}
  texture={{
    source: '/checkerboard.png',
    coordinates: [
      { x: 0, y: 0 },
      { x: 1, y: 0 },
      { x: 0.5, y: 1 },
    ],
    filter: 'linear',
  }}
/>

Interaction

Every Canvas leaf supports portable onPress and destination-bearing href. The same topmost shape must contain both the pointer/touch start and release; dragging away cancels activation. Non-interactive drawing above a target does not block it.

<Canvas.Rect
  x={40}
  y={10}
  width={20}
  height={30}
  onPress={({ point, surfacePoint }) => {
    // point uses viewBox/scene coordinates; surfacePoint uses CSS pixels or
    // React Native layout points and is useful for positioning a tooltip.
    selectBar(point.x, surfacePoint.x)
  }}
/>

An href uses the same installed @hozo/navigation adapter as Link, Button href, and Pressable href. On Web, Ctrl/Command-click, Shift-click, and a middle-button press open a new browsing context without entering the client router. A named destination also produces a real anchor in the hidden accessibility layer; an action produces a button. Native exposes the matching link or button role.

<Canvas.Rect
  width={40}
  height={20}
  href="/orders/42"
  replace
  accessibilityLabel="Open order 42"
/>

onPress runs before navigation. It receives the familiar ctrlKey, metaKey, shiftKey, altKey, and button fields. Call preventDefault() to retain an activation for local interaction—for example, Ctrl/Command-click or Shift-click selection in a Canvas-drawn table—without following href.

<Canvas.Rect
  width={40}
  height={20}
  href="/orders/42"
  accessibilityLabel="Open order 42"
  onPress={(event) => {
    if (event.ctrlKey || event.metaKey || event.shiftKey) {
      event.preventDefault()
      toggleSelectedOrder(42)
    }
  }}
/>

Hit testing follows reverse paint order, viewBox, fit, nested group translation/rotation/scale/origin, and rectangle clips. It uses the closed shape's geometry rather than inspecting painted pixels. Changing only an event handler or disabled updates a separate registry and does not redraw the scene.

Line hit testing follows its painted stroke and cap. Path containment and text measurement are answered by each platform's renderer so they can participate without a second geometry or font implementation.

Canvas pixels cannot themselves provide keyboard or screen-reader interaction. A named interactive shape therefore gets a semantic control in a visually hidden sibling layer. An unnamed interactive shape remains pointer-only and reports a warning; give it an accessibilityLabel or provide equivalent visible controls outside the Canvas.

Canvas pixels are not semantic content. Every root must therefore either have an accessibilityLabel, provide an accessibleFallback (for example a data table), or opt out explicitly with decorative.

These are distinct accessibility modes. A label-only Canvas is exposed as one named image. When accessibleFallback is present, the pixel surface is hidden from assistive technology and the fallback is rendered as a visually hidden semantic sibling; it is not nested under an image role that would erase the fallback's table, list, or heading semantics. A decorative Canvas and its drawing surface are hidden completely.

Build a fallback from the source data rather than trying to reconstruct it from rectangles and paths. It should be informational rather than interactive: a visually hidden button or link would create an invisible keyboard or screen reader focus target.

<Canvas
  width={320}
  height={120}
  accessibilityLabel="Revenue by quarter"
  accessibleFallback={
    <table>
      <caption>Revenue by quarter</caption>
      <tbody>{/* rows made from the same series as the chart */}</tbody>
    </table>
  }
>
  {/* visual marks */}
</Canvas>

Skia is not installed by this package, so a Web-only installation does not download its native binaries. Add it only to a React Native app:

npx expo install @shopify/react-native-skia

The Native entry imports Skia directly and will fail at bundle time with a missing-module error until it is installed. It remains an app-level dependency so Web-only Hozo installations stay small.


Part of Hozo, a universal UI framework for React Native, compiled by Rust, with native semantics on every platform, first-class accessibility, and diagnostics for humans and AI agents alike. Most applications install @hozo/core and one build integration; see Getting started. Source and issues: github.com/iray-tno/hozo.