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.
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
| Option | Type | Default | What it does |
|---|---|---|---|
container | selector | id | HTMLElement | document.body | Where the chrome is built. |
header_space | boolean | number | 57 (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
| Channel | URL |
|---|---|
| Latest stable | https://cdn.typograph.nl/editor/latest/editor.js |
| Minified | https://cdn.typograph.nl/editor/latest/editor.min.js |
| Colour engine | https://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(...):
| Event | When it fires | Handler receives |
|---|---|---|
typograph_init_ready | Canvas is up and window.typograph is populated. | — |
typograph_editor_ready | The editor has finished setting up. Register your callbacks here. | — |
typograph_document_opened | A document has been opened. | — |
typograph_user_fonts_loaded | User-provided fonts (from the manifest) finished loading. | — |
typograph_pageelement_selected | The 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:
- List user-owned documents / templates / clips — return URLs pointing at JSON payloads the editor can fetch.
- Persist save operations — accept
{ filename, data }payloads and store thedata(a serialized template JSON string). - Host images, fonts and swatches from the manifest at the
*_urlfields 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.
Related
- Editor API Reference — generated TypeDoc for the
editormodule - Embedding — the authoritative embedding contract for both the editor and the viewer
- EditorUI API — show, hide and relabel individual chrome controls
- Menu API — adding and removing menu items
- Find and Replace —
find()/replace_all() - Page Preview — host-side thumbnails
- Viewer Integration — the read-only template-filling counterpart, same bundle
- Form Integration — letting an end user fill a template in through a form
- Templates Integration — how template URLs are sourced for the list callbacks
- Documents concept — the schema the editor reads and writes