Artboards

The pages of your site you choose, side by side on one board, the real site in each frame. Make a site editable with the bridge, then pan, zoom, click a page to edit it with its form or by prompt, and save one page or all of them.

13 min read

Artboards are off until you turn them on under Account → Preferences on www.obelism.studio. Then Artboards (in the sidebar) shows the pages on the environment's board: your site, rendered at the environment's preview URL (Settings) in a frame per page, with the page's path appended (with {locale} in the preview URL, like https://example.com/{locale}, the locale chosen at the top goes there and the path is the one in that locale). A product page opens at the environment's product path instead (/products/<handle> by default).

Each page type is a column, with the home page's first. A column with more than six pages shows five of them and a sixth frame with a page switcher over it. Edit on page on an entry's page opens just that page (/artboards?entry=<id>), when artboards are on: the breadcrumb above it goes back to Everything or to every page of its type, and Show entire board fits the whole board.

Choosing pages

The board starts small: the home page and the first page of each type. Pages in the toolbar lists every page of the environment by type, with the ones on the board ticked. Tick another to add it, untick one to take it off; Show all pages fills the board, Remove all empties it, and Back to the starter board returns to the first setup. The pages save as you change them, and everyone in the Space sees the same board.

A page that is not on the board can still be opened: Edit on page and /artboards?entry=<id> show it for that visit.

Making a site editable

  1. Include the bridge on every page. With the React SDK that is <ContentBridge /> in the root layout; with a bundler, getContentBridge() from @obelism/content/bridge; without one, a script tag:

    <script src="https://content.obelism.studio/bridge.js"></script>

    It does nothing outside the artboards. Inside it, it only talks to the CMS origin it was loaded from. The script tag puts it on window.ObelismBridge. Options go on the tag: data-prompt="false" leaves the prompt box of editing on your site out, and data-url names the CMS when the file comes from an npm CDN.

  2. Mark blocks with their uid, which every block in a delivery response has:

    <section {...editable(block)}>…</section>
    // or, without the SDK:
    <section data-cms-uid={block._uid}>…</section>
  3. Show drafts in preview. Every frame's URL carries cms_preview=1, cms_entry=<id>, cms_environment=<name> and cms_token: a token made for the person editing, each time the artboards open. Read the page with cms_token as the bearer token, on your server, naming your space and environment (space and environment on ContentClient, the Obelism-Space and Obelism-Environment headers otherwise), and the delivery API answers drafts. Without them, a cms_token someone made for their own space would put that space's drafts on your site. It only reads, acts as that person (with their role at the time) and expires after 8 hours. A missing, made-up or expired token gets a 401: show the published page then. Previews and draft mode has the code, and the rules that keep drafts off your public pages.

    Watch out

    Decide on drafts by cms_token, never by cms_preview alone with a preview token of your own: anyone can add cms_preview=1 to a URL.

  4. Re-render on live updates (optional, recommended): useLiveEntry(entry) in React, or

    ObelismBridge.on('update', ({ entry }) => render(entry))

    entry has exactly the shape of a delivery API entry for a preview token, built from the unsaved draft. Without an update listener the page reloads after every save instead.

  5. Allow framing by the CMS: if your site sends Content-Security-Policy: frame-ancestors, include https://content.obelism.studio, and drop X-Frame-Options: DENY for preview requests. The preview URL has to be reachable by the editor's browser too: a private host — Vercel deployment protection, Cloudflare Access, a private Oxygen environment — shows its login page in the frame instead of your site (Shopify Oxygen).

Products

With a Shopify store connected to the space, the artboards have a Products and a Collections column after the others. Products holds the product pages of every product page type and ends with Any product: your site's product route (the environment's product path, /products/<handle> by default) for a product of the store, the store's first to begin with. Select it and Choose a product to see another. Collections does the same for collection pages and Any collection, at the collection path (/collections/<handle> by default).

For a product (and a collection the same way):

  • A product with a page: Show its page selects that page's frame, where you edit it like any other page. Choosing a product that has one goes straight there.
  • A product without one: Add a product page makes a draft page of the product page type for it, with the product filled in and its handle as slug, and opens it. The space needs a product page type for this (Settings → Shopify).

Products without a page are shown as visitors see them; there is nothing to edit on them in Content.

Moving around

