@daz4126/morpheus
v0.1.0
Published
HTML-first navigation with intent prefetching and focused DOM morphing
Downloads
140
Maintainers
Readme
Morpheus
Morpheus is a small, framework-neutral navigation library for HTML-first sites. It intercepts eligible same-origin links, prefetches on user intent, and uses Idiomorph to morph one page target while the surrounding layout remains in place.
It is designed to work in static HTML, Laravel, Rails, and server-rendered applications without becoming a client-side router.
Why this package exists
Lunula and Swifty had closely related Morpheus implementations. This package keeps their shared behavior in one neutral core and leaves project-specific contracts in adapters:
- the core accepts normal successful HTML responses and defaults to
<main>; - the Lunula adapter requires Lunula's response opt-in headers and preserves its Helium-aware node replacement behavior;
- the Swifty adapter retains
swifty:*event, history, attribute, and request header aliases.
The unscoped morpheus npm name belongs to an unrelated animation package, so
this package uses the available @daz4126/morpheus scope.
Install
npm install @daz4126/morpheusimport Morpheus from "@daz4126/morpheus";
const navigation = new Morpheus({ target: "main" });
navigation.start();Every linked page must return HTML containing the same target selector. If a request fails, leaves the current origin, returns non-HTML, or omits the target, Morpheus falls back to normal browser navigation.
Navigation preserves destination fragments for anchor scrolling and focus,
including when a page was prefetched. Back and forward navigation restore the
scroll position recorded for each history entry while the instance is running.
Call navigation.stop() to remove its listeners, cancel pending navigation,
clear loading indicators, and restore the browser's previous scroll-restoration
setting. The instance can subsequently be started again.
Declarative startup
With a bundler, import the auto-start entry and put its configuration on the module script:
import "@daz4126/morpheus/auto";<script type="module" src="/assets/app.js"
data-morpheus
data-target="main"
data-prefetching="intent"
data-prefetch-delay="75"
data-cache-size="20"
data-cache-ttl="15"></script>For plain HTML, serve the global build from your own assets or an npm CDN:
<script src="/assets/morpheus.global.min.js" data-morpheus data-target="main"></script>The global build exposes Morpheus.Morpheus, Morpheus.startMorpheus, and the
other public exports. Auto-started navigation is available as window.morpheus.
Laravel with Vite
Install the package, then start it in resources/js/app.js:
import Morpheus from "@daz4126/morpheus";
new Morpheus({ target: "#page" }).start();Use the same target in every Blade layout response:
<main id="page">
@yield('content')
</main>No Laravel middleware is required for the generic core. You may inspect the
X-Morpheus-Navigation: true request header to return a smaller response, but
the returned HTML must still contain the configured target.
Project adapters
import LunulaNavigation from "@daz4126/morpheus/adapters/lunula";
new LunulaNavigation().start();The Lunula adapter defaults to #morpheus-page, requires
X-Morpheus-Navigation: morph, supports X-Morpheus-Navigation: reload, uses
X-Morpheus-Title, respects X-Morpheus-Prefetch-Cache: no-store, and replaces
nodes when @*, :*, or data-he* attributes change.
The auto-start entry is available at
@daz4126/morpheus/adapters/lunula/auto. Its built
dist/adapters/lunula.auto.js file is standalone and can be copied directly
into a non-Node application's public assets.
import SwiftyNavigation from "@daz4126/morpheus/adapters/swifty";
new SwiftyNavigation({ target: "main" }).start();The Swifty adapter emits both morpheus:* and swifty:* events and keeps its
legacy data attributes, history state, and request headers.
@daz4126/morpheus/adapters/swifty/auto provides the equivalent declarative
startup and a standalone dist/adapters/swifty.auto.js browser asset.
Browser contract
data-morpheus="off"ordata-morpheus-navigation="off"opts out a link or ancestor subtree.data-morpheus-prefetch="off"disables intent prefetch for a link subtree.data-morpheus-permanentpreserves an element across morphs.data-morpheus-active-prefix="/docs"maintainsaria-current="page".<html data-morpheus-navigating>and targetaria-busyindicate loading.- Modified clicks, downloads, external links, cross-origin URLs, hash-only changes, and non-GET navigation retain native browser behavior.
Requests send Accept: text/html and X-Morpheus-Navigation: true. Prefetches
also send X-Morpheus-Prefetch: true.
Events
Events are dispatched on document:
morpheus:before-navigate(cancelable)morpheus:navigation-startmorpheus:before-morph(cancelable)morpheus:loadmorpheus:navigation-errormorpheus:navigation-endmorpheus:invalidate(dispatch this to clear the prefetch cache)
morpheus:load fires on the animation frame after the morph, after mutation
observers have had an opportunity to run.
Options
The most common options are target, prefetching, prefetchDelay,
cacheSize, and cacheTTL (seconds). The package also exposes integration
hooks for event aliases, history aliases, selectors, request and response
headers, response validation, custom Idiomorph callbacks, and attribute-prefix
replacement. See MorpheusOptions in the bundled TypeScript declarations for
the complete API.
Development
npm install
npm run checkSibling Lunula and Swifty checkouts can use this working tree without changing
their registry dependency by running npm run morpheus:link in those projects.
Run npm run build here before refreshing a consumer's copied browser assets.
npm run build creates bundled ESM, browser-global, source map, declaration,
and Idiomorph license files in dist/. npm run check tests the behavior,
verifies every public export, and performs a publish dry run.
License
MIT. Idiomorph is distributed under the Zero-Clause BSD license (0BSD).
