Skip to content
04 / DESIGN + ENGINEERING CASE STUDYRestaurant graphics · Eshop templates · Discord botIN PRODUCTION

The Line.

  1. Input
  2. Audit
  3. Banners
  4. Icons
  5. Screens
  6. Template
  7. Chrome
  8. Handover

A graphic designer built a production line for restaurants. Not a generator. Claude Code makes the banners, icons, store screenshots and whole eshop templates by rules I wrote down from 13 years of graphic and UI design — and measures what can be measured before a store, a client or a customer sees the mistake.

BUILT BY DAVID RAJNOHA ·  AUG–OCT 2026
Scroll
00Project Index
Role
Designer & engineerRules, pipeline, templates, bot
Context
Ordering systems for restaurantsIn-house work at my employer
Period
Aug–Oct 2026Status · in daily use
Stack
Python · Node · Claude CodeChrome DevTools Protocol · Discord
0
Pipeline scripts · 11,509 lines
0
Eshop templates since September
0
Platform bugs written down
0
Median minutes · Discord job
01
The Problem
Customer
A restaurant on an ordering platform
Needs
Graphics for two app stores + an eshop
Before
Made by hand, client by client
One restaurant, one kit
  • Banners · eshop, app, Google Play
  • Logo pack · every admin slot
  • 38 icons · App Store + Google Play
  • Store screenshots · one set per store
  • QR code for the receipt
  • “Get the app” posts and posters
  • Eshop template · desktop + mobile

A restaurant that starts selling online needs a surprising amount of design. Banners for the eshop, the app and Google Play. A logo pack in every size the admin asks for. Icons for both app stores. Two sets of store screenshots, because Apple and Google want different things. A QR code for the receipt. Posters announcing the app. And on top of that, the eshop itself has to look like the restaurant, not like the platform default.

All of it was made by hand, from scratch, for every client. Chains drifted apart: one pizza chain ran 9 eshops for 11 locations in three different looks. Nobody chose that. It just happened, one location at a time.

“Each restaurant needed the same kit, and each one got it made from zero. The work wasn't hard. It was the same work, over and over, with new ways to get it wrong.”
02
Thesis

A line, not a generator. The machine does the repeated work and the measuring. Taste, brand and sign-off stay with a person.

A generator gives you something that looks finished. A line gives you something that was checked at every station, and it stops when a check fails. Every quality gate in it exists because something once got through.

What stays human
  • Taste: what is good, and what is slop
  • The brand, and talking to the client
  • Approving new work; a colleague checks Discord jobs
  • Uploading to the admin and to the stores
  • Turning a new mistake into a new rule
What the machine does
  • Every size, every format, every store
  • Contrast, dimensions, alpha, overflow, tap targets — measured
  • Every page and state of an eshop, in a real Chrome
  • Says where it stopped and why
  • Never uploads anything by itself
03
The graphics line

One photo, three logos and a URL in. A checked kit out.

The input is what a restaurant actually has: one photo of their food, their logo in three variants, and the address of their eshop. Everything about the client lives in one spec file — colors, fonts, banner copy. Nothing client-specific is hardcoded in the scripts.

  1. 01

    Inputs

    Measures the logos and the photo before anything runs: halos around the logo, dots left by upscaling, a background baked into a “transparent” file.

    Stops the run on a bad input

  2. 02

    Audit

    Reads the live eshop: IDs, what is already deployed, logos, photo coverage.

    Knows what exists before making more

  3. 03

    Banners

    Exact sizes for eshop, app and Google Play. The headline grows to fill the space, max two lines.

    Contrast and busyness under the text, measured

  4. 04

    Logo pack

    Logos in every admin slot, map pins with 3:1 contrast against the map, favicon from the mark.

    A check page for Chrome: tab, map, footer

  5. 05

    Icons

    38 icons for both stores plus the feature graphic. Play icon as a full square, iOS dark and tinted variants.

    Safe zone inside Android masks

  6. 06

    Store screens

    Four screens per store, one continuous background cut into four, the right phone for the right store.

    Dimensions, alpha, seams between panels

  7. 07

    Checklist

    Compares every output against the rules of the store it is going to.

    “DONE” or “the store will reject this”

