SkillAgentSearch skills...

Zumly

Zoom-based navigation for the web. Turn any site into a spatial zoom interface with just HTML

Install / Use

/learn @zumerlab/Zumly
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <a href="https://github.com/zumerlab/zumly"> <!-- Built from docs/zumly-logo.png in this repo; raw URL so the image works on npm too --> <img src="https://raw.githubusercontent.com/zumerlab/zumly/main/docs/zumly-logo.png" alt="Zumly" width="200"> </a> </p> <h1 align="center">ZUMLY</h1> <p align="center"><em>Z over XY</em></p> <p align="center"><strong>Focus-driven navigation. Zoom into what matters.</strong></p> <p align="center"> <a href="https://zumerlab.github.io/zumly/"><strong>Live Demo</strong></a> </p> <p align="center"> <a href="https://www.npmjs.com/package/zumly"><img src="https://img.shields.io/npm/v/zumly.svg" alt="npm version"></a> </p> <p align="center"> <strong>Zumly</strong> is a JavaScript library for <strong>hierarchical zoom navigation</strong>: you move in <strong>Z</strong> (depth) through discrete views laid out in the <strong>XY</strong> plane, with spatial transitions instead of flat screen swaps. It is inspired by <a href="https://en.wikipedia.org/wiki/Zooming_user_interface">zoomable user interfaces (ZUI)</a> but targets <strong>structured, trigger-driven</strong> zoom—not infinite pan/zoom canvases. </p>

Status

Zumly is under active development. The core stack is stable: depth and lateral navigation, pluggable transition drivers (CSS, WAAPI, none, Anime.js, GSAP, Motion, custom), unified nav UI (depth + lateral, eight positions), view resolver and prefetch cache, optional plugin API (<code>.use()</code>), and the hash router plugin. View sources include HTML strings, URLs, async functions, objects with <code>render()</code>, DOM nodes, and web component tags.

Zoom-out geometry uses batched DOM reads plus pure math where possible to cut layout thrash before animations (see Geometry optimization).

Docs: Roadmap & topics · Transition drivers · Geometry notes

Overview

Unlike free-pan ZUIs, Zumly focuses on discrete, hierarchical navigation: users zoom into a focused element (<code>.zoom-me</code>) to open the next view, so attention (focus) and depth (Z) stay aligned with layout (XY).

The engine is UI-agnostic—you supply markup and CSS. Transforms and timing are handled for you; design systems and frameworks integrate by resolving each view to a DOM subtree (see View sources and Framework integration below).

What Zumly is

Zumly is not a freeform zooming canvas or map-like navigation system. It is a discrete, hierarchical zoom interface: screens are views at different depths, connected by triggers, with continuous motion between them.

It fits especially well when:

  • you want focus-driven flow (zoom into what matters)
  • spatial context between parent and child should persist
  • you are building menus, stories, dashboards, or exploratory UIs without a classic router-only metaphor

Installation

NPM

npm install zumly

# or

yarn add zumly

CDN

Include Zumly in your project via a <script> tag from unpkg.com/zumly.

Direct download

Download the built files from unpkg.com/zumly (see the dist folder).

Setup

Browser bundle (global)

  1. Add the CSS in your <head>:
<link rel="stylesheet" href="zumly/dist/zumly.css">
<!-- or https://unpkg.com/zumly/dist/zumly.css -->
  1. Load the JS bundle (it exposes window.Zumly):
<script src="zumly/dist/zumly.js"></script>
<!-- or https://unpkg.com/zumly/dist/zumly.js -->

Hello World

  1. Add a container with the class zumly-canvas:
<div class="example zumly-canvas"></div>
  1. Create your views and start Zumly:
const hello = `
<div class="z-view">
  H E L L O <br>
  W <span class="zoom-me" data-to="world">O</span> R L D!
</div>
`;

const world = `
<div class="z-view">
  <img src="https://raw.githubusercontent.com/zumly/website/gh-pages/images/world.png" alt="World">
</div>
`;

const app = new Zumly({
  mount: '.example',
  initialView: 'hello',
  views: { hello, world },
});

await app.init();

Options

Zumly constructor:

| Option | Type | Required | Description | |--------|------|----------|-------------| | mount | string | Yes | CSS selector for the canvas element (must have class zumly-canvas). | | initialView | string | Yes | Name of the first view to show. | | views | object | Yes | Map of view names to view sources (see View sources below). | | preload | string[] | No | View names to resolve and cache when the app initializes. | | transitions | object | No | Duration, ease, cover, driver, effects, stagger, hideTrigger for zoom transitions. | | deferred | boolean | No | Defer content rendering until after animation completes (default: false). | | debug | boolean | No | Enable debug messages (default: false). | | lateralNav | boolean | object | No | Lateral navigation UI: { mode, arrows, dots, keepAlive, position }. | | depthNav | boolean | object | No | Depth back button: { position }. Default: 'bottom-left'. | | inputs | boolean | object | No | Input methods: { click, keyboard, wheel, touch }. | | componentContext | object | No | Context passed to component-style views. |

