Build RelayThat
KINDAback to the verdict
A local brand design tool whose hard problems are one layout resolving correctly into a dozen aspect ratios, text and fonts that render identically on two machines, and exports whose pixel dimensions are the ones you asked for.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Tauri 2 needs both, and the desktop shell is what gives you the font folder and the export folder.
Get it nodejs.org and rustup.rs, then the Tauri prerequisites for your platform. open ↗
Verify
node --version prints v22 or higher and cargo --version succeeds - have readyfree for open licensed families, otherwise whatever you paid
Why The whole rendering model loads faces by file, and the licence of each face decides whether it may travel in an export.
Get it Your own purchased faces, or open licensed families whose licence text you have actually read.
Verify
Every face in the folder has a recorded licence name and a redistributable flag - have readyfree
Why Contrast over a photograph behaves nothing like contrast over a flat colour, and it is the case that breaks first.
Get it Export your logo with transparency at a generous size, and pick two photographs with busy areas.
Verify
A headline over the busy photograph is flagged at some sizes and not others - have readyfree
Why The minimum ratio is copied from published guidance rather than chosen, and it differs by text size.
Get it Read the current accessibility guidance for contrast, note the ratios and the date, and record both in your configuration. open ↗
Verify
A note records the ratios used and the guidance version - have readyfree
Why The export dimension bug only appears when the display scale factor differs from one, and it is the most common way a render comes out the wrong size.
Get it A laptop with a high density panel plus any ordinary external monitor.
Verify
The same export produces identical dimensions on both
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- brands(id, name, version, palette_json, logo_asset_ids, font_ids, created_at) · versioned, because a render has to say which brand it used - fonts(id, family, style, file_path, licence_name, licence_url, redistributable, added_at) · the licence is recorded per face, not per project - assets(id, kind, path, width, height, sha256, has_alpha) · logos and photographs copied in, never referenced from somewhere else on disk - sizes(id, name, width_px, height_px, safe_top, safe_right, safe_bottom, safe_left) · every output shape carries its own safe area - layouts(id, name, version, slots_json) · a slot has an anchor, a minimum and maximum type size, and a stated behaviour when the text still does not fit - designs(id, brand_id, layout_id, headline, subhead, call_to_action, asset_ids_json) - renders(id, design_id, size_id, path, manifest_json, rendered_at) · the manifest holds font ids, asset hashes, brand version and layout version - A design is stored once; a render is a design plus a size plus the manifest that lets you rebuild that exact file a year later.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATA_DIR | required | ~/.local/share/brandkit | SQLite plus the copied assets. Back it up with the fonts folder. |
FONTS_DIR | required | ~/.local/share/brandkit/fonts | Font files loaded by path. Each face records its licence. |
EXPORT_DIR | required | ~/Pictures/brandkit-exports | Renders and their manifests, side by side. |
EXPORT_RETENTION_DAYS | required | 180 | How long renders are kept. Manifests let you rebuild after that. |
MAX_RENDER_PIXELS | required | 40000000 | A ceiling on width times height, so a mistyped size cannot exhaust memory. |
The build, in order
One design, many shapes
A design laid out once renders into a wide, a square and a tall size without anything falling off the canvas.
The safe area is what the layout anchors to, so it is part of the size definition.
Files
src/sizes.tssrc/layout/anchors.tsScaling is the bug this whole phase exists to avoid.
Files
src/layout/resolve.tssrc/layout/resolve.test.tsA bounding box check catches the failure the eye misses at thumbnail size.
Files
tests/safe-area.test.ts
done when · tick each as it passeswatch out- Producing the other sizes by multiplying the square design's coordinates by a scale factor pushes the headline off a tall canvas and drops the call to action to nine pixels on a wide banner, and both look acceptable in a thumbnail. Anchor each slot to the target size's safe area and resolve its type size for that size alone.
Fonts that render the same on two machines
Type is drawn from a font file you hold, with its licence recorded, and a missing face fails loudly.
The licence belongs to the face, and you will want it when you export a kit.
Files
src/fonts/registry.tssrc/fonts/licence.tsAny other measurement is an estimate that diverges on the long strings.
Files
src/text/measure.tssrc/text/measure.test.tsA silent fallback is a wrong export you will ship.
Files
src/fonts/registry.tstests/missing-face.test.ts
done when · tick each as it passeswatch out- Naming a font family and letting the system pick means the machine without that family quietly substitutes a fallback, so the same design exports with different line breaks on your laptop and on the machine you render the batch from, and nobody notices until a headline wraps onto three lines in the one size you did not check. Load the file, measure with the engine that draws, and treat a missing face as a failure.
Text that fits and colour you can read
Long copy on a narrow size fails visibly rather than exporting something unreadable.
A readable failure beats an unreadable export.
Files
src/text/autofit.tssrc/text/autofit.test.tsThe crop changes per aspect ratio, so the background does too.
Files
src/contrast/sample.tsCopied from the current guidance, with the date, not invented.
Files
src/contrast/thresholds.tsdocs/contrast.md
done when · tick each as it passeswatch out- Shrinking type until the copy fits gives you a six point headline on the tall size, and clipping gives you a caption that ends mid-word, and both export without complaint because the code only asked whether the text fitted. Set a floor, and when the copy still does not fit, fail that render with the size name and the character count so the copy gets shortened instead.
- Contrast checked once against the brand's background colour passes, then the same headline goes over a photograph and becomes invisible on half the sizes because the crop differs per aspect ratio. Sample what is actually behind the text at each size.
Exports at the size you asked for
A file requested at 1080 by 1080 is 1080 by 1080 on every display.
The display's scale factor must not reach the output.
Files
src/render/offscreen.tsRead the file back; do not trust the call that wrote it.
Files
tests/export-size.test.tsA failed size should not leave half a PNG in the folder.
Files
src/render/batch.ts
done when · tick each as it passeswatch out- Exporting by reading pixels back from the canvas that is on screen gives you a file at the display's device pixel ratio, so the same button produces a 2160 pixel square on a high density laptop and a 1080 pixel one on an external monitor, and the platform rejects one of them. Render off-screen at the requested dimensions and assert the written file's size.
Renders you can reproduce next yearproduct builder
Any file in the export folder can be traced to the exact inputs that made it, and rebuilt.
Font ids, asset hashes, brand and layout versions, size.
Files
src/render/manifest.tsAn edited brand makes every past render unreproducible.
Files
src/brands/versions.tstests/rerender.test.tsFolders of images and a text list of colours and font licences.
Files
src/export/kit.ts
done when · tick each as it passeswatch out- Editing a brand colour in place rewrites the brand for every past render, so the file a client approved in March can no longer be reproduced and nobody can say which logo version they actually saw. Version the brand kit, and record the version in each render's manifest.
That is the whole plan for RelayThat. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Font files inside exports.
- Print colour profiles.
- A stock template and photograph library.
- Cloud collaboration and a mobile editor.
- color-management edge cases
- cloud collaboration and mobile apps
- pixel-perfect professional tooling
- large template and asset libraries
- Animated sizes, once static renders have been reproducible for a month
- A copy length budget shown while typing, computed from the tightest size
- Per-platform size presets imported from a file you maintain rather than hard-coded
Need the files? The project pack on the verdict page hands your agent the whole brief · more design.