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.
| fact | value |
|---|---|
| Stars | 1,485 (read from the GitHub API, 2026-09-16) |
| Last activity | 2026-05-25 (commit 18fb7c5, master) |
| Version documented | 0.4.5 |
| License | MIT |
| Billable files | 70 (76 on master, minus 6 vendored: bundled jQuery/QUnit test libs and .min.js) |
| Engagement tier | M: $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
- What it is
- Architecture overview
- Setup guide
- Quickstart (5 minutes)
- Usage and API reference
- Plugins
- Writing a plugin
- Troubleshooting
- Contributing
- 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:
| Component | Responsibility |
|---|---|
core.js | init/deinit, method dispatch, step selection, callback registry, prefixed CSS helper |
near.js | $(el).near(selector, backwards) jQuery helper: nearest step in DOM order |
transform.js | parses data-x/y/z/r/phi/rotate*/scale* into stepData; applies CSS transforms |
active.js | adds/removes active / nested-active classes on the step and its parents |
circular.js | wraps navigation from last step back to first; data-exclude skips a step |
start.js | chooses the initial step (from the start setting) |
ways.js | custom navigation order via data-next / data-prev and the route method |
ajax.js | lazy-loads step content from href / data-src with jQuery .load() |
hash.js | syncs the active step with the URL hash (#/step-id); a[href^="#"] links select steps |
keyboard.js | arrow / space / pgup / pgdn / home / end / tab key bindings, with form-field ignore rules |
viewport.js | scales the canvas to a viewport, user zoom (+/-, wheel, drag) |
mouse.js | clicking a step selects it (unless user-zoomed) |
mobile.js | swipe left/right on touch devices goes prev/next (50px threshold) |
templates.js | named templates that position groups of steps programmatically |
jqevents.js | fires enterStep / leaveStep jQuery events (non-bubbling) on steps |
animation.js | sub-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 witha[href^='#'](see Troubleshooting). - A browser with CSS3 transforms and transitions. Unsupported browsers are detected and get the
not-supportedclass 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.jsThe 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, cssminThis 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
| File | Contents | Use when |
|---|---|---|
dist/jmpress.js | core + all 16 components | standard use |
dist/jmpress.impress.js | core, near, transform, active, circular, hash, keyboard, mouse | migrating from impress.js, or minimal footprint |
dist/jmpress.all.js | everything + all four plugins | you need duration, presentation-mode, secondary, toggle |
dist/plugins/jmpress.<name>.js | one plugin at a time | you need a single plugin with the standard build |
dist/basic-animations.css | appear, drive, expand, fade, warp, zoom animation classes | you 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):
| Attribute | Default | Meaning |
|---|---|---|
data-x, data-y, data-z | 0 | position in pixels on each axis |
data-r | 0 | polar radius in pixels (used with data-phi; x falls back to r·sin(phi), y to −r·cos(phi)) |
data-phi | 0 | polar angle in degrees, used with data-r |
data-rotate | 0 | rotation in degrees |
data-rotate-x, data-rotate-y, data-rotate-z | 0 | per-axis rotation |
data-scale | 1 | uniform scale |
data-scale-x, data-scale-y | off | per-axis scale |
data-scale-z | 1 | z scale |
data-view-port-width, data-view-port-height | global | per-step viewport size override |
data-view-port-min-scale, data-view-port-max-scale | global | per-step zoom limits |
data-view-port-zoomable | global | per-step zoom steps |
data-template | none | space-separated template names (see Templates) |
data-next, data-prev | DOM order | selectors overriding next/previous navigation (see Ways) |
data-src (or href) | none | URL to lazy-load step content via AJAX |
data-exclude | off | any value except false/no excludes the step from circular navigation |
data-jmpress | none | sub-step animation, e.g. fade or fade after 500ms |
data-duration, data-duration-action | global | auto-advance delay in ms and action (duration plugin) |
data-secondary, data-secondary-* | none | secondary animation swap rules (secondary plugin) |
Navigation methods
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 initializedRegistered 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/deinitStatic 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 namePer-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, orend.- Global defaults under the
durationsetting:defaultValue(-1disables),defaultAction, andbarSelector/barPropertyfor 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 immediately7. 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 viagrunt jshintor the defaultgrunttask before submitting. - Tests:
npm testruns the grunt qunit task overtest/*.htmlwith the QUnit suites intest/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-editdist/; 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.jsand version0.4.5were read from package.json; thefiles: ["dist/jmpress.js"]field was read from package.json; the grunt build producingdist/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 (
reselectdoes not take a step argument;data-r/data-phiare 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.