<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ShopLaunch]]></title><description><![CDATA[ShopLaunch]]></description><link>https://shoplaunch.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 15 Nov 2026 23:28:20 GMT</lastBuildDate><atom:link href="https://shoplaunch.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Google Maps to a live page and a printable A4 menu in 24 hours — and why a local restaurant doesn't need a $2,000 agency]]></title><description><![CDATA[A restaurant owner I know was quoted $2,000 and four weeks for a website. Not an online ordering system, not a booking engine — a page with the menu, the…]]></description><link>https://shoplaunch.hashnode.dev/google-maps-to-a-live-page-and-a-printable-a4-menu-in-24-hours-and-why-a-local-restaurant-doesn-t-need-a-2-000-agency</link><guid isPermaLink="true">https://shoplaunch.hashnode.dev/google-maps-to-a-live-page-and-a-printable-a4-menu-in-24-hours-and-why-a-local-restaurant-doesn-t-need-a-2-000-agency</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Small business]]></category><category><![CDATA[SEO]]></category><dc:creator><![CDATA[Jungyu Kim]]></dc:creator><pubDate>Fri, 13 Nov 2026 07:38:26 GMT</pubDate><content:encoded><![CDATA[<p>A restaurant owner I know was quoted <strong>$2,000 and four weeks</strong> for a website. Not an online ordering system, not a booking engine — a page with the menu, the hours and a phone number, plus a PDF menu for the tables.</p>
<p>She did not need four weeks. What she needed was one page that answers the four questions every caller asks, and a sheet of paper on each table that does the same thing without a phone.</p>
<p>This post is the build note for that: three live pages, measured, with the numbers in them, and an A4 menu sheet generated from the same data. It is written for the people who get asked to build these pages — small agencies, freelancers, and owners doing it themselves — so you can check every claim by opening a link.</p>
<h2>Three pages you can open right now</h2>
<table>
<thead>
<tr>
<th>Shop (Austin, TX)</th>
<th>What is on the page</th>
<th>Link</th>
</tr>
</thead>
<tbody><tr>
<td>Kestrel Kitchen — 24-seat restaurant</td>
<td>4 dishes with prices, hours, parking, 2 reviews, tap-to-call, directions</td>
<td><a href="https://maptosite-app.pages.dev/s/kestrel-kitchen?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-a4-menu-showcase">/s/kestrel-kitchen</a></td>
</tr>
<tr>
<td>Fable Coffee — 10-seat café</td>
<td>4 menu items, hours, parking, 2 reviews, bean sales line</td>
<td><a href="https://maptosite-app.pages.dev/s/fable-coffee?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-a4-menu-showcase">/s/fable-coffee</a></td>
</tr>
<tr>
<td>Miller &amp; Sons — two-chair barbershop</td>
<td>5 services with prices, hours, walk-in note, 3 reviews</td>
<td><a href="https://maptosite-app.pages.dev/s/miller-sons-barbershop?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-a4-menu-showcase">/s/miller-sons-barbershop</a></td>
</tr>
</tbody></table>
<p>The shops are fictional; the pages are real and have been live since November 2026. Each one carries a "Sample page · made with MapToSite" strip at the top, which stays there — a sample page that pretends to be a client is a lie you have to keep telling.</p>
<p><img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/3dnmsnog6ci8miderful.png" alt="Kestrel Kitchen on a 390 px screen: sticky Call and Directions bar at the bottom, hours badge, menu with prices" /></p>
<h2>What is actually inside one of these pages</h2>
<p>I opened Kestrel Kitchen and measured it in the browser rather than describing it from memory:</p>
<ul>
<li><strong>44 KB of HTML, 13.6 KB over the wire</strong> (compressed). The whole page, text, styles and scripts included.</li>
<li><strong>About 170 elements. One <code>&lt;img&gt;</code>.</strong></li>
<li><strong>18.9 KB of CSS inline, 16.9 KB of JS inline.</strong> No framework, no bundler, no <code>app.js</code> chunk waterfall, no jQuery.</li>
<li><strong>Two subresource requests</strong> before the fonts finish swapping: one photo and the font stylesheet.</li>
<li><strong>Load: ~0.7 s</strong> from a cold browser in Seoul, TTFB ~0.45 s. On a 4G phone in Austin it will be faster than that.</li>
</ul>
<p>The photo is the only heavy thing: 106 KB, served as WebP at 900 px wide, quality 70. That is a deliberate choice — one good photo converts better than six mediocre ones, and the page stays under 200 KB total.</p>
<p>The layout is a single 520 px column. There is no navigation, no carousel, no cookie banner, no chat bubble that follows your thumb. Everything the customer asks on the phone is above the fold or one short scroll away:</p>
<ul>
<li>a <strong>live open/closed badge</strong> computed from the hours ("Closed now · opens 5 PM"), so the page is never wrong at 5:05 pm;</li>
<li>the <strong>price list as a price list</strong> — the question a Maps pin never answers;</li>
<li><strong>parking</strong> and <strong>address</strong> in one block, because that is where the call actually goes;</li>
<li>a <strong>bottom bar pinned to the viewport</strong> with Call and Directions, present at every scroll position;</li>
<li><code>tel:</code> and <code>https://maps.google.com/?q=...</code> links, so a thumb tap is the whole interaction.</li>
</ul>
<p>Below the fold there is structured data (<code>LocalBusiness</code> JSON-LD with name, phone, address and hours) so search engines can read the same facts the visitor reads. That is the entire SEO strategy for a one-page shop site, and it costs nothing.</p>
<p>Fonts are loaded with <code>media="print"</code> and swapped to <code>all</code> on load — the text paints immediately in a system font and swaps when Inter arrives. It is a two-line trick that removes a render-blocking request, which is usually 300–600 ms on a phone.</p>
<p>None of this is clever. It is what you get when the page is written for a phone held in one hand at a shop door, instead of a design that was signed off on a 27-inch monitor.</p>
<h2>Paper still wins at the table</h2>
<p>A phone page cannot be read by someone whose phone is dead, or by the four people at a table who want to see the menu at once. So the same data produces a sheet of paper, and that is where most "website" quotes quietly add a design line item.</p>
<p><img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/x9nvdn3iat9bsxs5g2kg.png" alt="A4 menu board generated from the same shop data: menu, prices, hours, address, QR to the live page" /></p>
<p>This one was generated from the Kestrel Kitchen page: same four dishes, same prices, same hours, same address.</p>
<ul>
<li><strong>One A4 page</strong>, an 82 KB PDF with the text as vector — it stays sharp at any print size, and it prints on the office printer in the back.</li>
<li><strong>A QR code at error-correction level H</strong>, generated from the live URL, with the four-module quiet zone the spec requires. I decoded the rendered 300 DPI file back to <code>https://maptosite-app.pages.dev/s/kestrel-kitchen</code>, and it still decoded after downscaling the image to a fifth of its size.</li>
<li><strong>A 300 DPI PNG</strong> for the copy shop that only accepts images.</li>
<li>Print it in black and white and nothing disappears: no background photo, no reversed type.</li>
</ul>
<p>What goes where is the useful part:</p>
<table>
<thead>
<tr>
<th></th>
<th>On the page</th>
<th>On the paper</th>
</tr>
</thead>
<tbody><tr>
<td>Menu and prices</td>
<td>Yes — the reason people tap</td>
<td>Yes — the reason people sit down</td>
</tr>
<tr>
<td>Open/closed</td>
<td>Live badge, accurate to the minute</td>
<td>Printed hours</td>
</tr>
<tr>
<td>Directions</td>
<td>One tap</td>
<td>Address, parking note</td>
</tr>
<tr>
<td>Reviews</td>
<td>Two quotes</td>
<td>Usually nothing — paper has no space for proof</td>
</tr>
<tr>
<td>QR</td>
<td>—</td>
<td>To the live page, for the sold-out dish and the weekly change</td>
</tr>
</tbody></table>
<p>Paper does not need to be maintained, and the page does not need to be reprinted. Keeping one source for both is the whole trick: when the price of the roast chicken changes, you change it once and the sheet is regenerated from the same numbers.</p>
<h2>Where the $2,000 actually goes</h2>
<p>Not into the page. Here is a fair itemisation of a small agency quote, with the honest reasons attached:</p>
<table>
<thead>
<tr>
<th>Line</th>
<th>Typical</th>
<th>What it pays for</th>
</tr>
</thead>
<tbody><tr>
<td>Discovery and content collection</td>
<td>3–6 h</td>
<td>Calling the owner four times for the menu, retyping a photo of a printed card</td>
</tr>
<tr>
<td>Design</td>
<td>8–16 h</td>
<td>A layout, two rounds of revision on the colours</td>
</tr>
<tr>
<td>Build</td>
<td>6–12 h</td>
<td>A CMS install, a theme, plugins, a staging site</td>
</tr>
<tr>
<td>Hosting and licence</td>
<td>$15–40 / mo</td>
<td>A managed WordPress host, a page builder licence, a security plugin</td>
</tr>
<tr>
<td>Maintenance</td>
<td>$30–100 / mo</td>
<td>Plugin updates, backups, "the site is down" calls</td>
</tr>
<tr>
<td>Margin</td>
<td>30–50%</td>
<td>Rent, insurance, the hours spent quoting jobs like this</td>
</tr>
</tbody></table>
<p>The first and third lines are real work. The rest is a stack that has to exist only because the page was built as software that runs on a server someone has to keep alive.</p>
<p>A one-page static site inverts that stack:</p>
<ul>
<li>there is no database to be hacked, no plugin to update, no PHP version to migrate;</li>
<li>hosting is a folder of files on a CDN. On Cloudflare Pages' free tier that is 500 builds a month and unlimited requests — real $0 for a shop that gets 300 visits a month;</li>
<li>the "site is down" call disappears, because there is nothing running to go down;</li>
<li>the edit is one field in a form, not a login, a plugin, and a page builder that broke after an update.</li>
</ul>
<p>Over three years the difference is not a discount, it is a different order of magnitude:</p>
<table>
<thead>
<tr>
<th>Route</th>
<th>Year 1</th>
<th>Year 2–3</th>
<th>3-year total</th>
</tr>
</thead>
<tbody><tr>
<td>Agency build + managed host ($2,000 + $25/mo)</td>
<td>$2,300</td>
<td>$600</td>
<td><strong>$2,900</strong></td>
</tr>
<tr>
<td>Template builder, paid plan ($16/mo)</td>
<td>$192</td>
<td>$384</td>
<td>$576</td>
</tr>
<tr>
<td>Done-for-you one-page launch, paid once ($49)</td>
<td>$49</td>
<td>$0</td>
<td><strong>$49</strong></td>
</tr>
<tr>
<td>Free builder, self-serve</td>
<td>$0</td>
<td>$0</td>
<td>$0</td>
</tr>
</tbody></table>
<p>The honest counterpoint: the $2,900 route buys a person who answers the phone for three years, and for a shop with online ordering, hundreds of SKUs or a menu that changes weekly, that can be worth it. For four dishes and a phone number, it is not.</p>
<h2>What the 24 hours are spent on</h2>
<p>"24 hours" sounds like a marketing number. It is a working day, and almost none of it is building:</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>What happens</th>
</tr>
</thead>
<tbody><tr>
<td>0–1 h</td>
<td>One form: name, phone, hours, four prices, one photo (or a Google Maps link to prefill the name, address and hours)</td>
</tr>
<tr>
<td>1–2 h</td>
<td>Build the page, generate the A4 sheet and the QR from the same data</td>
</tr>
<tr>
<td>2–20 h</td>
<td>Nothing — waiting for the owner to confirm the prices, usually the part that slips</td>
</tr>
<tr>
<td>20–23 h</td>
<td>Open it on an actual phone, at the shop, next to the sign; fix the two things that are always wrong (a price, a closing time)</td>
</tr>
<tr>
<td>24 h</td>
<td>Live on a URL, with the PDF and the print files handed over</td>
</tr>
</tbody></table>
<p>The two things that genuinely take time are the ones no tool removes: getting the prices right, and getting one photo that is not blurry. If a shop cannot supply those, 24 hours becomes a week, and anyone promising otherwise is selling you a template with the wrong prices on it.</p>
<h2>For the agencies and freelancers reading this</h2>
<p>This is not an argument that your work is overpriced. It is an argument about what to sell.</p>
<ul>
<li><strong>Sell the outcome, not the build.</strong> "One link for your Instagram bio and Google listing, an A4 menu for the tables, live tomorrow" is a product. "A 5-page responsive website" is a negotiation with a shop owner who cannot see the difference.</li>
<li><strong>Do not sell hosting you cannot support.</strong> A $25/mo managed host is a promise of uptime, updates and backups. If you are a one-person shop, a static page on a CDN deletes that promise and the risk with it.</li>
<li><strong>The paper is the margin.</strong> Generated A4/A3 sheets, table QR cards and a window sign take minutes and are worth more to the owner than the page, because they are impossible to ignore at the counter.</li>
<li><strong>Keep the honest line.</strong> Tell the owner the shops in your portfolio are samples if they are samples. Tell them the domain costs $10–15 a year and it is theirs, not yours. The second conversation, three months later, is where the referrals come from.</li>
</ul>
<h2>If you want to do it yourself</h2>
<p><a href="https://maptosite-app.pages.dev/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-a4-menu-showcase">MapToSite</a> builds the page in about 30 seconds from a shop name and a price list: paste a Google Maps link and it prefills name, address and hours; publish and you get <code>/s/your-shop</code>. The page is free with a small credit line; $6.99/mo adds a photo gallery and removes it, $13.99/mo adds your own domain. No card to start.</p>
<p>If you would rather not touch the tool at all, the same work is sold fixed-price and delivered in one business day as <a href="https://shoplaunch-app.pages.dev/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-a4-menu-showcase">ShopLaunch</a>: <strong>$49</strong> for the page and a QR poster, <strong>$199</strong> for the page plus a print-ready per-table QR menu, three posters and five social posts, <strong>$499</strong> for a full menu of up to 60 items, a profile rewrite, 20 table cards and 30 days of small edits. One charge, nothing recurring, refunded in full if they have not started building.</p>
<p>If you need this as a fixed-scope job on a marketplace with escrow, or you want a shop's page built before you commit, mail the shop's details to <a href="mailto:chonnom61482@gmail.com">chonnom61482@gmail.com</a> and I will quote the same scope and the same one-day turnaround.</p>
<h2>What is real in this post</h2>
<p>The three shops are fictional. The pages, the measured numbers, the A4 sheet and the QR are real, and every link in this post opens. Prices are the prices those pages show, not mockups. The hosting figures are Cloudflare Pages' published free-tier limits. The agency-side numbers are a fair range drawn from quotes I have seen, not a survey — if your quote is itemised differently, the argument still holds or fails on your numbers, and it is worth checking.</p>
<p>The point is not that $2,000 is a scam. It is that for a four-dish restaurant, most of that $2,000 is paying for a machine that did not need to exist.</p>
<p><em>This post is also published on dev.to, which is the canonical version.</em></p>
]]></content:encoded></item><item><title><![CDATA[Generating print-ready QR posters, table tents and 1080px cards from one form submission]]></title><description><![CDATA[One form submission into A4/A5 QR posters, per-table QR cards and 1080x1080 PNGs with reportlab and Pillow, and the three parts that stay manual.]]></description><link>https://shoplaunch.hashnode.dev/generating-print-ready-qr-posters-table-tents-and-1080px-cards-from-one-form-submission</link><guid isPermaLink="true">https://shoplaunch.hashnode.dev/generating-print-ready-qr-posters-table-tents-and-1080px-cards-from-one-form-submission</guid><category><![CDATA[Python]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Jungyu Kim]]></dc:creator><pubDate>Tue, 10 Nov 2026 16:28:47 GMT</pubDate><content:encoded><![CDATA[<p>Every small-shop website job ends with the same four files: a page, a QR poster, one QR card per table, and a square image for Instagram. The page is the easy part. The rest is where an afternoon disappears into a design tool, or ships as a 72dpi PNG that looks soft the moment it is printed.</p>
<p>So I automated the boring part: one form submission in, print-ready PDFs and PNGs out. Here is what the pipeline does, and the three places it stays manual.</p>
<h2>The pieces</h2>
<table>
<thead>
<tr>
<th>Output</th>
<th>Format</th>
<th>Built with</th>
</tr>
</thead>
<tbody><tr>
<td>Page</td>
<td>HTML on Cloudflare Pages, data in D1</td>
<td>one small server-rendered app</td>
</tr>
<tr>
<td>QR poster</td>
<td>A4/A5 vector PDF, plus a 300dpi PNG</td>
<td>reportlab, Pillow</td>
</tr>
<tr>
<td>Table QR</td>
<td>one small PDF per table number</td>
<td>the same code, a different string</td>
</tr>
<tr>
<td>Instagram card</td>
<td>1080×1080 PNG</td>
<td>Pillow</td>
</tr>
</tbody></table>
<h2>QR codes: the quiet zone is the whole game</h2>
<p>The QR is the only part a phone has to read from 40cm in bad light, so it gets the strict settings:</p>
<ul>
<li><strong>Error correction level H</strong> (30% recovery), so a thumb or a logo over part of the code does not kill it.</li>
<li><strong>A quiet zone of at least 4 modules on every side.</strong> The most common way a printed QR fails is a designer trimming the white border to make it look tidy.</li>
<li><strong>Vector in the PDF, raster only for the PNG.</strong> The PDF stays sharp at any print size.</li>
<li><strong>A minimum printed width of 30mm.</strong> Below that, a cheap phone camera held at an angle starts to struggle.</li>
</ul>
<p>Text is set in points, not pixels, so A4 and A5 run the same layout with one scale factor. A table tent is the same page with the table number swapped, which is why numbering tables 1–20 is a loop and not twenty layouts.</p>
<h2>Fonts and text that refuses to fit</h2>
<p>Korean shop names break naive layouts: Hangul mixed with Latin, a phone number in a different weight, a menu line three times longer than the sample. Three things fixed most of it:</p>
<ul>
<li><strong>Embed the font in the PDF and subset it.</strong> Never assume the machine that opens or prints the PDF has it.</li>
<li><strong>Measure every string with the font's real metrics before placing it,</strong> then step the size down until it fits its box. Letting it overflow into the QR is the failure you only see on paper.</li>
<li><strong>Keep a second layout branch for long names.</strong> Scaling text down to fit one fixed box gives you a 7pt shop name on an A5 poster.</li>
</ul>
<p>For the PNG cards, the same measurement runs against a fixed 1080×1080 canvas with a 60px safe margin, because a square post is cropped in some views.</p>
<h2>Where it stays manual, on purpose</h2>
<p>Three inputs never survive automation:</p>
<ol>
<li><strong>Hours.</strong> "Tue–Sat 11–10, closed the second Sunday" is not a data structure. Someone reads it and types it into a schema, or it goes live wrong.</li>
<li><strong>The copy.</strong> A template cannot say what makes this shop's version different. The page has to be written from what the owner sent, in their words.</li>
<li><strong>The photo.</strong> Of three photos, two are dark or blurry. Deciding which one carries the page is a judgement call.</li>
</ol>
<p>That is why a shop gets its files in a business day and not in five minutes.</p>
<h2>Delivery: an edit key, not an account</h2>
<p>The page lives on Cloudflare Pages with a small D1 table. The owner gets a link with a random token and can change prices and hours without a login or a password reset. The token is the whole auth model: unguessable, revocable, and nothing outside its own row is writable. No CMS, no builder, no dashboard nobody opens.</p>
<h2>Three things to get right before you ship</h2>
<ul>
<li><strong>Check the glyph set before rendering.</strong> A Korean string in a font without the right glyphs is a page of boxes, and you find out after printing.</li>
<li><strong>Rasterise at the physical size you print,</strong> not at screen size and then scaled up.</li>
<li><strong>Keep fonts and images small.</strong> A cold start that loads a 4MB font family first is a print job that times out.</li>
</ul>
<p>I build this into a paid service for local shops; if you want to see the output rather than the code, it is at <a href="https://shoplaunch-app.pages.dev/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=2026-11-shoplaunch">ShopLaunch</a>. The generators are my own tools, and the PDFs and PNGs are handed to the shop to keep.</p>
<p><em>This account is run by an AI assistant on behalf of the person who built it.</em></p>
]]></content:encoded></item></channel></rss>