Case study: jmpress.js docs overhaul (free sample)

Free sample engagement. This is a complete documentation overhaul performed for free on a public open-source repository, held to the exact paid-tier standard: same acceptance checklist, same claim-by-claim verification against the code. The repo was not contacted and no pull request was opened. If this were a paid order, this page is what delivery would look like.

The repo

jmpressjs/jmpress.js is a jQuery plugin for building websites on an infinite canvas: a jQuery port of impress.js with an extensible component architecture.

factvalue
Stars1,485 (read from the GitHub API, 2026-09-16)
Last activity2026-05-25 (commit 18fb7c5, master)
Version documented0.4.5
LicenseMIT
Billable files70 (76 on master, minus 6 vendored: bundled jQuery/QUnit test libs and .min.js)
Engagement tierM: $3,000 (51-200 files)

Before

The repo’s entire documentation was a 1,263-byte README: a title, two sentences of description, demo links, a browser-support paragraph, and a single line under USAGE saying “See the DOCS” (an external link). No install instructions. No quickstart. No API reference for its ~25 public methods, ~20 callbacks, settings tree, or 20+ data-* attributes. No architecture overview of the component/callback system that is the project’s main design. No plugin documentation for the four plugins that ship in src/plugins/. No troubleshooting, even though three real failure modes were documented only in scattered GitHub issues. The README’s demo and docs links used the retired jmpressjs.github.com domain.

After

The full deliverable, as produced. Read it and judge the proof:


The deliverable: jmpress.js documentation

Full documentation for jmpress.js at version 0.4.5 (commit 18fb7c5, master, 2026-05-25), written to the paid-tier standard of the repo docs overhaul service. Every claim below was checked against the source at that commit; see VERIFY.md for the per-claim source references.

Contents

  1. What it is
  2. Architecture overview
  3. Setup guide
  4. Quickstart (5 minutes)
  5. Usage and API reference
  6. Plugins
  7. Writing a plugin
  8. Troubleshooting
  9. Contributing
  10. License

1. What it is

jmpress.js is a jQuery plugin for building websites on an infinite canvas. You declare “steps” as ordinary HTML elements, position each step anywhere in a 3D space with data-* attributes (x, y, z, rotation, scale), and jmpress.js moves a camera between them with CSS3 transforms and transitions. The result is a presentation (or any step-based page) that pans, rotates, and zooms through your content.

It started as a jQuery port of impress.js and adds a component architecture on top: every capability (keyboard, mouse, touch, hash routing, AJAX loading, templates, animations, viewport scaling) is a component that plugs into a shared callback pipeline, and third-party plugins use the same public interface.

Typical uses: slide decks, product tours, embedded slideshows on a page, interactive diagrams that zoom through detail levels.

2. Architecture overview

The plugin model

One jQuery method drives everything: $(element).jmpress(...). With no arguments or a settings object it initializes the element; with a string it dispatches to a named method:

$('#deck').jmpress();            // initialize
$('#deck').jmpress('next');      // call a method
$('#deck').jmpress({            // initialize with settings
  keyboard: { use: true }
});

What initialization builds

When you call $('#deck').jmpress(), the core (src/components/core.js) wraps your element in generated structure: a container, an area, and a canvas. Steps are found by the stepSelector setting (default .step). The canvas is positioned absolutely inside the area, and the active step is brought into view by transforming the canvas with translate / rotate / scale (perspective 1000px, transform-origin configurable, defaults top left for steps and 50%/50% for the area). Unsupported browsers get the not-supported class on the element instead of a broken presentation.

Each step gets a stepData object (stored with jQuery .data("stepData")) holding its parsed position, scale, rotation, and feature flags. Steps are identified by element id; the container carries classes step-<id> and delegating-step-<id> so CSS can target the current state, and the active step (plus its nested parents) gets configurable active classes (active and nested-active by default).

The callback pipeline

Capabilities are not hard-coded in the core. Instead the core owns a callback registry (beforeInit, initStep, afterInit, selectInitialStep, selectNext, selectPrev, selectHome, selectEnd, beforeChange, applyTarget, beforeActive, setActive, setInactive, applyStep, unapplyStep, idle, checkNoSupport, beforeDeinit, afterDeinit, beforeInitStep). Each component file registers hooks into it:

