Skip to main content

Editor Integration

The Typograph Editor is a full browser-based document editor, delivered as a CDN-hosted script. It builds its own chrome — menu bar, toolbar, content bar, parameter bar — and exposes a window.typograph global for wiring up loading, saving, and the image/font manifest.

There is no npm package — include the script from the CDN.

Quick Start

Two scripts, and no markup to author.

<!DOCTYPE html>
<html translate="no">
<head>
<meta charset="utf-8">
<title>Typograph Editor</title>
<script src="https://cdn.typograph.nl/editor/latest/scripts/jsColorEngineWeb.js"></script>
</head>
<body>
<div id="app" style="position:absolute; top:0; left:0; right:0; bottom:0;"></div>

<script type="module">
import { Editor } from 'https://cdn.typograph.nl/editor/latest/editor.js';
const editor = new Editor({ container: '#app' });
</script>
</body>
</html>

The colour engine (jsColorEngineWeb.js) is a bare-global script and stays your responsibility. Everything else — the chrome, the stylesheet, the webfont — the bundle brings itself.

Editor is also exposed as window.Typograph.Editor if you would rather not use a module script.

Upgrading from an older integration

Earlier versions of these docs required the page to reproduce ~31 specific element ids (wrapper, dialoglayer, dialoglayer_01, popupsliderlayer, htmllayer, menulayer, …). That is no longer necessary. The bundle builds its own DOM.

Existing pages keep working: the bundle detects that markup and leaves it alone, and a page that links its own stylesheets/style.css or Source Sans Pro keeps using its own copy — including a customised stylesheet. Nothing needs to change on your side, but new integrations should not author that markup.

Full-page vs. contained

Load the two scripts and construct nothing, and the editor mounts itself on document.body, filling the viewport. Every existing host relies on this, so it is not going away.

Construct an Editor with a container and the chrome is built inside that box instead of taking over the page.

container

A CSS selector, a bare element id, or an HTMLElement — all three work:

new Editor({ container: '#app' });                          // selector
new Editor({ container: 'app' }); // bare id
new Editor({ container: document.getElementById('app') }); // element

If nothing matches, a warning is logged and the editor mounts on document.body rather than failing silently.

The container must have a height

This is the one requirement. The editor's layout is absolutely positioned and resolves against the container's box, so a container with no height gives you an editor with no height. A plain <div id="app"></div> collapses to zero.

#app { position: absolute; top: 44px; left: 24px; right: 24px; bottom: 24px; }
#app { height: 90vh; }
/* Inside a flex or grid parent that gives it a track. */
#app { min-height: 0; }

An unpositioned container is given position: relative, and one with visible overflow is given overflow: hidden so nothing escapes the box you allotted. Both are restored on destroy().

Everything stays inside the container

Menus, dialogs, colour pickers and the floating tool palettes are all confined to the box you gave the editor — none of them can be drawn over the rest of your page. A dialog that would be centred on screen in the full-page editor is centred in your container instead.

The one consequence worth knowing: a container much smaller than the editor's natural layout clips its own overlays rather than letting them escape. The chrome assumes roughly the room a desktop application gets.

Construct it early

Construct the Editor before the window load event. Later than that and the editor has already mounted itself on document.body under the full-page defaults; container is then ignored and a warning says so. A module script in the page body is early enough.

Options

OptionTypeDefaultWhat it does
containerselector | id | HTMLElementdocument.bodyWhere the chrome is built.
header_spaceboolean | number57 (px)The empty strip above the menu bar, for a host's logo.

The editor reserves 57px above the menu bar for a host logo. If your application already has its own header, you will not want it:

new Editor({ container: '#app', header_space: false });   // remove it
new Editor({ container: '#app', header_space: 80 }); // or set your own height

Everything below moves with it. Omit the option and the layout is exactly what it has always been.

Script URL