The line never uploads anything. A person looks at the contact sheets and uploads to the admin and the stores. Two phases are not scripts at all: writing the eshop template and walking every page in Chrome — that is Claude Code's job, next section.

04
Eshop templates

Claude Code builds the whole template, then walks every page in a real browser.

The platform has two admins, and they take templates in completely different forms. The method is the same for both: build, verify on the live site before deploying, deploy, verify again for real.

Old admin

A CSS and JS diff appended to the compiled style

Templates live as one folder per location. A linter catches the platform's traps — it silently drops clamp(), min(), max(), @import and // comments — then loads the CSS in headless Chrome and counts how many rules the browser actually accepted.

A pizza chain: 9 eshops for 11 locations in three different looks, merged into one. Each location's file also carries its existing code (redirects, kiosk mode). Simulation before deploy: 9 locations plus the app, 1440 and 390 px, 20 runs. After deploying on 13 October, a live check on the real sites: 20 runs, 16 passed first time and 4 on a re-run (the first pass still saw the old page before the admin save propagated). The kiosk looked identical to before, pixel for pixel, at two locations, with a 2 px antialiasing difference at a third.

New admin

A template as JSON: layout, pages, prefabs, style variables

One builder per client generates the JSON, so a fix is made once in source, not in a 200 KB JSON file. Next to it: a playbook for new templates and a catalogue of platform bugs and techniques, written down as they were found.

A dark template for a restaurant with two entrances — Mexican on one side, Italian on the other — built from the client's mockup, desktop and mobile, then debugged end to end.

The loop, every template
  1. 01
    Build

    Builder → JSON. A guard fails the build if the previous client's name or prefix leaks in.

  2. 02
    Inject

    The audit injects the template's CSS and tokens into the live site, where they will sit after deploy.

  3. 03
    Audit

    10 scenarios on 5 widths. Screenshot and measurements for each.

  4. 04
    Fix

    One fix in the builder, rebuild, audit again.

  5. 05
    Deploy

    A person uploads it. Nothing deploys itself.

  6. 06
    Accept

    The same audit without injection, on the real deployed site.

audit-zive.mjs — report excerpt
# Audit template.json · CSS injected before hydration · 1440 1024 768 440 390## Static CSS checkhover outside @media(hover:hover) ......... 0rules with :active (tap feedback) .......... 36prefers-reduced-motion ..................... yeshex outside tokens ......................... 0scenario  width  overflow contrast tap<44  no-hover  errorshome      1440   0        0        0       0/17      1home       390   0        0        0       0/16      1menu       390   0        0        0       0/18      1modal     1440   0        0        0       0/4       1   close .95 .76 .37 .00cart       440   0        0        2       0/24      2   added, pickup+pay+tipcontact   1440   0        0        0       1/12      1legal      390   0        0        0       —         1   terms ok, privacy ok
A real report, trimmed. Not all zeros: two tap targets under 44 px in the cart at 440 px and one element without hover on the contact page went back for a fix. The console error is the platform's own — it is there without the template too.
05
Quality gates

Every gate is a scar.

None of these were designed up front. Each one is there because a mistake reached someone once — a store reviewer, a colleague, a client, or me.

Graphics

  • Exact dimensions and no alpha, per store
    ↳ App Store rejected a set Google had accepted
  • Phone frame per store
    ↳ An iPhone-looking frame came back from Google Play
  • Contrast and busyness under every headline
    ↳ Text on food photos has to be readable at thumbnail size
  • Seams between store panels
    ↳ Four screens share one background and must line up
  • Czech diacritics in the font files Google actually serves
    ↳ Some popular fonts have no č, ř, ž at all
  • Bad inputs stop the run
    ↳ A logo with a halo ruins every output after it

Eshop

  • Horizontal overflow on 5 widths
    ↳ Mobile is where it breaks first
  • Contrast of every text against its real background
    ↳ Layered alpha lies to simple checks
  • Fonts really loaded, measured on a canvas
    ↳ A font that fails to load falls back without a word
  • Tap targets of 44 px on mobile
    ↳ Restaurant customers order with one thumb
  • Hover by a real mouse, the modal's closing animation
    ↳ A closing modal froze for 0.3 s, then vanished
  • Node tree compared with a template that runs in production
    ↳ A wrong node type renders nothing, with zero errors
