Generate dynamic, embeddable SVG website preview badges from a URL in seconds.
Note
This repository implements a website preview badge service (not a general-purpose logging framework). The README below documents the current production behavior and architecture of this codebase.
- Features
- Tech Stack \& Architecture
- Getting Started
- Testing
- Deployment
- Usage
- Configuration
- License
- Contacts \& Community Support
- URL-to-SVG rendering pipeline for generating rich link preview cards.
- Dual API design:
GET /infofor normalized metadata (title, screenshot URL, canonical URL).GET /badgefor generatedimage/svg+xmloutput.
- Automatic website title extraction via HTML parsing (
<title>fallback logic). - Screenshot sourcing through WordPress
mshotsendpoint for remote page previews. - Customizable visual output controls:
- Width, optional fixed height, border radius, border width/color.
- Title text color/opacity and title plate color/opacity.
- Title placement modes:
overlay_top,overlay_bottom,outside_top,outside_bottom. - Image scale and X/Y offsets for framing adjustments.
- Optional custom title override.
- Optional thumbnail embedding as Base64 data URI for self-contained SVGs.
- Frontend generator UI with live controls, i18n support, and copy-ready embed snippets.
- Multi-format output snippets: Markdown, HTML, and raw URL.
- Safe query handling with typed parsing and value clamping (
safe_int,safe_float). - Cross-origin friendly response headers for API consumption.
- Short-term cache headers on badge responses for CDN/browser efficiency.
Tip
Use the custom_title query parameter when target pages have weak or noisy <title> metadata.
- Backend: Python + Flask
- HTTP/Data:
requests,urllib.parse,urllib.request - HTML Parsing:
beautifulsoup4 - Rendering: Programmatic SVG composition in pure Python
- Frontend: Vanilla HTML/CSS/JavaScript (ES modules)
- Localization: File-based JS locale dictionaries under
i18n/locales - Deployment Target: Serverless-compatible Python API routes (Vercel config present)
Expand full repository tree
site-preview/
โโโ api/
โ โโโ card.py # SVG composition + optional thumbnail embedding
โ โโโ index.py # Flask app, static routes, /info and /badge APIs
โโโ i18n/
โ โโโ index.js # Translation exports
โ โโโ keys.js # Shared i18n keys
โ โโโ locales/
โ โโโ cs.js
โ โโโ de.js
โ โโโ en.js
โ โโโ es.js
โ โโโ fr.js
โ โโโ ja.js
โ โโโ kk.js
โ โโโ nl.js
โ โโโ pl.js
โ โโโ pt.js
โ โโโ ru.js
โ โโโ sv.js
โ โโโ uk.js
โ โโโ zh.js
โโโ app.js # UI behavior and request orchestration
โโโ index.html # UI shell
โโโ styles.css # UI styles
โโโ requirements.txt # Python dependencies
โโโ vercel.json # Hosting/runtime config
โโโ LICENSE
- Stateless generation: Card rendering is request-time and deterministic from query parameters.
- Pure-SVG output: Ensures embed compatibility in README/platform contexts.
- Resilience-first metadata lookup: Fail-soft behavior on title fetch/screenshot failure.
- Parameter guardrails: Width, radius, opacities, and similar values are range-constrained.
- Frontend/backend contract: UI maps one-to-one with
/badgequery parameters.
Request/data flow diagram
flowchart LR
A[User enters target URL in UI] --> B[GET /info?url=...]
B --> C[fetch_website_info]
C --> D[requests + BeautifulSoup title extraction]
C --> E[Construct mshots screenshot URL]
D --> F[Return JSON title + image_url + normalized url]
E --> F
F --> G[UI composes /badge query with visual settings]
G --> H[GET /badge?...]
H --> I[Parameter parsing + clamping]
I --> J[generate_svg in api/card.py]
J --> K[Optional fetch_image_as_base64]
J --> L[SVG assembly]
L --> M[image/svg+xml response]
Important
The screenshot source is an external service dependency. Production reliability and latency are partly coupled to external screenshot availability.
- Python
3.10+(recommended3.11+) pipfor dependency installation- Network egress for remote metadata/screenshot fetches
git clone https://github.com/readme-SVG/readme-SVG-site-preview.git
cd readme-SVG-site-preview
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtRun locally with Flask:
export FLASK_APP=api/index.py
export FLASK_ENV=development
flask run --host 0.0.0.0 --port 5000Open: http://localhost:5000
Troubleshooting and source-build notes
-
ModuleNotFoundError- Confirm virtualenv is activated.
- Re-run
pip install -r requirements.txt.
-
Timeouts on remote sites
- Some target websites block automated fetchers or respond slowly.
- Retry with a different URL and verify outbound connectivity.
-
SVG displays "No Image"
- The screenshot endpoint may not yet have generated a shot.
- Wait and refresh; UI already triggers short auto-refresh cycles.
-
Locale assets not loading
- Ensure
/i18n/<path>route is reachable in your runtime.
- Ensure
python -m flask --app api/index.py run --host 0.0.0.0 --port 5000Warning
This repository currently has no committed unit/integration test suite. Use the checks below as baseline validation until formal tests are added.
Recommended local checks:
python -m compileall api
python -m flask --app api/index.py run --port 5000
curl "http://127.0.0.1:5000/info?url=https://example.com"
curl -I "http://127.0.0.1:5000/badge?url=https://example.com"Suggested future automation:
- Unit test parameter guards (
safe_int,safe_float). - Snapshot/semantic tests for generated SVG fragments.
- Integration tests for
/infoand/badgeresponse contracts.
- Keep runtime stateless and horizontally scalable.
- Place API behind CDN where possible to cache badge responses.
- Ensure egress is allowed to target websites and screenshot source.
- Configure observability and rate limiting at edge/proxy layer.
- Build image/runtime with locked dependencies.
- Enforce request timeout ceilings and upstream retry policy.
- Add reverse-proxy cache for
/badgeresponses. - Configure CORS policy according to your consumer surface.
Containerization baseline (example)
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "-m", "flask", "--app", "api/index.py", "run", "--host=0.0.0.0", "--port=5000"]services:
site-preview:
build: .
ports:
- "5000:5000"
restart: unless-stoppedGenerate a default preview badge:
curl "http://127.0.0.1:5000/badge?url=https://example.com" -o badge.svgEmbed in Markdown:
[](https://example.com)Embed in HTML:
<a href="https://example.com" target="_blank" rel="noopener">
<img
src="https://your-host/badge?url=https%3A%2F%2Fexample.com"
alt="Website preview"
/>
</a>Advanced Usage
curl "http://127.0.0.1:5000/badge?url=https://example.com&width=480&height=270&radius=12&border_width=2&border_color=3b82f6&title_color=ffffff&title_opacity=1&plate_color=111827&plate_opacity=0.72&title_position=overlay_bottom&image_scale=1.15&image_offset_x=0&image_offset_y=-8&custom_title=Production%20Landing%20Page"overlay_topoverlay_bottomoutside_topoutside_bottom
- Remote site responds but has empty/invalid
<title>. - Screenshot unavailable or delayed.
- Aggressive query values (negative/overflow) are clamped.
- Very long titles are wrapped and truncated to max lines.
Runtime behavior is controlled primarily through query parameters.
| Parameter | Type | Default | Range / Allowed | Description |
|---|---|---|---|---|
url |
string | required | valid URL | Target website to render. |
width |
int | 320 |
200..1000 |
Card width in px. |
height |
int | 0 |
>=0 |
Card total height in px (0 = auto). |
radius |
int | 10 |
0..30 |
Card border radius. |
title_color |
hex string | ffffff |
hex | Title text color. |
title_opacity |
float | 1.0 |
0.0..1.0 |
Title text alpha. |
plate_color |
hex string | 0f1117 |
hex | Title plate color. |
plate_opacity |
float | 0.78 |
0.0..1.0 |
Title plate alpha. |
title_position |
enum | overlay_bottom |
position enums | Where title plate renders. |
border_width |
int | 1 |
0..10 |
Outer stroke width. |
border_color |
hex string | ffffff |
hex | Outer stroke color. |
image_scale |
float | 1.0 |
>=0.1 |
Screenshot zoom factor. |
image_offset_x |
int | 0 |
integer | Horizontal pan. |
image_offset_y |
int | 0 |
integer | Vertical pan. |
custom_title |
string | empty | free text | Overrides fetched page title. |
embed |
bool-like | true |
true/false |
Inline embed screenshot as data URI. |
Caution
With embed=true, SVG payload size may increase significantly because raster image bytes are base64-embedded.
Reference request templates
GET /info?url=https://example.comGET /badge?url=https://example.comGET /badge?url=https://example.com&width=480&height=270&radius=12&title_color=ffffff&title_opacity=1&plate_color=0f1117&plate_opacity=0.78&title_position=overlay_bottom&border_width=2&border_color=ffffff&image_scale=1.05&image_offset_x=0&image_offset_y=0&custom_title=My%20Service&embed=trueThis project is distributed under the MIT License. See LICENSE for full terms.
If you find this tool useful, consider leaving a star on GitHub or supporting the author directly.