ChannelURL
Latest stablehttps://cdn.typograph.nl/editor/latest/editor.js
Minifiedhttps://cdn.typograph.nl/editor/latest/editor.min.js
Colour enginehttps://cdn.typograph.nl/editor/latest/scripts/jsColorEngineWeb.js

Load editor.js as a module (<script type="module" src="...">).

Lifecycle

const editor = new Editor({ container: '#app' });

editor.get_document(); // the canvas document, once booted
editor.destroy(); // remove the editor's DOM and styles

Re-mounting after destroy() is not supported — reload the page instead. Constructing a second Editor destroys the first; two editors side by side is not supported.

Lifecycle Events

The editor emits document events you listen to with document.addEventListener(...):

EventWhen it firesHandler receives
typograph_init_readyCanvas is up and window.typograph is populated.
typograph_editor_readyThe editor has finished setting up. Register your callbacks here.
typograph_document_openedA document has been opened.
typograph_user_fonts_loadedUser-provided fonts (from the manifest) finished loading.
typograph_pageelement_selectedThe user selected a page element.CustomEvent with detail.uuid and detail.message

See Document Events and Element Events for the full set.

Callback Registration

Inside your typograph_editor_ready handler, retrieve the canvas document and plug in your callbacks.

document.addEventListener('typograph_editor_ready', set_callbacks);

function set_callbacks() {
// Either the instance method or the global — both return the same document.
const typograph_document = window.typograph.get_typograph_document();

// How to list documents, templates, and clips the user can open.
typograph_document.set_documents_callback(get_documents);
typograph_document.set_templates_callback(get_templates);
typograph_document.set_clips_callback(get_clips);

// How to persist edits.
typograph_document.set_save_document_callback(save_document);
typograph_document.set_save_template_callback(save_template);
typograph_document.set_save_clip_callback(save_clip);
}

List callbacks — provide options in menus

// Return a JSON-stringified array of URLs pointing to the user's saved documents.
function get_documents() {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/list-documents', false);
xhr.send();
return xhr.response; // already JSON
}

Save callbacks — persist edits

// Called with { name, data } where `data` is a serialized template JSON string.
function save_template({ name, data }) {
const form = new FormData();
form.append('filename', name);
form.append('data', data);

const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/save-template', false);
xhr.send(form);
}

Manifest — images, fonts and swatches

set_manifest_data is async — await it before assuming the assets are in place.

await typograph_document.set_manifest_data({
images: [
{
id: '019b28fb-a11e-7641-a28f-e978f892ec06',
name: 'hero',
size: { w: 5213, h: 4160 },
original_url: 'https://cdn.example.com/hero/original.jpg',
editor_url: 'https://cdn.example.com/hero/editor.webp',
print_url: 'https://cdn.example.com/hero/print.jpg',
thumbnail_url: 'https://cdn.example.com/hero/thumb.webp',
},
],
fonts: [/* { id, name, url } — Open Sans / Source Sans Pro are built in */],
swatches: [/* named colours available in the editor */],
});

preserve_geometry (default true) keeps each placed image's authored size and scale when the manifest changes. Pass false to reflow images to the new asset's natural size:

await typograph_document.set_manifest_data(data, false);

To fetch a manifest from a URL instead, use set_manifest_url(url, replace = true, preserve_geometry = true):

await typograph_document.set_manifest_url('https://example.com/manifest.json');

Pass replace: false to merge into the existing manifest rather than clearing it first.

Opening a Document Programmatically

window.typograph.get_typograph_document().open_document_url(url);

Backend Glue

The editor expects your backend to:

  1. List user-owned documents / templates / clips — return URLs pointing at JSON payloads the editor can fetch.
  2. Persist save operations — accept { filename, data } payloads and store the data (a serialized template JSON string).
  3. Host images, fonts and swatches from the manifest at the *_url fields you supply.

If you want those backing URLs to be Typograph-hosted, wire your backend to the Typograph File Service and the presigned-URL upload flow. Before publishing a document, run it through POST /v1/document/validate to catch schema issues early.