Transitions (optional):

transitions: {
  driver: 'css',       // 'css' | 'waapi' | 'anime' | 'gsap' | 'motion' | 'none' or custom function(spec, onComplete)
  cover: 'width',      // or 'height' — how the previous view scales to cover the trigger
  duration: '1s',
  ease: 'ease-in-out',
  effects: ['blur(3px) brightness(0.7)', 'blur(8px) saturate(0)'],  // CSS filters for [previous, last] background views
  stagger: 0,          // delay (ms) between layers during transition
  hideTrigger: false,  // false | true (visibility:hidden) | 'fade' (opacity crossfade)
  // threshold: { enabled: true, duration: 300, commitAt: 0.5 }  // parsed but not wired in the engine yet
}

transitions.parallax is accepted for compatibility but not applied (reserved; intensity is fixed to 0 in the engine).

Transition drivers: Zoom animations are handled by a pluggable driver (transitions.driver). You can swap implementations without changing app logic. To author your own, see docs/DRIVER_API.md and the zumly/driver-helpers export.

| Driver | Description | |--------|-------------| | 'css' (default) | CSS keyframes and animationend; uses zumly.css variables. | | 'waapi' | Web Animations API (element.animate()). No extra dependency. | | 'none' | No animation; applies final state immediately. Useful for tests or instant UX. | | 'anime' | Anime.js — requires global anime (load from CDN before use). | | 'gsap' | GSAP — requires global gsap (load from CDN before use). | | 'motion' | Motion — requires global Motion (load from CDN before use). | | function(spec, onComplete) | Custom driver. Receives { type, currentView, previousView, lastView, currentStage, duration, ease } and must call onComplete() when done. |

Example with instant transitions (e.g. for tests):

const app = new Zumly({
  mount: '.canvas',
  initialView: 'home',
  views: { home, detail },
  transitions: { driver: 'none', duration: '0s' },
});

Lateral navigation (arrows + dots bar):

lateralNav: true                            // mode: 'auto' (default), bottom-center
lateralNav: false                           // disabled
lateralNav: { mode: 'always' }              // always show when siblings exist
lateralNav: { mode: 'auto', dots: false }   // auto mode, no dots
lateralNav: { position: 'top-center' }      // top instead of bottom

| Mode | Description | |------|-------------| | 'auto' (default) | Shows lateral nav only when the current view doesn't cover the full canvas — preserving spatial context. | | 'always' | Always shows lateral nav when siblings exist, regardless of coverage. |

Position: 'bottom-center' (default) or 'top-center'.

Depth navigation (back button):

depthNav: true                              // default: back button at bottom-left
depthNav: false                             // disabled
depthNav: { position: 'top-left' }          // top instead of bottom

Position: 'bottom-left' (default) or 'top-left'. The depth and lateral nav are separate, independently positioned components.

Zoomable elements:

  • Give the view root the class z-view.
  • Add class zoom-me and data-to="viewName" to the element that triggers zoom-in.
  • Per-trigger overrides via data-* attributes:

| Attribute | Description | |-----------|-------------| | data-to | Required. Target view name. | | data-with-duration | Override transition duration (e.g. "2s"). | | data-with-ease | Override easing function. | | data-with-cover | Override cover dimension ("width" or "height"). | | data-with-stagger | Override stagger delay in ms (e.g. "100"). | | data-with-effects | Override effects (pipe-separated: "blur(5px)\|blur(10px)"). | | data-hide-trigger | Override hideTrigger ("fade" or presence = hide). | | data-deferred | Override deferred rendering (presence = true). | | data-* | Any other data attribute becomes a prop in ViewContext.props. |

<div class="z-view">
  <div class="zoom-me" data-to="detail"
       data-with-duration="2s"
       data-with-ease="ease-in"
       data-with-cover="height"
       data-with-stagger="100"
       data-id="42">
    Zoom in
  </div>
</div>

View sources

Each entry in views is a view source. The resolver detects the type and resolves to a DOM node. Hyphenated view names (e.g. 'my-dashboard') are resolved as keys in views first; only raw template strings with a hy

View on GitHub
GitHub Stars114
CategoryDevelopment
Updated1d ago
Forks10

Languages

JavaScript

Security Score

100/100

Audited on Apr 5, 2026

No findings