The Line.
- 01 Input
- 02 Audit
- 03 Banners
- 04 Icons
- 05 Screens
- 06 Template
- 07 Chrome
- 08 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.
- Customer
- A restaurant on an ordering platform
- Needs
- Graphics for two app stores + an eshop
- Before
- Made by hand, client by client
- 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.”
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.
- 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
- 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
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.
- 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
- 02
Audit
Reads the live eshop: IDs, what is already deployed, logos, photo coverage.
Knows what exists before making more
- 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
- 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
- 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
- 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
- 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.
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.
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.
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.
- 01Build
Builder → JSON. A guard fails the build if the previous client's name or prefix leaks in.
- 02Inject
The audit injects the template's CSS and tokens into the live site, where they will sit after deploy.
- 03Audit
10 scenarios on 5 widths. Screenshot and measurements for each.
- 04Fix
One fix in the builder, rebuild, audit again.
- 05Deploy
A person uploads it. Nothing deploys itself.
- 06Accept
The same audit without injection, on the real deployed site.
# 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 okEvery 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
# 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 rulesSeven 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
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.
- 01Colleague
Writes the job into a channel, attaches the logo or QR
- 02Discord bot (Docker)
Opens a thread, downloads attachments, queues the job
- 03Worker
Picks one job at a time, starts a Claude Code session in the pipeline repo
- 04Claude Code
Same rules as manual work. Talks to the thread through two tools only: reply and ask
- 05Thread
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.
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.
- 1)Store screenshots for App Store and Google Play, from their eshop, with the right phone frames
- 2)Icons for the new admin, all 7 layers
- 3)QR code for the receipt — QR from the admin attached
- 00:00startSession starts.
- 00:52threadChecking what already exists for this client before generating anything.
- 02:06threadReading the attached QR to see where it leads.
- 02:10hookHook blocked a command: the job queue belongs to the worker — don't touch inbox, outbox or state.
- 02:36threadThe attached QR matches the one already in the handover folder. Moving on to screenshots.
- 04:50threadTwo small defects in the shots: a card edge peeks above a heading, a heading is cut off at the bottom. Adjusting the scroll offset.
- 10:02threadEshop shots are clean. Generating both sets.
- 11:06threadIcons fit: mark inside the safe zone, favicon readable on light and dark.
- 11:45threadQR verified: Android goes straight to Google Play, iPhone gets a page with both stores.
- 12:45threadChecking dimensions and alpha of every file.
- 13:44threadA vertical seam shows in the Play banner background. Re-cropping to a single panel.
- 14:57resultDone. 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.
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
What this is not.
- 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.
- 02
Nothing uploads itself. People upload to the admin and to the stores.
- 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.
- 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.
- 05
The worker runs on my PC. When it's off, jobs wait.
- 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.