URL Mode

Every tool's state is expressible as URL parameters. This means any combination of inputs and export settings can be bookmarked, linked, embedded, or piped through automation - with no login, no cookies, and no server state.

The CLI uses the same parameter names and the same conversion logic. A URL you build for the web shell runs unchanged as --flag=value arguments on the CLI.


URL structure

https://your-host/#/tool/{toolId}?{param}={value}&{param}={value}

Examples:

/#/tool/qr-code?url=https://suse.com&color=%230c322c
/#/tool/qr-code?url=https://suse.com&format=png&export&filename=my-qr
/#/tool/quotes?quote=Open+source+wins.&name=Andy&format=svg&export&full

The first of those opens the QR tool with the address and the dark green already applied, no clicks in between.

A QR code rendered straight from the URL, in the dark green passed as ?color

Clean URL redirect

If a tool is deployed at a dedicated domain or path, you can use a plain query string and the shell redirects to hash form automatically:

https://qr.brand.example.com/?url=https://suse.com
  → redirects to → /#/tool/qr-code?url=https://suse.com

Setting tool inputs

Every input defined in a tool's manifest can be set as a URL parameter using its id as the key.

String, text, longtext, url

Pass the value directly. URL-encode spaces and special characters.

?quote=The+best+way+to+predict+the+future+is+to+create+it.
?name=Andy+Fitzsimon
?url=https%3A%2F%2Fwww.suse.com

The Wordmark tool is nothing but text params, so a link is the whole brief: /#/tool/wordmark?text=Ship it&weight=800&tracking=-12&size=200.

The word

Select

Pass the option value (not the label).

?theme=dark
?ecl=H

A one-word change swaps the whole render: /#/tool/street-map?city=barcelona&theme=dark.

Street Map drawn in its dark theme, picked entirely by the theme param

Number

?size=800
?padding=4

A number can also set how much of a thing gets made. Colour Palette builds one ramp cell per step, so /#/tool/color-palette?steps=11 returns a wider sheet than the default seven.

A palette sheet eleven cells wide, the count set only by the steps param

Boolean

1 or true for on, 0 or false for off.

?join=1
?showBorder=false

One flag can add a whole layer of information: /#/tool/d3?showValues=1 prints the number on every bar.

The D3 bar chart with a value printed on each bar, switched on by a single boolean param

Color

