Skip to content

Shop themes and load time

The designer is dropped into a page it does not control: a shop’s theme, on whatever phone the customer holds. This page says what it does about the one, and what it costs on the other.

The designer draws into your page, not into a shadow DOM, and your theme’s global rules for input, button, label and select would reach its form: a margin under every input, uppercase buttons, padded fields. It keeps them out. It adds one stylesheet that takes every style it does not set itself back to the browser’s own, for the elements it draws only, and outweighs a theme’s selectors, ids included. The form looks the same under Storefront, Astra, Flatsome or Divi as on a page without a theme, on a desktop and on a phone.

Two things still reach it. A theme rule marked !important wins over any style the designer sets, save one: what the designer hides stays hidden, even under a theme that sets display on every button. And the text it does not give a font of its own (the labels, the help) takes your page’s font and colour, as any text on the page does. To restyle the form, use the accent option and the --pixonaut-* properties; past those, headless mode draws no form at all.

Its icons are sized and painted by their own styles, so a theme’s svg { fill: currentColor } or button svg { width: 1em } leaves them as they are.

The elements of yours that the designer shows inside its form, such as your own option fields in an Options step, keep your styles, and the box sizing they had on your page: a theme that sets box-sizing once and has every element inherit it still sizes them the same way.

In a browser without adopted stylesheets (Safari before 16.4) the designer adds no stylesheet, and a theme’s rules reach the form as they would without it.

The designer is measured on the slowest common case in Europe: a budget Android phone (Galaxy A16 class) on weak 4G, modelled as a CPU four times slower than a desktop’s and a 4G network of about 9 Mbps down with a 170 ms round trip. Nothing is cached, and the script and the renderer are the release’s files, compressed with brotli as the CDN serves them. Its first preview takes about 2.9 seconds from a cold start. Its parts:

Part Over the wire Done after
The script (pixonaut.iife.js) 320 KB 0.6 s
The api’s calls (the template, its options) 8 KB 1.1 s
The renderer (pixonaut_wasm.js and its wasm) 1.44 MB 2.3 s
The template’s fonts, each cut to the characters set in it 26 KB 2.7 s
The first draw, after the last of them arrived 0.2 s more

On Lighthouse’s mobile profile, whose Slow 4G is a 1.6 Mbps network with a 150 ms round trip, the same load takes about 12 seconds, most of it the renderer’s download.

The renderer is asked for while the script is still being run, before your page has mounted anything, so it downloads beside the api’s calls rather than after them, and it is compiled as it streams in rather than once the whole of it has arrived. A browser that cannot compile a download as it streams compiles it once it is complete. Nothing of it runs before it has been checked against the hashes of the release. The fonts follow it, since the renderer works out which faces the template’s text is set in: each face comes cut down to the characters its text uses, and the font picker’s samples to their “Aa”. The whole face of a text the customer can type is fetched once the page is idle, or as soon as they type, whichever is first; until it has arrived, a character outside the cut waits for it rather than being drawn in another font. A text the customer cannot change never needs more than its cut. A font file that fails to arrive is fetched again a second later, and once more four seconds after that, before the preview gives up on it. A returning customer has the script and the renderer cached.

The renderer is started early only when the script is served under its own name (pixonaut.iife.js, pixonaut.esm.js or pixonaut-core.js), from Pixonaut’s CDN or a copy of yours, since the renderer then sits beside it. A bundle your build tool made, or a copy you renamed, fetches it when you mount, from wasmUrl or beside the script, as before. A page that loads the script fetches the renderer whether or not it mounts the designer.

You need no markup for any of this. To have the renderer start downloading with the page rather than once the script runs, add two preloads beside the script’s tag, at the same version and address:

<link
rel="preload"
href="https://cdn.pixonaut.app/sdk/<version>/pixonaut_wasm.js"
as="fetch"
crossorigin="anonymous"
/>
<link
rel="preload"
href="https://cdn.pixonaut.app/sdk/<version>/pixonaut_wasm_bg.wasm"
as="fetch"
crossorigin="anonymous"
/>

The SDK fetches both files to check them before it runs either, so as="fetch" with crossorigin="anonymous" is what lets it take the preloaded files rather than download them again; a modulepreload of the glue does not match. Preload only on the pages that mount the designer.

The SDK’s own tests load it the same way and fail when any part moves more over the wire than it does now: the script, the renderer, the fonts, the api’s calls, or a download of another kind. They also fail when the renderer is not asked for before the script has finished running. They print the time, but do not fail on it: a CPU four times slower is measured against the machine running the test, so the same build would pass on one and fail on another.

Questions? Write to [email protected].

Status · Terms of service · Privacy policy · Subprocessors