Template
Scanwell
A single-screen QR code studio that encodes nine content types (links, text, Wi-Fi, email, SMS, and hosted files such as images, PDFs and audio) and lets the user style and download the result. It can serve as a free lead-magnet tool for a business, or as a white-label utility for agencies and small SaaS builders.
Branded QR code studio · App · Small tools and apps · front-end app (local state)
Who it is for
- Solo founders offering a free marketing tool
- Shops and restaurants printing codes for signage and menus
- Agencies white-labelling a generator for clients
- Makers of micro-SaaS products
Layout
- Three-pane workspace, no global header
- Left pane: 'Select QR type' vertical list of nine large tiles, each with a pastel illustrated thumbnail and label; active tile tinted indigo with border
- Center card: live preview with large QR on a warm paper gradient, pill 'Download QR code' button + copy icon, then the type-specific input form (e.g. Website URL) and helper text
- Right pane: 'Style your QR' with Theme cards (Paper/Midnight/Pastel), Pattern tiles (5 shapes), QR color and background swatch rows plus custom picker, Download Size segmented cards (Social/Card/Print)
- Mobile: collapses to a single column with a dropdown type selector
Palette
clean, playful, tactile, approachable, crafted. A tidy tool with soft pastel thumbnails that make a utility feel designed.
- background
#fafafa - card
#ffffff - text
#262626 - accent / focus blue
#256af4 - selected tint (indigo wash)
#eef2ff - QR ink (warm brown)
#3b2f26 - border
#e6e6e6
Every checked pair, measured again
| Sample | Where | Ratio | Needs |
|---|---|---|---|
| Aa | body text on background | 14.50:1 | 4.5:1 |
| Aa | text on card | 15.13:1 | 4.5:1 |
| Aa | helper text on card | 4.74:1 | 4.5:1 |
| Aa | text on selected indigo wash | 13.53:1 | 4.5:1 |
| Aa | white label on accent button | 4.72:1 | 4.5:1 |
| focus ring on white | 4.72:1 | 3:1 | |
| focus ring / selected border on indigo wash | 4.22:1 | 3:1 | |
| input and tile border on card | 3.41:1 | 3:1 | |
| Aa | QR ink on paper gradient start | 11.37:1 | 4.5:1 |
| Aa | QR ink on paper gradient end | 10.49:1 | 4.5:1 |
As vibld’s tokens
The palette on the fifteen colour tokens vibld styles a project with, each text colour on the fill it is read on. Marked tokens are solved from the palette, because no swatch held that role at 4.5:1.
- background
- card
- muted
- primary
- secondary
- accent
- destructive *
Type
- Display
- Satoshi Medium 20px section titles, sentence case (Satoshi is not on Google Fonts; a vibld project loads Plus Jakarta Sans)
- Body
- Satoshi 16px body, 14px tile labels and helper text (Satoshi is not on Google Fonts; a vibld project loads Plus Jakarta Sans)
One friendly geometric sans; hierarchy is minimal because the QR itself is the hero.
Spacing and imagery
Three columns (~270 / 450 / 380px) with wide gutters; center card 24px radius with soft shadow; option tiles 12px radius; comfortable 16-24px padding.
Pastel 3D-ish mini illustrations for each QR type (browser window, Wi-Fi card, envelope, chat bubble, landscape, PDF, music note, phone); theme swatches as mesh-gradient orbs; pattern previews as tiny QR fragments.
Components
- QR type selector list with illustrated tiles
- Live preview card
- Themed QR canvas
- Download pill button with state feedback
- Copy-to-clipboard icon button
- Type-specific forms (URL, text, Wi-Fi, email, SMS, file upload, app store)
- Theme preset cards
- Pattern shape tiles
- Color swatch rows + custom hex picker
- Download size segmented cards
- File dropzone for image/PDF/MP3
- Toasts
Interactions
- QR re-renders live as the user types
- Switching type preserves each type's own inputs
- Selected tile/card gets indigo wash and border
- Download button disabled until content is valid, then shows success check
- Copy image to clipboard with toast
- Custom color picker popover
- File upload with progress, type and size validation
- Responsive collapse to dropdown on mobile
Data
QrState{type, contentByType: Record<type, fields>, theme, pattern, fg, bg, size}WifiFields{ssid, password, security WPA|WEP|nopass, hidden}EmailFields{to, subject, body}SmsFields{phone, message}UploadedAsset{id, path, mime, bytes, public_url, created_at} (storage)UploadLog{ip_hash, created_at} for rate limiting
Build prompt
The baseline every prompt in the catalog assumes, then this design’s own ten sections, from goal to guardrails.
The baseline
### How to use these prompts Paste a template's build prompt into your coding agent as the first message. Each prompt names its own stack, tokens and acceptance criteria; the rules below apply to all of them and can be prepended once per project. ### Engineering baseline - TypeScript strict mode, no `any`, small typed components, feature folders, and one source of truth for design tokens (CSS variables consumed by Tailwind). - Validate every input with a shared zod schema on the client and again on the server or edge function. Never trust client-side checks alone. - Show loading, empty and error states for every async view. Surface errors in plain language with a retry, and log details to the console in development only. - Keep secrets out of the bundle. Only publishable keys (for example a Supabase anon key) belong in client code; service-role keys, API keys and webhooks live in server or edge-function environment variables. ### Data and auth baseline (full-stack templates) - Enable Row Level Security on every table before inserting data. Default-deny, then add owner-scoped policies (`auth.uid() = user_id`) and explicit role checks for admin views. - Store roles in a separate table checked by a security-definer function, never in a user-editable profile field. - Upload files to private storage buckets with size and MIME limits, and serve them through signed URLs. - Rate-limit public endpoints (forms, auth, AI calls) and add a honeypot field or captcha to anonymous forms. - Take payments through a hosted checkout and verify webhooks by signature. Never handle raw card data. ### Accessibility and UX baseline - Target WCAG 2.2 AA: 4.5:1 contrast for normal text and 3:1 for large text, input borders, focus rings and meaningful icons or chart lines. Every palette in this catalog lists its verified pairs; re-check with a contrast tool after any colour change. - Keep body text at 16px or larger with 1.5 line height, nothing below 12px, no light weights under 24px, and uppercase only for short labels. - Give every interactive element a visible focus ring, full keyboard support, semantic landmarks, labelled form fields, and alt text on meaningful images. - Respect `prefers-reduced-motion` for every animation. Give drag-and-drop and carousels keyboard and button alternatives. - Build mobile-first and test at 375px, 768px and 1280px. ### Content guardrails - Use original copy, fictional sample data and placeholder or licensed imagery. Do not reuse another product's name, logo, screenshots or marketing text. - Label demo testimonials and metrics as samples. Collect the minimum personal data the feature needs.
### Goal Build **Scanwell**, a one-page QR studio that a small business can put on its site as a free tool. Visitors pick a content type, fill in a short form, style the code, and download a crisp PNG. ### Stack React 18 + TypeScript + Vite, Tailwind CSS, shadcn/ui (Radix: ToggleGroup, Popover, Select, Tabs) and lucide-react. Use `qr-code-styling` (or `qrcode` plus custom SVG rendering) for dot patterns, react-colorful for the custom picker, react-hook-form + zod for each type's form, react-dropzone for uploads, and Sonner for toasts. Use Supabase only for file types (image, PDF, audio): a Storage bucket and an Edge Function that validates the upload, rate-limits it and returns a public URL to encode. Everything else runs client-side. ### Pages & layout One route with three panes: 1. **Left: type list**: nine large tiles (URL, Text, Wi-Fi, Email, SMS, Image, PDF, Audio, App link), each with a soft pastel thumbnail and label; the selected tile gets an indigo wash. 2. **Center: preview card**: the QR on a themed square, a pill Download button with a copy icon next to it, then the form for the active type with a helper line. 3. **Right: style panel**: theme cards (Paper, Midnight, Pastel), five pattern tiles, foreground and background swatch rows with a custom picker, and output size cards (500 / 800 / 1200 px). Below 1024px, stack the panes and turn the type list into a Select. ### Design system - Tokens: `--background: #fafafa`, `--card: #ffffff`, `--foreground: #262626`, `--accent: #256af4`, `--selected: #eef2ff`, `--border: #e6e6e6`, `--input-border: #8b8b8b`, `--muted-fg: #737373`, default ink `#3b2f26` on a paper gradient (`#f7efe4` to `#efe6da`). - Font: Satoshi or General Sans from Fontshare, or Plus Jakarta Sans from Google Fonts as the Google-only alternative. Titles 20px medium, body 16px (line-height 1.5), labels 14px, nothing below 12px. - Spacing on a 4px base; pane gap 48px; card padding 24px. - Radius: preview card 24px, tiles 12px, pills fully rounded. - Shadows: preview card `0 12px 32px -12px rgb(0 0 0 / .18)`. - Motion: 150ms color/border transitions, and a 200ms crossfade when the QR regenerates. - Light UI by default; the Midnight theme only restyles the QR canvas. ### Components & interactions TypeTile (hover, selected, focus), PreviewCanvas (placeholder "Enter content to generate", error state for over-long payloads), DownloadButton (disabled, loading, success check), CopyButton (clipboard API with fallback), per-type forms (URL normalization, Wi-Fi escaping of `;,:\`), UploadDropzone (drag-over highlight, progress, rejected-file message), ThemeCard, PatternTile, SwatchRow, CustomColorPopover, SizeSelector. Keep each type's inputs in its own slice so switching types loses nothing. Warn when foreground/background contrast is too low to scan reliably. ### Data & state Client: a single typed reducer `QrState` persisted to `localStorage`. Server: bucket `qr-uploads` with object metadata; table `upload_log(ip_hash, created_at)` for the rate limiter. Always encode with error-correction level H. Use example.com URLs and fictional Wi-Fi names in placeholders. ### Accessibility Build the type list and theme, pattern and size groups as radio groups with arrow-key navigation. Swatches need `aria-label`s with color names, and selection must be shown by more than color (a check icon). Keep `#256af4` focus rings visible on white and on the selected wash; tile and input borders use `--input-border` so boundaries hold 3:1. Give the QR image alt text describing its content type. Every form field gets a visible label, and errors are linked with `aria-describedby`. Verified contrast: body: #262626 on #fafafa = 14.5:1; helper: #737373 on #ffffff = 4.7:1; white on accent: #ffffff on #256af4 = 4.7:1; QR ink on paper: #3b2f26 on #f7efe4 = 11.4:1. ### Security The upload Edge Function checks MIME type and magic bytes for PNG/JPEG/WebP/PDF/MP3, enforces a 10 MB cap, randomizes object names and allows 15 uploads per IP hash per hour. Validate all fields with zod on the client and again in the function. Only accept `https:` or `http:` URLs, never `javascript:` or `data:`. Keep the service key in the function environment only. Expire old files with a bucket lifecycle policy. ### Performance & SEO Debounce regeneration by 100ms, lazy-load the color picker and dropzone, and render the preview as SVG with PNG rasterized only on download. Add title, meta description and OG image, and put a single H1 in the visually hidden page heading. Target Lighthouse 95+. ### Guardrails - Write fresh copy and your own thumbnail illustrations; no app store or brand logos. - Store uploads only as long as needed, and never log payload contents. - Keep components small and typed; no `any`. Show upload and generation errors visibly. - Done when: (1) all nine types produce codes that scan on iOS and Android; (2) switching types preserves inputs; (3) the three sizes download at the exact pixel dimensions; (4) invalid or oversized files are rejected server-side; (5) the whole flow works by keyboard.