checklist.py — a store set before the fix
# 3-store/listingGooglePlay/01-1080x1920.png ......... OKGooglePlay/02-1080x1920.png ......... OKAppStore/01-1242x2688.png ........... store will reject: slot wants 1206x2622AppStore/02-1242x2688.png ........... store will reject: slot wants 1206x2622NOT DONE · 2 outputs fail the App Store rules
06
What broke

Seven things that went wrong, and what they changed.

I keep these because they are the actual design of the system. Each one ended as a rule, a script or a gate — not as a note to be more careful.

  1. 23 Sep01

    App Store rejected the screenshots

    What happened
    One set of 1080×1920 screens went to both stores. Google took it, App Store Connect didn't. A colleague found out while uploading. Worse: the checklist had said DONE, even though the Apple set didn't exist at all.
    What changed
    Two sets, one per store. The checklist now reads the PNG header and checks exact dimensions and alpha against each store's rules.
  2. 1 Oct02

    “That's an iPhone. Google won't take it.”

    What happened
    The phone frame on the Google Play set had a punch-hole camera but an iPhone body. A colleague sent it back.
    What changed
    The frame is chosen by store: an Android phone with an Android status bar for Google Play, an iPhone for Apple.
  3. 9 Oct03

    “We need a real photo, not AI slop.”

    What happened
    A banner draft showed food that a model had re-imagined. It looked fine, and it was not the restaurant's food.
    What changed
    Banners show only real food. AI may recompose a real photo into a banner shape, the food pixels can be kept original, and when AI touched an image it is said right away.
  4. 12 Oct04

    Apple changed the required size

    What happened
    A new mandatory iPhone 6.3" slot accepts only 1179×2556 or 1206×2622. Finished sets for three clients (1242×2688 and 1320×2868) were rejected.
    What changed
    A script derives the 6.3" set from the existing one, the checklist got the new rule, and all three clients were converted the same day.
  5. 26 Aug05

    The menu vanished with zero errors

    What happened
    A new template deployed with the category filter and all products missing. HTTP 200, a clean console. It looked like the client had no products. Three wrong node types at once.
    What changed
    The golden rule of the playbook: copy the tree 1:1 from a template that runs in production, add novelties one at a time. A script compares node types before the first deploy.
  6. 18 Sep06

    Our CSS hid add-ons the customer paid for

    What happened
    A rule meant to hide an empty note block also hid the “Added: bacon, cheddar” line. The note is almost always empty, so the paid extras disappeared from the cart summary.
    What changed
    Fixed across three eshops and their apps the same day. The lesson went into the bug catalogue: an :empty child does not mean the block is empty — read the platform source before hiding anything.
  7. 29 Sep07

    The simulation can't see what the data doesn't trigger

    What happened
    The pre-deploy audit ran on a site with 4 categories. The real restaurant had 20, so the platform showed a scroll arrow — as a solid colored circle over a category.
    What changed
    After every deploy, the same audit runs again on the real site, without injection. The pizza chain's invisible “We're closed” bar was caught exactly that way.
07
The Discord bot

Colleagues who aren't designers run the line with a message.

Not a chatbot that generates pictures. A job queue with clear boundaries: a colleague from support or sales writes what the restaurant needs, attaches files, and gets the finished kit back in the same thread.

FIG. 01 — ONE JOB
  1. 01
    Colleague

    Writes the job into a channel, attaches the logo or QR

  2. 02
    Discord bot (Docker)

    Opens a thread, downloads attachments, queues the job

  3. 03
    Worker

    Picks one job at a time, starts a Claude Code session in the pipeline repo

  4. 04
    Claude Code

    Same rules as manual work. Talks to the thread through two tools only: reply and ask

  5. 05
    Thread

    Milestones, then the result with attachments. A reply continues the same session

Design decisions

File protocol, one writer per file
The bot and the worker never write the same file. No locks to get wrong.
One job at a time
Two sessions in one repo would overwrite each other's outputs.
Same rules as manual work
The session reads the same project rules and memory as I do. It may report a lesson, never write one.
Heartbeat
The bot says whether the PC is on. After 12 minutes of silence the worker posts its progress by itself.
Catch-up after sleep
When the PC wakes up, missed messages are fetched, up to 3 days back.
Big files via a shared drive
Anything over 20 MB goes as a link instead of an attachment.