Pass a hex value (URL-encode the #).

?color=%230c322c
?background=%23ffffff

Colour params stack. Mesh Gradient takes one per stop, so three hex values in a link are three fields of colour in the render.

A generative gradient blooming in coral, amber and violet, one bloom per colour param

Asset

Pass the asset's library ID - the runtime resolves it to the full asset object at render time.

?logo=suse/logo/primary
?headshot=team/andy-fitzsimon

To discover asset IDs, open the asset picker in the tool UI and inspect the value shown when an asset is selected. An audio ID works the same way as an image: Audiogram decodes whichever track the link names and draws its real waveform.

An audiogram card whose waveform is the actual shape of the catalogue track named in the link, with the title and the wide 16:9 size set alongside it

An asset value can also be another tool's render. When a user pastes a Lolly tool link into the asset picker (a share link or an embed URL), the chosen value's "id" is the canonical embed URL of that render, so it round-trips through the URL exactly like a library id - just longer:

?hero=https%3A%2F%2Flolly.tools%2Ftool%2Fqr-code.svg%3Furl%3Dhttps%3A%2F%2Fsuse.com%26w%3D600%26h%3D600

On load the runtime re-renders it via host.compose.renderUrl instead of looking it up in the catalog. This is how one tool's output (a QR code, a filtered hero graphic) flows into another tool's image slot through a plain shareable link. See Tool composition and the authoring guide.

User-uploaded images are device-local and not URL-shareable. Images a user adds from their own device (AssetRef.source: "user", ids like user/upload/…) live only in that device's local storage. There is no shareable id to encode, so they are deliberately omitted from the URL - a link that referenced one would not resolve on another device. To share a layout that uses a personal image, the recipient must select their own. (Avoiding this would require cloud hosting, which the platform intentionally does not do.)

Blocks

Blocks inputs are repeating groups of fields (e.g. a list of team members, each with a name and city). Pass the value as a JSON array of objects, URL-encoded.

?people=[{"name":"Andy","city":"Nuremberg"},{"name":"Lisa","city":"Sydney"}]

Each object's keys must match the field ids defined in the tool's manifest. Fields can be omitted - missing fields are treated as empty strings. Chart Creator's data input is a blocks list of label, value and colour, so a whole dataset travels in the link:

A three bar chart whose labels, values and bar colours all arrived as one JSON blocks param

CLI:

lolly meeting-planner --people='[{"name":"Andy","city":"Nuremberg"},{"name":"Lisa","city":"Sydney"}]'

The URL updates automatically as block items are added, removed, or edited in the UI - copy from the address bar to get a shareable link with all entries included.

Blocks with a JSON representation larger than 8 KB are not written to the URL to avoid exceeding browser URL limits. In that case, use a saved state slot for sharing.

Vector

A vector input is a fixed group of numbers edited as one control (e.g. a zoom + x/y offset). It has no single-param form - pass each field as a flat dotted param <inputId>.<fieldId>:

?imageFraming.zoom=200&imageFraming.x=30&imageFraming.y=70

One readable value per param. Used by tools such as bag-video, chart-creator, filter-duotone, dynamic-layout, and quotes. Mesh Gradient parks each colour stop with one, so adding ?pos1.x=8&pos1.y=8&pos2.x=92&pos2.y=12&pos3.x=50&pos3.y=94 to the gradient above drives its three blooms out to the edges:

The coral, amber and violet gradient again, this time with each bloom pinned to an edge by its own dotted x and y param

File

A file input (the user's own file, processed in memory) is never put in a URL - its bytes live only on the device, so there is nothing shareable to encode. On the CLI a file param is a filesystem path, loaded into memory before rendering:

lolly strip-data --source=./photo.jpg --format=jpg --output=clean.jpg

In the web shell a file input can't be pre-filled from a URL; a link that referenced one resolves as blank, and the recipient picks their own file.


Compact encoding (opt-in)

Tools can opt into a shorter URL form, which the web shell emits when you copy a share link (the live address bar keeps the readable long form). Both the long forms above and the compact forms below parse, so either kind of link works:

chart-creator is a live tool that uses urlKey, so a link copied via its Copy URL / share button won't match the long-form examples in this doc - that's expected. d3 uses them too: ?ct=radar&pl=cool&t=Short keys&lg=0 is chart type, palette, heading and legend in twelve characters of query.

A radar chart in the cool palette, drawn from four short-key params instead of their long names


Reserved parameters

These keys are never treated as tool inputs. They control shell-level behaviour.

ParamWhereDescription
formatweb + CLIOutput format (png, svg, pdf, …). Used by export and copy.
exportweb + CLIPresence flag - trigger an immediate download on page load.
copyweb onlyPresence flag - arm copy-to-clipboard on first interaction.
fullweb onlyPresence flag - open in fullscreen (sidebar collapsed).
optionsweb onlyPresence flag - open with the export-settings panel expanded instead of the collapsed Render button. full wins if both are set.
filenameweb onlyName for the downloaded file (no extension). Defaults to the tool ID.
slotweb onlyName of a saved state slot to pre-load. URL params override saved values. (The one-shot CLI has no saved-state store, so it ignores slot.)
outputCLI onlyFile path to write the exported file. Defaults to stdout.
_vweb + CLITool version pin (e.g. 1.0.0). Ignored if not matched - forward-compat safety.
width / wweb + CLIOutput width, as a value in unit. Also pre-fills the export dimensions panel.
height / hweb + CLIOutput height, as a value in unit. Also pre-fills the export dimensions panel.
unitweb + CLIPhysical unit for width/height: px (default), mm, cm, in, pt, pc.
dpiweb + CLIRaster resolution for physical units (default 300). Ignored for px and for vector formats.
passwordweb + CLIPDF open password (pdf only; the CLI applies it wherever it can render a PDF). A basic lock, not strong encryption; it travels in clear text in the URL, so it's a light deterrent, not protection for confidential material. Ignored when bleed/marks are on (encrypted PDFs can't carry print finishing).
profileweb onlyColour profile, two roles by format. For ordinary raster (png / jpg) it selects the ICC profile: srgb (the default) embeds an sRGB profile; none omits it. For the print formats (pdf-cmyk / cmyk-tiff) it is the CMYK press condition, e.g. fogra51 - embedded as the PDF's output intent, recorded in the TIFF's provenance.
bleedweb onlyBleed amount for the print formats (pdf / pdf-cmyk / cmyk-tiff), as a dimension (e.g. 3mm, 0.125in). The artwork is scaled to fill the bleed; the PDF declares TrimBox/BleedBox, the TIFF is enlarged to the full sheet.
marksweb onlyPrint marks for the print formats (pdf / pdf-cmyk / cmyk-tiff) - a CSV of crop, reg, bleed, bars, prov. Drawn in the page margin (PDF) or rasterised into the image margin (TIFF); registration prints on all four plates in pdf-cmyk and cmyk-tiff. prov (provenance credit text) is PDF-only.
c2paweb + CLIContent Credentials for the stampable formats. c2pa=7/30/90/365 embeds the credential with that ephemeral-certificate lifetime in days; c2pa=1 (or a bare --c2pa on the CLI) uses the default (30); c2pa=off forces it off, overriding a tool's render.c2pa default. Web: an enrolled identity's certificate window (fixed at enrolment) takes precedence and the lifetime value is ignored. CLI: ephemeral signing only (svg in the lean CLI). Mutually exclusive with password on PDFs.
durableweb (opt-in)Durable Content Credential for raster exports (png / jpg/jpeg / webp / avif / tiff): an opt-in neural TrustMark-format watermark carrying Lolly's own identifier, so the "made with Lolly" link survives a metadata strip (a social upload, a re-save) and any TrustMark-aware tool can recover it. Off by default - a heavy on-device neural encode that also needs a model fetched once - so pass durable=1 (or durable=on) to turn it on. A no-op if the encoder model isn't on-device (see scripts/convert-trustmark-encoder-onnx.py); raster-only for now (not the pdf/pptx container rasters). Complements - does not replace - the default imprint and the c2pa credential. Recognised on-device on the /verify page as a "Lolly durable mark" pip. See plans/durable-content-credentials.md.
imprintweb (default-on) + CLI (opt-in)Lolly pixel watermark for raster exports (png / jpg/jpeg / webp / avif / tiff - the RGB TIFF, not Print/CMYK TIFF), plus Lolly-rendered raster content embedded inside a pdf, pdf-cmyk, or pptx export - a composed tool render, a gradient/filter fallback, or an SVG illustration that gets walked to pixels still carries the mark even though the container itself isn't a raster format (a zip bundle carries it through to whichever of its members qualify). It never marks a user's own uploaded image - only art Lolly itself rasterised. On by default on the web, like c2pa - embedded unless explicitly disabled; imprint=0 (or imprint=off) turns it off. imprint=1 (or a bare ?imprint) is still accepted for existing links (redundant with the web default). On the CLI it is opt-in - pass --imprint (and note the resvg PNG fast path can't embed it; use a browser-tier format). Unlike c2pa - which lives in a metadata container and dies to any re-save or strip - the imprint survives metadata stripping, recompression (down to ~JPEG q50) and an 8-pixel-aligned crop, so it's a durable complement to the credential. TIFF (lossless) round-trips the mark exactly; AVIF's AV1 encode applies the mark pre-encode but its survival through that encode is not yet calibrated/verified. It does not survive an arbitrary resize, and it is security-through-obscurity (the detector key is public), so it's an integrity hint, not a hardened claim. Detected on-device on the /verify page - for pdf/pdf-cmyk/pptx files, detection scans the embedded Lolly-rendered rasters, not the page/slide as a whole.
langweb + CLIUI/content language as a canonical short code: en (default), es, de, fr, zh (Simplified), zh-hant (Traditional), ja, ko, vi, pt, it, nl, sv, no, pl, cs, ro, tr, uk, bg, ms, id, tl, hi, bn, ur, and ar (the LANGS set in engine/src/lang.ts is the source of truth). Arabic and Urdu render right-to-left (the whole UI mirrors). Informal aliases (cn, jp, and in for id) are accepted and normalized on parse. Applies for that session only - it does not overwrite the recipient's saved language preference. Unset/unrecognized falls back to the profile, then localStorage, then the browser's language, then English.
nostageweb onlyPresence flag - for the html export only, drop the fixed-size canvas frame ("stage") so the saved page fills the whole window: the tool's content becomes the document body, with no centred card or grey backdrop. Mirrors the Full page toggle in the export panel.
zweb + CLIA packed whole-state token - the entire readable query, compressed (raw DEFLATE) and base64url-encoded, for complex tools whose readable link would blow past practical URL limits. See Packed links below.
zxweb onlyAn encrypted whole-state token - the packed state AES-256-GCM-encrypted under a password-derived key (PBKDF2). Opening the link prompts for the password in the browser (no server); the password itself is never in the link. See Encrypted links below.

export, copy, full, options, and nostage are presence flags - the parameter value is ignored; what matters is whether the key appears in the URL.

lang is the one reserved param that changes the interface rather than the file. Adding ?lang=ja to any tool link hands the recipient the whole sidebar in Japanese for that session, without touching their saved preference.

The Colour Palette controls with every label, hint and dropdown option in Japanese, from one lang param

Building share links in the UI. In the web shell you don't have to hand-write these. The Share button (in the export panel) opens a dialog with the ready-to-copy link plus a toggle for each on-visit flag - _open fullscreen_ (full), _open with the export panel expanded_ (options), _download automatically_ (export), _copy to clipboard_ (copy), and _pin tool version_ (_v). The copy toggle appears only for clipboard-friendly formats (bitmap/text/html) and is hidden for SVG, PDF, and video. Ticking a box rewrites the link in place. The same dialog is reachable from Projects → Share link on any saved session (it reconstructs the tool URL from the saved inputs).

Readable URLs are first-class - a simple ?color=30BA78&theme=dark link can be hand-edited. But a complex tool (e.g. Layout Studio, with dozens of boxes each carrying coordinates, colours and text) serialises to thousands of characters, past the ~2000-char ceiling that pasted links, social crawlers, QR codes and some servers still enforce.

For those, the app compresses the entire readable query into one z param:

/t/layout-studio?background=…&boxes=…&format=png   ← readable (e.g. 2729 chars)
/t/layout-studio?z=1eJyFkc…                         ← packed   (e.g. 1059 chars)

A password-gated variant of a packed link: share a link that only opens for someone who knows the password - with no server and no account.

The state is DEFLATE'd then AES-256-GCM-encrypted under a key derived from a password (PBKDF2-SHA256), and carried in a zx param. Opening the link prompts for the password, derives the key, and decrypts entirely in the recipient's browser, then rebuilds the tool content. Wrong password → it asks again; cancel → the tool loads at its defaults.

Physical units (unit= + dpi=)

width/height are plain numbers; unit says what they mean. With a physical unit the output is rendered at the correct physical size for the format, not just a pixel count:

px is the default and behaves exactly as before (the CSS 96-DPI convention). The canvas takes the physical shape straight away, so ?w=210&h=297&unit=mm&format=pdf shows you a portrait A4 before you export anything.

The Wordmark canvas standing as a portrait A4 page, its shape set by the width, height and unit params before anything is exported

lolly quotes --quote="Print me." --width=210 --height=297 --unit=mm --export=svg --output=a4.svg

For print-ready output, bleed= and marks= add the prep a print shop expects to the pdf (RGB), pdf-cmyk (Print PDF) and cmyk-tiff (Print TIFF) formats. They're ignored for every other format. The two CMYK formats apply the same engine geometry - the PDF as vectors with declared page boxes, the TIFF rasterised onto an enlarged sheet.

The Print marks and bleed card opened by a bleed and marks link, with each mark toggle already set

The CMYK press condition (profile=, e.g. fogra51) is carried for both CMYK formats: the Print PDF embeds it as the document's output intent; the Print TIFF records it as provenance in ImageDescription (the pixels stay untagged DeviceCMYK - no embedded profile, so the file is never mislabelled).

?format=cmyk-tiff&bleed=3mm&marks=crop,reg,bleed,bars&profile=fogra51&export

Marks/bleed and the PDF open-password are mutually exclusive: print finishing is applied via pdf-lib, which can't write encrypted PDFs, so a password is ignored when marks/bleed are on. (cmyk-tiff has no password concept.)


Format with format=

format=<fmt> selects the output format for both export (download) and copy (clipboard).

Supported values:

ValueOutput
svgScalable vector (requires <svg> root in the template)
svg-animAnimated SVG - self-contained vector flipbook (animated tools only)
emfEnhanced Metafile vector (for Office apps)
epsEncapsulated PostScript vector (RGB)
eps-cmykEncapsulated PostScript vector - DeviceCMYK (naive conversion, no output intent)
dxfDXF vector cut file - AutoCAD R12, paths in mm (laser/vinyl/CNC)
pngLossless raster
jpg / jpegLossy raster
webpLossy/lossless raster
avifAVIF raster
tiffUncompressed sRGB raster (RGB TIFF)
pdfPDF document
pdf-cmykPrint PDF - CMYK with output intent (see print marks & bleed)
cmyk-tiffPrint TIFF - flattened CMYK raster
pptxPowerPoint deck - native editable text/shapes + extractable images/vectors
icoIcon bundle (e.g. tool-logo)
zipMulti-file bundle (optionally password-locked - see Exporting → Locked downloads)
htmlStatic HTML document
md / txtMarkdown / plain text
json / csvStructured data
ics / vcfCalendar event / contact card
gifAnimated GIF (animated tools only)
apngAnimated PNG - full colour + real alpha (animated tools only)
webp-animAnimated WebP - full colour + alpha, smallest file (animated tools only)
webmWebM video (animated tools only; Chrome/Firefox/Android)
mp4MP4 video (animated tools only; Safari/iOS and recent Chrome)

Not all tools support all formats - only the formats listed in the tool's manifest render.formats are valid (the full set is the 30-value enum in schemas/tool.schema.json). Requesting an unsupported format falls back gracefully.


Download with export

Adding export (no value needed) triggers an automatic download the moment the tool finishes rendering. Pair it with format= to set the file type; if format is omitted the tool's default format is used.

/#/tool/qr-code?url=https://suse.com&format=svg&export
/#/tool/qr-code?url=https://suse.com&format=png&export
/#/tool/qr-code?url=https://suse.com&format=pdf&export

export without format downloads in the tool's first listed format:

/#/tool/qr-code?url=https://suse.com&export

Download filename with filename=

Sets the name of the downloaded file. The format extension is appended automatically - do not include it.

/#/tool/qr-code?url=https://suse.com&format=png&export&filename=homepage-qr
→ downloads as homepage-qr.png

/#/tool/qr-code?url=https://suse.com&format=svg&export&filename=event-badge
→ downloads as event-badge.svg

Without filename=, the download is named after the tool ID (e.g. qr-code.png).


Copy to clipboard with copy

copy (no value needed) arms the tool's copy-to-clipboard action. Pair it with format= to choose the format; if format is omitted the tool's default is used.

/#/tool/email-signature?firstname=Andy&format=html&copy
/#/tool/qr-code?url=https://suse.com&copy
/#/tool/qr-code?url=https://suse.com&format=png&copy

It does not fire silently on load. Browsers only allow a clipboard write in response to a user gesture (navigator.clipboard.write rejects otherwise, and the image path would fall back to an unexpected download). So when copy is present, the shell highlights the Copy button and performs the copy on your first interaction with the page - the click that supplies the required gesture.

Use export instead if you want a genuinely unattended result (a download needs no gesture). copy is for "open this link, then it's ready to paste." It is a web-shell affordance; the CLI ignores it (use --output / stdout).


Canvas dimensions with width= / height=

width and height (short aliases w and h) set both the canvas document size and pre-fill the export dimensions panel. They are not passed to the tool as inputs.

?width=1200&height=630
?w=800&h=800
?w=1920&h=1080

Mixing long and short forms is fine - ?width=1200&h=630 works. The canvas preview updates to the new aspect ratio.

Mesh Gradient reshaped to a 1920 by 1080 canvas by the w and h params alone


Fullscreen with full

full collapses the sidebar so the canvas fills the entire viewport. The value is ignored - presence of the param is enough.

/#/tool/qr-code?url=https://suse.com&full

Any tool takes it. Mesh Gradient with full is artwork and nothing else.

A tool opened with full - no sidebar and no chrome, just the artwork edge to edge

Combine with export for a clean unattended export flow:

/#/tool/qr-code?url=https://suse.com&format=png&filename=my-qr&export&full

Land on the export panel with options

options opens the tool with the export-settings panel already expanded (format, dimensions, DPI, and the export/copy buttons) instead of the collapsed Render button. Use it to share a link where the recipient is one click from downloading.

/#/tool/qr-code?url=https://suse.com&options

The export panel already expanded at the foot of the sidebar, one click from a download

options is the opposite of full: full hides all chrome to show only the preview, while options surfaces the export chrome. If both appear, full wins (there's nowhere to anchor the export panel once the sidebar is collapsed). The flag is web-only - the CLI ignores it.


Transparent background

Tools that support transparent export expose a transparentBg boolean input. Pass it like any other boolean input:

?transparentBg=1

Transparency is preserved in formats that support an alpha channel: png, webp, and avif. It is ignored for jpg, pdf, and svg (SVG has no background rect when transparent).

Full example:

/#/tool/qr-code?url=https://suse.com&color=%230c322c&transparentBg=1&format=png&export&filename=qr-transparent

The engine injects a second export toggle the same way: convertPaths (the Convert paths text-to-vector outlining control) is added automatically to tools that export a vector format. It is URL-expressible as any boolean - ?convertPaths=0 to leave text live, ?convertPaths=1 to outline it - and defaults on. A tool that sets render.convertPaths: false suppresses it (and the param has no effect).


Loading saved state with slot=

Saved state slots are named snapshots of input values stored in the browser. The slot param loads one by name. Any URL params present alongside slot override the saved values for that render only.

/#/tool/quotes?slot=andy-quote-v2
/#/tool/qr-code?slot=homepage-qr&format=png&export

Combining parameters

All parameters compose freely. A fully-specified automation URL might look like:

/#/tool/qr-code?url=https://suse.com/event&color=%230c322c&background=%23ffffff&ecl=H&padding=4&format=png&export&filename=event-qr&w=600&h=600&full

This opens the QR tool, applies all inputs, sets the canvas to 600×600, collapses the sidebar, and immediately downloads event-qr.png.

The same stacking works on a chart, where inputs, compact keys, canvas size and full arrive together:

/#/tool/d3?ct=donut&pl=warm&t=Everything+in+one+link&lg=1&lp=right&sv=1&w=1200&h=800&full

A warm-palette donut with a legend on the right and values on every slice, filling the window because the same link also passed full


CLI usage

The CLI uses the same param names as URL mode - --key=value instead of ?key=value. format, export, and output are handled as special flags; all other params are tool inputs.

# Web equivalent: /#/tool/qr-code?url=https://suse.com&format=png&export&filename=my-qr
lolly qr-code --url=https://suse.com --format=png --export --output=my-qr.png

# Pipe SVG to another tool
lolly qr-code --url=https://suse.com --format=svg > qr.svg

# Print available inputs for a tool
lolly qr-code

Integration patterns

The web shell writes the current input state to the URL hash automatically as inputs change - copy from the address bar at any time.

Pre-filled embed

Embed the tool in an iframe with inputs pre-filled via URL:

<iframe src="https://brand.example.com/#/tool/qr-code?url=https://suse.com&full"
        width="900" height="700" frameborder="0"></iframe>

Tool composition (portable embed URL)

A tool can embed another tool's render with no tool-to-tool imports. The URL-mode face of this is a portable embed URL - a real-looking image URL whose query is ordinary URL-mode params:

<img src="https://lolly.tools/tool/qr-code.svg?url=https://suse.com&color=0c322c">

Nothing is ever fetched from lolly.tools. A shell recognises this exact shape and renders the named tool locally, substituting the result (a placeholder pixel shows until the local render resolves). Anything that isn't exactly this grammar is treated as an ordinary image - that strict match is the security boundary.

The path extension is the author's fidelity choice. Compose any tool's render: an SVG child stays a true vector when the parent exports to SVG or PDF and rasterises crisply for PNG; raster children (png, jpg/jpeg, webp) embed as images. (pdf appears in the grammar but is not inlined as a child format.)

This is the URL-mode surface of composition. The declarative form - a manifest composes: [{ id, tool, inputs, format? }] block resolved by the engine and placed in the template as {{asset <id>}} - is not a URL param; see the authoring guide. Either form requires the tool to declare the compose capability. event-name-badge composes qr-code as SVG today.

Automation / CI

Call the CLI in a build pipeline to generate assets on demand:

lolly qr-code \
  --url=https://suse.com/product/${SLUG} \
  --color=#0c322c \
  --format=svg \
  --export \
  --output=./dist/qr-${SLUG}.svg