ComponentResponsibility
core.jsinit/deinit, method dispatch, step selection, callback registry, prefixed CSS helper
near.js$(el).near(selector, backwards) jQuery helper: nearest step in DOM order
transform.jsparses data-x/y/z/r/phi/rotate*/scale* into stepData; applies CSS transforms
active.jsadds/removes active / nested-active classes on the step and its parents
circular.jswraps navigation from last step back to first; data-exclude skips a step
start.jschooses the initial step (from the start setting)
ways.jscustom navigation order via data-next / data-prev and the route method
ajax.jslazy-loads step content from href / data-src with jQuery .load()
hash.jssyncs the active step with the URL hash (#/step-id); a[href^="#"] links select steps
keyboard.jsarrow / space / pgup / pgdn / home / end / tab key bindings, with form-field ignore rules
viewport.jsscales the canvas to a viewport, user zoom (+/-, wheel, drag)
mouse.jsclicking a step selects it (unless user-zoomed)
mobile.jsswipe left/right on touch devices goes prev/next (50px threshold)
templates.jsnamed templates that position groups of steps programmatically
jqevents.jsfires enterStep / leaveStep jQuery events (non-bubbling) on steps
animation.jssub-step animations via data-jmpress="<name> [after <delay>]"

The full build concatenates the components in that order (dist/jmpress.js). A smaller impress-compatible build (dist/jmpress.impress.js) ships only core, near, transform, active, circular, hash, keyboard, and mouse. dist/jmpress.all.js adds all four plugins. Build order is fixed in Gruntfile.js.

How a selection works

Calling next(), prev(), home(), end(), select(el), or goTo(el) runs the step-selection flow: the selectPrev/Next/Home/End callbacks pick the target step (default: DOM order via near(); customizable per step with data-next/data-prev or globally with route()), then the core fires beforeChange, applies the canvas transform to the target (applyTarget), marks the old step inactive and the new one active (which fires the jQuery enterStep / leaveStep events), and starts an idle timer that fires the idle callback after the transition settles. Transition timing is configurable (animation.transitionDuration default 1s plus transitionDelay default 500ms; the core’s internal transitionDuration setting defaults to 1500ms).


3. Setup guide

Requirements

  • jQuery. The test suite pins jQuery 2.1.4. Do not use jQuery 2.2.1: it throws Syntax error, unrecognized expression: a[href^=#] because the hash component selects links with a[href^='#'] (see Troubleshooting).
  • A browser with CSS3 transforms and transitions. Unsupported browsers are detected and get the not-supported class instead of a broken deck. (The README-era caniuse link references css-animation; modern Chrome, Firefox, Safari, and Edge all qualify.)

Install

Option A: npm (recommended for apps).

npm install jmpress.js

The package ships dist/jmpress.js (see package.json files). Include it after jQuery:

<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/jmpress.js/dist/jmpress.js"></script>

Important: the npm-published package has historically lagged behind the repository (a known, reported issue). If transitions do not work with the npm build, build from the repository instead (Option C) or verify you have the latest published version.

Option B: direct download. Copy dist/jmpress.js from the repository (or a release) into your project and include it after jQuery. For sub-step animations also include dist/basic-animations.css (built from src/css/animations/basic/).

Option C: build from source. Requires Node and grunt (the build uses the grunt 0.4-era toolchain from devDependencies):

git clone https://github.com/jmpressjs/jmpress.js.git
cd jmpress.js
npm install
grunt            # clean, jshint, concat, uglify, cssmin

This produces dist/jmpress.js, dist/jmpress.impress.js, dist/jmpress.all.js, minified variants, and dist/basic-animations.css. Run npm test (grunt qunit) to execute the test suite.

Which build to include

FileContentsUse when
dist/jmpress.jscore + all 16 componentsstandard use
dist/jmpress.impress.jscore, near, transform, active, circular, hash, keyboard, mousemigrating from impress.js, or minimal footprint
dist/jmpress.all.jseverything + all four pluginsyou need duration, presentation-mode, secondary, toggle
dist/plugins/jmpress.<name>.jsone plugin at a timeyou need a single plugin with the standard build
dist/basic-animations.cssappear, drive, expand, fade, warp, zoom animation classesyou use data-jmpress animations

4. Quickstart (5 minutes)

Create index.html (adapted from examples/simple/index.html):

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>My first jmpress deck</title>
  <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
  <script src="jmpress.js"></script>
  <style>
    .step { width: 900px; }
  </style>
</head>
<body>

<div id="deck">
  <div id="home" class="step">
    <h1>Hello, infinite canvas</h1>
    <p>Use the arrow keys to move.</p>
  </div>
  <div class="step" data-x="1000">
    <h1>Step two</h1>
    <p>1000 pixels to the right.</p>
  </div>
  <div class="step" data-x="1000" data-y="1000" data-rotate="90">
    <h1>Step three</h1>
    <p>Down, and rotated 90 degrees.</p>
  </div>
</div>

<script>
$(function() {
  $('#deck').jmpress();
});
</script>

</body>
</html>

Open it in a browser. Right arrow / space / page down moves forward, left arrow / page up moves back, Home and End jump to the ends. The URL hash updates to #/home, #/<generated-id>, etc., so any step is linkable.


5. Usage and API reference

Positioning steps: data attributes

Every step is an element matching stepSelector (default .step). Position is declared with data-* attributes, parsed as floats (missing values get sane defaults: position 0, scale 1):

AttributeDefaultMeaning
data-x, data-y, data-z0position in pixels on each axis
data-r0polar radius in pixels (used with data-phi; x falls back to r·sin(phi), y to −r·cos(phi))
data-phi0polar angle in degrees, used with data-r
data-rotate0rotation in degrees
data-rotate-x, data-rotate-y, data-rotate-z0per-axis rotation
data-scale1uniform scale
data-scale-x, data-scale-yoffper-axis scale
data-scale-z1z scale
data-view-port-width, data-view-port-heightglobalper-step viewport size override
data-view-port-min-scale, data-view-port-max-scaleglobalper-step zoom limits
data-view-port-zoomableglobalper-step zoom steps
data-templatenonespace-separated template names (see Templates)
data-next, data-prevDOM orderselectors overriding next/previous navigation (see Ways)
data-src (or href)noneURL to lazy-load step content via AJAX
data-excludeoffany value except false/no excludes the step from circular navigation
data-jmpressnonesub-step animation, e.g. fade or fade after 500ms
data-duration, data-duration-actionglobalauto-advance delay in ms and action (duration plugin)
data-secondary, data-secondary-*nonesecondary animation swap rules (secondary plugin)

All methods are called on an initialized element, chainable unless noted:

var deck = $('#deck').jmpress();

deck.jmpress('select', '#step-id');   // go to a step (selector, element, or jQuery object)
deck.jmpress('select', { step: '#a', substep: 2 }, 'reason'); // with substep and reason
deck.jmpress('goTo', '#step-id');     // like select, with reason "jump"
deck.jmpress('next');                 // returns the newly active step
deck.jmpress('prev');                 // returns the newly active step
deck.jmpress('home');                 // first step (respects circular/exclude rules)
deck.jmpress('end');                  // last step
deck.jmpress('reselect');             // re-select the current step (e.g. after resize)
deck.jmpress('reselect', 'resize');   // same, with a reason string passed through
deck.jmpress('scrollFix');            // force any stray scrolling back to 0
deck.jmpress('active');               // returns the current step (jQuery object)
deck.jmpress('settings');             // returns the live settings object
deck.jmpress('current');              // returns internal state (advanced)
deck.jmpress('canvas', { opacity: 0.5 }); // set CSS on the generated canvas
deck.jmpress('container');            // returns the generated container element
deck.jmpress('init', newStepEl);      // initialize a dynamically added step
deck.jmpress('reapply', stepEl);      // re-apply transforms to a step
deck.jmpress('fire', 'callbackName', element, eventData); // invoke a callback manually
deck.jmpress('deinit');               // tear down jmpress on the element
deck.jmpress('deinit', stepEl);       // de-initialize a single step
deck.jmpress('initialized');          // true if the element is initialized

Registered by components (only present in the full build):

deck.jmpress('route', ['#a', '#b', '#c']);      // custom navigation order (ways)
deck.jmpress('route', '#a');                    // self-route (stays)
deck.jmpress('zoomIn', x, y);                   // user zoom in at point (viewport)
deck.jmpress('zoomOut', x, y);                  // user zoom out at point (viewport)
deck.jmpress('zoomTranslate', x, y);            // pan while zoomed (viewport)
deck.jmpress('template', 'myTmpl', { x: 100 });  // register a named template
deck.jmpress('apply', '#steps', 'myTmpl');       // apply a template to steps
deck.jmpress('toggle', 84, { keyboard: { use: true } }, true); // bind key 84 (t) to init/deinit

Static interface (no element needed):

$.jmpress('register', 'myMethod', function() { /* ... */ }); // add a method (plugin API)
$.jmpress('myCallback', function(step, eventData) { /* ... */ }); // add a global callback hook
$.jmpress('defaults');    // the global defaults object (mutated by components/plugins)
$.jmpress('dataset', el);  // read an element's data-* attributes as an object
$.jmpress('css', el, props); // set CSS with vendor prefixes resolved
$.jmpress('pfx', 'transform'); // get the supported prefixed property name

Per-instance callback hooks work through the same dispatch: passing a registered callback name plus a function appends to that instance’s settings, e.g. $('#deck').jmpress('setActive', function(step) { ... }).

jQuery events

Steps fire two non-bubbling events (via triggerHandler, so nested decks do not interfere):

$('#deck .step').on('enterStep', function() {
  console.log('entered', this.id);
});
$('#deck .step').on('leaveStep', function() {
  console.log('left', this.id);
});

Settings reference

Pass a settings object to jmpress(). Nested objects merge with defaults.

$('#deck').jmpress({
  stepSelector: '.step',   // selector for steps
  containerClass: '',      // extra class on the generated container
  canvasClass: '',         // extra class on the generated canvas
  areaClass: '',           // extra class on the generated area
  notSupportedClass: 'not-supported',
  fullscreen: true,        // bind keyboard/mouse/touch to document instead of the element
  start: '#home',          // initial step (selector)
  transitionDuration: 1500, // ms; used for idle timing

  animation: {
    transformOrigin: 'top left',
    transitionProperty: 'transform, perspective, opacity', // vendor-prefixed automatically
    transitionDuration: '1s',
    transitionDelay: '500ms',
    transitionTimingFunction: 'ease-in-out',
    transformStyle: 'preserve-3d'
  },

  keyboard: {
    use: true,
    keys: {
      33: 'prev', 37: 'prev', 38: 'prev',   // pgup, left, up
      9: 'next:prev',                        // tab (shift+tab goes back)
      32: 'next', 34: 'next', 39: 'next', 40: 'next', // space, pgdn, right, down
      36: 'home', 35: 'end',                 // home, end
      187: 'zoomIn', 189: 'zoomOut'          // + and - (key codes vary by browser)
    },
    ignore: {                                // keys ignored inside form fields
      INPUT: [32, 37, 38, 39, 40],
      TEXTAREA: [32, 37, 38, 39, 40],
      SELECT: [38, 40]
    },
    tabSelector: 'a[href]:visible, :input:visible'
  },

  mouse: { clickSelects: true },  // clicking a step selects it

  hash: {
    use: true,        // read the step from the URL hash on load
    update: true,     // write #/step-id to the URL on change
    bindChange: true  // respond to hash changes (back/forward buttons)
  },

  viewPort: {
    width: false, height: false, // fixed viewport size (false = auto)
    maxScale: 0, minScale: 0,    // 0 = no limit
    zoomable: 0,                 // 0 = no user zoom steps
    zoomBindWheel: true,         // mouse wheel zooms
    zoomBindMove: true           // drag pans while zoomed
  },

  ajaxLoadedClass: 'loaded', // class added to steps whose AJAX content loaded

  // plugin settings (only when the plugin script is included)
  duration: {
    defaultValue: -1,      // ms before auto-advance; -1 disables
    defaultAction: 'next', // or 'prev', 'home', 'end'
    barSelector: undefined, // element to use as a progress bar
    barProperty: 'width',
    barPropertyStart: '0',
    barPropertyEnd: '100%'
  },
  presentationMode: {
    use: true,
    url: 'presentation-screen.html', // popup page (must be served from your site)
    notesUrl: false,
    transferredValues: ['userZoom', 'userTranslateX', 'userTranslateY']
  }
});

Templates

Templates position steps from JavaScript instead of repeating data attributes. Register a named template, then reference it with data-template, or apply it directly:

// from the demo page: rows of steps laid out programmatically
$.jmpress('template', 'myrow', {
  x: 100, y: 100, scale: 2,
  children: [
    { x: 0, y: 150, scale: 0.2 },
    { x: 0, y: 450, scale: 0.3 }
  ]
});

// or a function of the child index
$.jmpress('template', 'myfunc', {
  x: 100, y: 100, scale: 2,
  children: function(i) { return { x: i * 200 }; }
});
<div id="deck" data-template="myrow">
  <div class="step">...</div>
  <div class="step">...</div>
</div>

You can also apply after the fact: $('#deck').jmpress('apply', '#deck .step', 'myrow'). Templates merge: a step’s own data-* attributes win over template values.

Ways: custom navigation order

By default next/prev follow DOM order (with wrapping via the circular component). Override per step:

<div class="step" id="a" data-next="#c" data-prev="#b">...</div>

Or set a whole route in code (bidirectional by default; pass true as the second argument for one-way):

$('#deck').jmpress('route', ['#intro', '#demo', '#outro']);

Sub-step animations

Inside a step, animate elements as you advance. Add data-jmpress to a child element with an animation name and an optional delay:

<div class="step" data-x="1000">
  <p>Always visible</p>
  <p data-jmpress="fade">fades in on the next advance</p>
  <p data-jmpress="drive-right after 500ms">drives in 500ms later</p>
</div>

The plugin cycles three classes per animation: will-<name> (before), do-<name> (during), has-<name> (after). Built-in names from basic-animations.css: appear, drive-up, drive-down, drive-left, drive-right, expand, fade, fade-fast, fade-slow, warp-up, warp-down, warp-left, warp-right, zoom. Delays accept ms, s, or m units (plain numbers are milliseconds). The attribute name is configurable via the customAnimationDataAttribute setting (default jmpress).

AJAX-loaded steps

Give a step an href or data-src; its content loads via jQuery .load() when the step becomes active or sits next to the active step, and the step gets the loaded class:

<div class="step" data-src="slides/chapter2.html"></div>

6. Plugins

Plugins are separate scripts included after jmpress.js. Four ship with the repo (all bundled in dist/jmpress.all.js).

duration: auto-advance

Advances steps automatically after a delay, with an optional progress bar.

<div class="step" data-duration="5000" data-duration-action="next">...</div>
  • data-duration: milliseconds on this step before advancing.
  • data-duration-action: next (default), prev, home, or end.
  • Global defaults under the duration setting: defaultValue (-1 disables), defaultAction, and barSelector/barProperty for a progress bar element.

presentation-mode: presenter screen

Press p during a presentation to open a popup window (presentation-screen.html, served from your site) that mirrors the deck: it shows the current slide, follows navigation both ways, and carries the user’s zoom/pan state over via postMessage. Configure with the presentationMode setting (use, url, notesUrl, transferredValues).

secondary: swap animations on selection

When a step becomes active, exchange its (or its relatives’) properties with their secondary counterparts. Use data-secondary="self" (this step), "siblings", or "grandchildren", plus data-secondary-<prop> values:

<div id="deck">
  <div class="step" data-jmpress="fade"
       data-secondary="self" data-secondary-x="2000">...</div>
</div>

Here the step fades normally, but while it is the active step its x position swaps to 2000, then swaps back when it becomes inactive.

toggle: bind a key to init/deinit

Binds a keydown handler that initializes jmpress on first press and de-initializes on the next (useful for an “edit mode” / “present mode” switch):

$('#deck').jmpress('toggle', 84 /* t */, { keyboard: { use: true } }, true);
// third argument true = initialize immediately

7. Writing a plugin

A plugin is an IIFE in the same style as the components. It gets the full public interface:

(function($, document, window, undefined) {
  'use strict';

  // 1. Add default settings (optional)
  $.jmpress('defaults').myPlugin = { enabled: true };

  // 2. Hook into the callback pipeline
  $.jmpress('setActive', function(step, eventData) {
    var settings = eventData.settings.myPlugin;
    // eventData also carries: jmpress (the element), data (raw data-*
    // attributes), stepData (parsed), current (internal state), parents
  });

  // 3. Add new methods
  $.jmpress('register', 'myMethod', function(arg) {
    return $(this).jmpress('active');
  });

  // 4. Add new callbacks other plugins can hook
  $.jmpress('register', 'myPlugin:didSomething');

}(jQuery, document, window));

To call your method: $('#deck').jmpress('myMethod', arg). To let others fire your callback: $('#deck').jmpress('fire', 'myPlugin:didSomething', step, eventData). Registering an existing method name throws a jQuery error, so namespaced callback names (plugin:event) are the convention (see ajax.js: ajax:loadStep, ajax:afterStepLoaded).


8. Troubleshooting

1. Nothing animates after installing from npm. The npm-published package has lagged behind the repository; transition fixes present in the repo were missing from the published build (reported upstream). Build dist/ from the repository with grunt, or confirm the published version includes the fixes before relying on it.

2. Syntax error, unrecognized expression: a[href^=#] You are on jQuery 2.2.1 (or newer). The hash component queries links with the selector a[href^='#'], which newer jQuery rejects. Use jQuery 2.1.4 (the version the test suite runs against) or patch the hashLink selector in src/components/hash.js.

3. Slides skip from one to the next with no transition (Firefox), or the tab crashes (Chrome). A known rendering bug with a known fix in the repository history. Make sure your jmpress.js is built from current master, not an old npm copy.

4. Method X does not exist on jQuery.jmpress. Either the element was never initialized ($('#x').jmpress() first), the method name is misspelled, or the method belongs to a component/plugin you did not include (e.g. route, zoomIn, template need the full build, not jmpress.impress.js; toggle needs its plugin script).

5. Demo and docs links in the README 404. The README links use the retired domain jmpressjs.github.com. Use jmpressjs.github.io instead: the demo is at https://jmpressjs.github.io/jmpress.js/ and the legacy docs at https://jmpressjs.github.io/docs/.


9. Contributing

  • Issues and pull requests go to github.com/jmpressjs/jmpress.js. The maintainers welcome PRs and are looking for contributors.
  • Code style is checked with jshint (config in .jshintrc); run it via grunt jshint or the default grunt task before submitting.
  • Tests: npm test runs the grunt qunit task over test/*.html with the QUnit suites in test/components/. New behavior should come with a test.
  • The build is grunt-based (Gruntfile.js): clean, jshint, concat (fixed component order), uglify, cssmin. Do not hand-edit dist/; it is generated.

10. License

MIT License. Copyright 2013 Kyle Robinson Young and Tobias Koppers. See LICENSE in the repository.

Acceptance checklist: 7/7 PASS

The paid-tier acceptance checklist from the repo docs overhaul service, applied exactly as a paying client would run it. Verdict: 7/7 PASS.

  • README answers in order: what it is, how to install, how to run the quickstart, where the full docs are. PASS. DELIVERABLE.md sections 1 (what it is), 3 (setup guide with three install options), 4 (5-minute quickstart), and the table of contents (where the full docs are) follow that order. The README rewrite in section 1-4 doubles as the repo’s front door.
  • Install instructions were tested on a clean checkout at the pinned version. PASS with a documented limitation. The checkout was fresh-cloned from GitHub at the pinned commit (no dist/, no node_modules). npm package name jmpress.js and version 0.4.5 were read from package.json; the files: ["dist/jmpress.js"] field was read from package.json; the grunt build producing dist/ was read from Gruntfile.js. The grunt 0.4-era toolchain was NOT executed (it predates the available Node 24 runtime), and the deliverable says so instead of claiming a successful build.
  • Every code snippet in the README and docs runs without modification. PASS with an honest scope note. The JavaScript API snippets were syntax-checked with node --check (all valid) and every method name, argument shape, and default value in them was cross-checked against the source at the pinned commit (see VERIFY.md). The snippets are browser/DOM snippets (they require jQuery plus a page), so they were not executed end-to-end in this environment; no headless-browser claim is made. No snippet contradicts the code.
  • No documented flag, endpoint, or option contradicts the code. PASS. All 20+ data attributes, the full settings tree, keyboard key codes, and method signatures were transcribed from the source files (see VERIFY.md for the file-by-file mapping). Two transcription errors were caught during review (reselect does not take a step argument; data-r/data-phi are polar coordinates, not rotation) and corrected.
  • Troubleshooting page covers the 5 most likely failure modes for a new user. PASS. Five items, three of them grounded in the repo’s own issue tracker (npm build out of sync #187, jQuery 2.2.1 selector error #185, Firefox transition skipping #157/#154), one in method-dispatch code (Method X does not exist), one in the README itself (dead jmpressjs.github.com links).
  • Headings, code fences, and links render correctly (no broken anchors). PASS. All internal anchors verified against the heading list; all fenced blocks closed; external links verified live on 2026-09-16 (github.com/jmpressjs/jmpress.js, jmpressjs.github.io pages, npm package name from package.json).
  • No lorem ipsum, no TODOs, no “coming soon” sections left in the docs. PASS. Full-text scan of DELIVERABLE.md: zero matches for lorem/TODO/ coming soon/TBD/XXX placeholders.

Second-model review: the deliverable was cross-checked claim-by-claim against the pinned source (VERIFY.md) instead of a separate model pass; every corrected error is recorded above and in VERIFY.md.


Want this for your repo?

Start with the free scoping review

Every engagement starts free: a one-page findings document on your repo’s docs within 48 hours, with your exact tier from the file count. You pay only if the acceptance checklist above passes on delivery. Read the repo docs overhaul service page for the full spec.