Security model

  • Only allowed people can start a job — not webhooks, not other bots.
  • A hook before every tool call: writes only into output folders and templates, never into rules, memory or scripts.
  • No secrets, no network beyond its own thread tools.
  • No access to my logged-in Chrome. Eshop screenshots come from headless Chrome in the pipeline scripts.
  • Attachments only from output folders, with a filter for anything that looks like a key.
  • Two rounds of review by AI agents for security, edge cases and a blind pass without context. The second round (6 Oct) found over 20 issues; the serious ones are fixed.

Described as a model on purpose. The concrete configuration would be a manual for getting around it.

08
One job, start to finish

A full app kit in 15 minutes.

A real job from the logs, 6 October. A colleague asked for an app kit for a pizzeria on the new admin. Times are from the worker log, messages are what the session wrote into the thread.

JOB#20261006-0736
Colleague from support
  1. 1)Store screenshots for App Store and Google Play, from their eshop, with the right phone frames
  2. 2)Icons for the new admin, all 7 layers
  3. 3)QR code for the receipt — QR from the admin attached
⎘ app-qr-code.svg
  1. 00:00
    startSession starts.
  2. 00:52
    threadChecking what already exists for this client before generating anything.
  3. 02:06
    threadReading the attached QR to see where it leads.
  4. 02:10
    hookHook blocked a command: the job queue belongs to the worker — don't touch inbox, outbox or state.
  5. 02:36
    threadThe attached QR matches the one already in the handover folder. Moving on to screenshots.
  6. 04:50
    threadTwo small defects in the shots: a card edge peeks above a heading, a heading is cut off at the bottom. Adjusting the scroll offset.
  7. 10:02
    threadEshop shots are clean. Generating both sets.
  8. 11:06
    threadIcons fit: mark inside the safe zone, favicon readable on light and dark.
  9. 11:45
    threadQR verified: Android goes straight to Google Play, iPhone gets a page with both stores.
  10. 12:45
    threadChecking dimensions and alpha of every file.
  11. 13:44
    threadA vertical seam shows in the Play banner background. Re-cropping to a single panel.
  12. 14:57
    resultDone. ZIP (18.9 MB), screenshot preview and receipt QR posted to the thread.

89 turns, 14 min 55 s, nobody in between. The new work was both screenshot sets; the icons and the QR existed from an earlier job and the session only verified them. A colleague checked the result before uploading.

09
Numbers

Only what was measured.

Counted on disk and in job logs on 17 October 2026. Time saved against manual work is not here: I haven't measured it, so I don't claim it.

44
Python scripts in the graphics line
11,509 lines
1,260
Lines of the runbook
PIPELINE.md
12
Kits handed over since 5 Oct
6 clients, incl. fixes and added sets
12+9
Eshop templates built or rebuilt since September
old admin + new admin
88
Platform bugs written down
plus 95 techniques
10×5
Audit scenarios × widths
per template, before and after deploy
11
Jobs via Discord in the first week
5–12 Oct · 1 test
12
Median minutes per Discord job
8 jobs without waiting for a reply · max 42
2,127
Lines of TypeScript in the bot
bot + worker · 24 test files
10
Honest limits

What this is not.

  1. 01

    Two modes, two levels of oversight. New work I do in a session with Claude Code and approve myself. Discord jobs run without me and a colleague checks the result. The gates are the same; a designer's eye isn't there.

  2. 02

    Nothing uploads itself. People upload to the admin and to the stores.

  3. 03

    The pre-deploy simulation can't show states the test site's data never triggers. That's why every deploy gets a real acceptance run.

  4. 04

    Windows has no shell sandbox. The rules match the shape of a command, and a determined attacker could get around them with a script. Acceptable for a few colleagues, not for more people.

  5. 05

    The worker runs on my PC. When it's off, jobs wait.

  6. 06

    The bot's sample is small: 11 jobs in its first week.

The rules are the product.

The scripts would be easy to rewrite. What took the time is the list of things that must not happen again — and a line that checks for them every single run.