The board fills the page beside the dashboard's navigation. At the top left are the environment, the page breadcrumb, Pages (which pages the board shows), a page type filter (All page types, or one), Go to page (any page of the board), New page, and Save all and Publish all while pages have changes. At the top right are the board's controls, and a pointer button whose tip sums them up:

  • Scroll or drag to move the board, ⌘ or Ctrl and scroll (or pinch on a trackpad) to zoom, or − and +. Fit shows the whole board. On the artboards, pinching zooms the board, not the browser.
  • Desktop and Mobile render every frame 1440 or 390 pixels wide, so your site's own breakpoints apply. With more than one locale, the locale select shows every page in that one.
  • Only the frames near what you're looking at load your site, at most eight at a time, each marked Rendering until it has loaded. The last dozen you've seen keep theirs when you move on; the others show an outline of a page until you move to them. A frame outside the visible area stops drawing until you bring it back (in Chromium, its animation frames stop too), so a board of open frames doesn't keep painting in the background; its document, scripts and unsaved changes stay as they were.

Editing a page

Click a page to select it. Its whole form opens in a panel over the right of the board, the same fields and blocks as on the entry page, with Save draft and Publish at its end and whether the draft has changes beside them. Clicks inside the panel now reach your site: clicking a block on the page, or a row in the form, opens that block's row and scrolls it into view, and the row is outlined. Hold Space to move the board over the selected page, and press Esc or the panel's close button to clear the selection.

Shift-click (or ⌘/Ctrl-click) adds pages to the selection or takes them out, and ⌘A (Ctrl A) selects every page of the last clicked page's column. The prompt bar shows what it will change: the selected pages as chips, which you can take out one by one or with Clear selection. A store column's last frame (Any product, Any collection) has no page of its own, so the bar asks you to select a page while it's selected.

  • Edits show on the page as you type, before they are saved.
  • The prompt bar acts on the selected page, or the selected block (a chip of its own): type what to change and it changes it by prompt. The change shows on the page at once, unsaved, and Suggested by Obelism above the bar says what changed: Keep it, Try another (undo it and ask again) or Undo, with the first text it changed before and after. Until you decide, the changed pages carry a Preview chip.
  • With several pages selected, each gets the same prompt, one after another; the panel lists them as they land.
  • New page asks for a page type, a path and what the page should say, and the model writes it as a draft of that type (with no page selected, the board's first gives an example). The page goes on the board and opens selected (save or undo other changes first).
  • Every page keeps its unsaved changes while you edit others; a page with changes is marked Unsaved. The panel's Save draft saves the selected page and Publish saves and publishes it; Save all and Publish all do the same for every changed page, each with its own check that nobody saved it in the meantime. A page that can't be saved or published is named. Closing the tab with unsaved changes asks first.
  • Where publishing is reviewed and you may not publish, Publish and Publish all ask for review instead.

Adding blocks

The page's form lists its blocks as rows, with Add block under them. A row's chevron opens its fields; Add block offers the types the blocks field allows (with more than one blocks field, each has its own list). A full field, or one that allows no type, says so.

Shift- or ⌘/Ctrl-click rows to pick several: a bar above the list moves them up or down together, or copies them, and dragging any picked row's grip moves the group.

Reusing a block on another page

Open a block's row and choose Copy block in its ⋯ menu — or pick several rows and use Copy in the bar above them. Select another page: every blocks field shows Paste block beside Add block, so paste it into the field it belongs in. The copies go at the end of that list with new ids throughout, so the same blocks can be pasted more than once, and the first opens. A block type the field doesn't allow, or a field without room for all of them, is refused.

The copy stays in the browser tab until you copy another block or close it. References and files in it are the same Space's, so they keep working on the new page.

Variants side by side

A page with an A/B test or flag block lists its tests and flags under Variants in the panel. Side by side adds a column after the others with a frame per option of that test or flag, each showing the page as it is with that option's content, while you edit the page in its own frame. Hide takes the column away again.

Each variant frame gets the page's unsaved content with every block of that test or flag holding only its option's blocks, so whatever option your site picks for the visitor, it can only render that one. This needs a site that re-renders on live updates (useLiveEntry, or ObelismBridge.on('update', …)); a site that only reloads after saving shows its own pick in every frame.

Changing the whole site

With no page selected, the prompt bar takes a request about the whole site, several pages at once: "make every hero shorter", "end each journal post with a call to action". It works in two steps:

  1. A plan: the model reads an outline of the pages on the board (their names, paths, types and the start of each block) and answers which of them the request is about, and what to ask about each, at most 12.
  2. Every step then runs as that page's own prompt, one after another, with the same checks as a prompt on one page.

The panel lists the steps as they land; click one to go to its page. The changes are unsaved, marked on their frames: look them over, Undo them all from the suggestion above the bar, and Save all or Publish all when they're right. A request reaches the pages on the board, so in a column with a switcher only the page it shows.

Whole pages

A frame is as tall as its page. The bridge tells the artboards the page's height, and the artboards tell the bridge the screen height the frame stands for (900 pixels on desktop, 844 on mobile). The bridge then rewrites vh, svh, lvh and dvh sizes in your stylesheets as pixels of that height, so a 100vh hero stays one screen high instead of growing with the frame. Stylesheets from another origin can't be read and keep their units. This needs @obelism/content 0.20 or later (or Content's own /bridge.js); with an older bridge, frames are one screen high and scroll inside.

Click rates

With Show click rates on the page in the panel, the selected page shows how often each of its blocks is clicked, as a label on each block. The switch shows once the page has click rates. The switch is remembered in your browser. The numbers come from Obelism numbers, the events service every Obelism product shares:

  1. Load numbers' script on every page of the site (its Setup page has the tag for your Organization and environment).

  2. Tag a block's clickable element with its uid, in production too:

    <a href={linkHref(block.link) ?? undefined} data-numbers-id={block._uid}>
    	{block.label}
    </a>

    The script sends a click event with that uid for every click on (or inside) the element.

A block's click rate is the share of the visitors who viewed a page it was clicked on who clicked it, over the last 30 days, in the environment you're editing. Blocks nobody clicked show nothing.

When a frame shows published content

A frame that shows the published page, or no page, instead of the draft almost always comes from the 401 fallback in step 3 of making a site editable. Log the response's body before falling back (error.body on the SDK's ContentApiError): it says which of these it is.

  • The entry was never published. The fallback has nothing to show, so the frame shows the page as if it had no entry: on a product route, the product without its blocks.

  • cms_token doesn't reach the client, and the error is Invalid or revoked token. Check what your server actually reads, especially after a rewrite or a redirect. A Next.js rewrite that captures the query with has and a bare key names the value by its letters only (cms_token is :cmstoken in the destination). Name it with a group instead:

    next.config.ts
    {
    	source: '/products/:handle',
    	has: [{ type: 'query', key: 'cms_token', value: '(?<cmsToken>.+)' }],
    	destination: '/preview/products/:handle?cms_token=:cmsToken',
    }
  • The space or environment doesn't match (token_place_mismatch). The token is for the environment you're editing, and the site names its own (CONTENT_SPACE_ID, CONTENT_ENVIRONMENT). Point each environment's preview URL at a deployment configured for that environment. With a connected deployment, that is the host environment whose rule leads there, such as a preview deployment for staging.

Bridge reference

window.ObelismBridge, or what getContentBridge() returns:

  • isEditing: true inside the artboards.
  • entryId: the entry being edited.
  • on(event, listener): update ({ entry }), saved, published, and change for the prompt state.
  • prompt(text), undo(), createPage(path, prompt): when editing on your site; see prompt editing.

The bridge and the artboards talk over postMessage. The site sends { source: 'obelism-content-bridge', type } with ready (with the page's height), size ({ height }, whenever it changes) and select; the artboards send { source: 'obelism-content-editor', type } with connect, viewport ({ height }, the screen height the frame stands for), update, select, saved, published or stats. stats carries the blocks' click rates and the switch, { stats: { [uid]: { clicks, clickers, viewers, rate } }, visible }, which the bridge shows on the page next to each data-cms-uid while visible is true (@obelism/content 0.7 and up; see the SDK's README, "Click rates"). Both sides check the other's origin.

Editors can also work on the site itself, outside the dashboard: see editing on your site.

On this page

Marks in the margin

  • Note
  • Watch out
  • Updated

Scribes marked doubtful lines in the margin. That practice is called obelism.

Stuck on a step?