A printable, single-file HTML template for creating personalized Christmas gift lists.
- Single-file design β Each HTML file is completely self-contained (no external dependencies)
- Printable β Optimized layout for printing or saving as PDF
- QR codes β Automatically generated QR codes for easy sharing
- Customizable β Change colors, add your own items, personalize for each recipient
- Cloudflare Pages ready β Designed to work seamlessly with Cloudflare Pages and custom domains
- CI/CD ready β Automated QR generation workflows
This is the easiest way to get started. GitHub Actions will handle QR code generation automatically!
-
Fork this repository (or use as template)
-
Configure your domain (Important!):
- Go to your repo β Settings β Secrets and variables β Actions
- Option A (Recommended for private repos): Secrets tab β New repository secret
- Option B (Easier for public repos): Variables tab β New repository variable
- Name:
ROOT_DOMAIN - Value: Your deployment URL (e.g.,
https://yourname.github.io/SimpleWishorhttps://yourdomain.com) - Note: Secrets take priority over variables if both are set
- Note: Secrets take priority over variables if both are set. The workflow used to generate QR SVGs sets
ROOT_DOMAINwith the following precedence: default (https://example.com) -> repository variableROOT_DOMAIN-> secretROOT_DOMAIN(secret overwrites variable).
- Note: Secrets take priority over variables if both are set. The workflow used to generate QR SVGs sets
-
Clone your fork:
git clone https://github.com/YOUR-USERNAME/SimpleWish.git cd SimpleWish -
Create personalized lists (recommended)
The project now supports generating per-recipient HTML from simple JSON files. Add one JSON file per person to the
recipientsdirectory. The CI build will generate HTML pages and inject QR SVGs at deploy time (generated files are not committed to the repo).See the included
elsa.htmlfile for a ready-made example. -
Commit and push the source (NOT generated HTML)
git add recipients/*.json git commit -m "Add recipient JSON files" git push
-
Deploy (choose one)
-
Cloudflare Pages (Recommended):
- Connect your repo to Cloudflare Pages through the web GUI.
- Build command (paste into the Pages web GUI build command):
./setup.sh --build
- Output directory:
public
The build script automatically detects Cloudflare Pages environment variables:
- Auto-detection: When running in Cloudflare Pages, the script automatically uses
CF_PAGES_URLfor QR code generation - Custom domain override: You can override by setting
ROOT_DOMAINenvironment variable in Cloudflare Pages settings
Example with custom domain override:
ROOT_DOMAIN="https://yourcustomdomain.com" ./setup.sh --buildThe build command (
setup.sh --build) automatically handles:-
Setting up Python virtual environment
-
Installing dependencies
-
Generating HTML from recipient JSON files
-
Creating QR codes with your domain (auto-detected or custom)
-
Injecting QR codes into HTML files
-
Preparing the
public/directory for deployment -
Why this matters: Cloudflare Pages will deploy only what you put in
public/, so generating and injecting pages in the build step keeps the repository free of generated artifacts and preserves privacy/security of scripts and configs.
-
GitHub Pages (Optional):
- Use the repository Actions workflow: go to Actions β "Deploy to GitHub Pages (Optional)" β Run workflow.
- The workflow will generate per-recipient HTML from
recipients/*.json, run QR generation and injection, and upload the producedpublic/folder to GitHub Pages.
-
That's it! The Cloudflare/GitHub workflows will automatically:
- Generate per-recipient HTML from
recipients/*.jsonusingindex.htmlas the template - Generate decorated QR SVGs for each generated page and inject them between the QR markers
- Upload the generated
public/folder to your Pages provider (Cloudflare Pages or GitHub Pages)
For contributors or advanced users who want to run scripts locally:
# Clone the repository
git clone https://github.com/YOUR-USERNAME/SimpleWish.git
cd SimpleWish
# Run the setup script (Linux/Mac)
./setup.sh
# Or manually set up (all platforms)
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r scripts/requirements.txt
pip install -r scripts/requirements-dev.txt
# Run tests to verify
python3 -m pytest
python3 -m flake8index.htmlβ Base template with classic green themeelsa.htmlβ Example with blue theme and tech-focused gifts
When creating a list for someone (e.g., elsa.html):
-
Update the title and header:
<title>Elsa's Christmas List</title> <h1 id="recipient">Christmas List for Elsa</h1>
-
Add gift ideas:
<ul id="gift-list" class="gift-list"> <li><a href="https://example.com/item" target="_blank" rel="noopener">Item description</a></li> </ul>
-
Customize colors (optional) - See "Customizing Colors" section below
You can easily customize the page theme by editing the CSS variables at the top of the <style> section. Look for the clearly marked customization section:
Page Theme Colors (edit in <style> section):
/* π¨ CUSTOMIZE YOUR THEME HERE - Change these color values! */
:root{
--bg:#f6f8fb; /* Page background color */
--card:#fff; /* Card/paper background */
--accent:#1565C0; /* Accent color (headings, links) */
--muted:#546E7A; /* Muted text (subtitles, hints) */
...
}QR Code Customization (via meta tags in <head> section):
<!-- QR Code Colors -->
<meta name="qr-foreground-color" content="#1565C0">
<meta name="qr-background-color" content="#E3F2FD">
<!-- QR Decoration (true/false) -->
<meta name="qr-decorate" content="true">
<!-- Decoration Type (choose one) -->
<meta name="qr-decoration-type" content="tree">
<!-- Options: tree, snowman, santa, gift, star, candy-cane, bell -->
<!-- Tree Style (only applies to tree decoration) -->
<meta name="qr-tree-style" content="fancy">
<!-- Options: fancy (with ornaments), plain (simple) -->Available Decoration Types:
- π tree - Christmas tree (default) - supports
fancyandplainstyles - β snowman - Classic snowman with hat and scarf
- π santa - Santa Claus face
- π gift - Wrapped present with bow
- β star - Decorative Christmas star
- π¬ candy-cane - Striped candy cane
- π bell - Christmas bell with ribbon
Example: The elsa.html file uses a blue theme and a snowman:
- Page CSS:
--accent:#1565C0(blue for headings/links) - QR Code Color:
<meta name="qr-foreground-color" content="#1565C0"> - Snowman Code:
<meta name="qr-decoration-type" content="snowman">
Color Palette Ideas:
- π΄ Classic Red:
#b71c1c(default) - π΅ Tech Blue:
#1565C0(elsa.html example) - π Forest Green:
#2e7d32 - π Royal Purple:
#6a1b9a - π§‘ Warm Orange:
#e65100
Where to Edit:
- Open your HTML file in any text editor
- Find the
<style>section near the top (line ~16) - Look for the comment:
/* π¨ CUSTOMIZE YOUR THEME HERE */ - Change the color hex codes to your preferred colors
- Optionally update QR code colors in the
<meta>tags
- Open the HTML file in your browser
- Use Print (Ctrl+P / Cmd+P) or "Save as PDF"
- The layout is optimized for printing with:
- QR code pinned to top-right
- Footer fixed at bottom
- Clean, minimal design
See CONTRIBUTING.md for guidelines on how to contribute to this project.
This project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0) - see the LICENSE file for details.
Key Terms:
- β Free to use for personal, educational, and non-commercial purposes
- β Can modify and adapt the code
- β Must include attribution to this repository
- β Share modifications under the same license
- β NO commercial use or profit-making without permission
Commercial Use: If you want to sell this software, use it commercially, or make profit from it, you must contact the author (@zaxlofful) for permission.
Free Use: You're welcome to use, modify, and share this project freely for non-commercial purposes!
This repository includes automated workflows:
- Lint β Runs flake8 on Python code
- pytest β Runs pytest test suite
- Generate QR β Automatically generates and commits QR codes
- Check Links β Validates links in the repository and creates issues for broken ones
Link Checker Workflow
The link checker workflow (.github/workflows/check-links.yml) automatically validates all links in the repository:
- Schedule: Runs weekly on Mondays at 9 AM UTC
- Manual Trigger: Can be run manually from the Actions tab with configurable scope:
external(default): Check only external links (excludes links to this repository)internal: Check only internal repository links (this repo + relative/anchor links)all: Check all links (includes both internal and external)
- Issue Creation: Automatically creates or updates an issue labeled
link-checkwhen broken links are found - Smart Detection: Avoids duplicate issues by checking for existing open link-check issues
- Detailed Reports: Provides comprehensive reports with all broken links and suggested fixes
The workflow scans all Markdown, HTML, JSON, and Python files for links and validates them with multiple retries and configurable timeouts to handle temporary failures.
Copilot Trigger & Automation Label
- Trigger: The repository's issue-based automation uses an issue body mention
@copilotto trigger the Copilot workspace integration from workflows or scripts. - Local filtering: The workflows apply a
todo-automationlabel to automation issues so you can filter them locally (e.g.,is:issue label:todo-automation). Workflows create this label idempotently if it is missing; to change this behavior, edit.github/workflows/check-todo.yml.
Copilot automation in CI (what the check-todo workflow does)
-
The repository ships a scheduled workflow (
.github/workflows/check-todo.yml) that automates TODO handling and can delegate work to GitHub Copilot. -
High-level behavior:
- If
TODO.mdcontains items, the workflow first checks for existing open issues labeledtodo-automationto avoid duplicates. - It then checks for existing Pull Requests (open and recently closed) that look like Copilot work β it matches by PR author (
copilot-swe-agent),todo-automationlabel, a title pattern like "Process TODO.md items", or by finding a short snippet ofTODO.mdin the PR body. Closed-but-merged PRs are ignored; closed-but-unmerged PRs are treated as existing to avoid reopening duplicate work. - The workflow logs the installed
ghCLI version and whethergh agent-taskis available in the runner (useful for debugging and diagnosing why the agent path was or wasn't used). - If no existing issue or PR is found and
gh agent-taskis available, the workflow prefersgh agent-task createto start a Copilot agent session directly from the TODO body (this avoids creating a separate issue in many cases). - If
gh agent-taskis not available or the agent task fails, the workflow falls back to creating atodo-automationissue, assigns it to the repository owner, and posts an@copilotcomment so Copilot can be triggered when available.
- If
-
Secrets/permissions: the workflow uses the built-in
GITHUB_TOKENfor repository operations (creating issues, comments, labels). No extra user token is required by default. To use thegh agent-taskpath in CI, ensure the runner has a reasonably recentgh(v2.80.0+ recommended) installed so theagent-taskcommand exists and behaves as expected. -
Why this helps: the combination of detecting existing PRs (including recent closed-but-unmerged Copilot PRs), preferring the agent task when available, and having a simple fallback keeps automation idempotent and avoids duplicate issues/PRs while still making Copilot integration convenient.
This repository is designed to work with Cloudflare Pages and custom domains. The single-file HTML design makes deployment simple:
-
Cloudflare Pages (Recommended):
- Connect your repository to Cloudflare Pages through their web GUI
- Important for security: Configure to deploy only HTML files:
- Build command:
mkdir public && find . -maxdepth 1 -type f -name '*.html' -print0 | xargs -0 -I {} cp -- '{}' public/ - Output directory:
public
- Build command:
- Why this matters: Cloudflare Pages will only deploy what's in the
publicfolder, preventing accidental exposure of scripts, configuration files, or other repository contents - Note: This same command is used in the GitHub Pages workflow (
.github/workflows/deploy-pages.yml) - Your lists will be available at your custom domain
-
GitHub Pages (Optional):
- A manual workflow is available if you prefer GitHub Pages
- Go to Actions β "Deploy to GitHub Pages (Optional)" β Run workflow
- The workflow uses the same security approach: copying only HTML files to a
publicdirectory before deployment - Enable GitHub Pages in repository settings if needed
-
Self-hosted:
- Simply copy the HTML files to any web server
- No build process required
- Recommended: Copy only
*.htmlfiles for security
The QR generation script reads metadata from HTML files:
qr-foreground-colorβ QR module colorqr-background-colorβ QR background colorqr-decorateβ Enable/disable Christmas tree decorationqr-tree-styleβfancy(with baubles) orplain
- No external tracking
- No CDN dependencies
- QR codes generated locally (no calls to remote services)
- All assets embedded in the HTML file
When adding gift links, remove tracking parameters:
- β
https://example.com/product?utm_source=email&ref=tracker - β
https://example.com/product
Or...Add your own to track if they opened your links!
This is a template (consider renaming the repo when you clone it)
- Intended workflow:
- Clone this repo as a template.
- Create one HTML file per person by copying
index.html(e.g.elsa.html,bob.html). - Use a CI job to generate and embed a per-page QR SVG into each file based on the filename and your public root domain. This keeps each file single-file and offline-friendly.
Files
index.htmlβ the starter single-file template. Copy and edit per recipient.
QR code approach and single-file guarantee
- Keep QR artwork embedded in each HTML file. Preferred formats:
- Inline SVG fragments, or
- Data-URI images:
data:image/svg+xml;utf8,...
- Embedding QR images in the page preserves the single-file guarantee (no runtime network calls or JS required).
Per-file metadata (qr-foreground-color, qr-background-color) are used by the generator when present and otherwise the CLI defaults are used.
Generating/updating SVGs via CI (recommended)
- Use CI (GitHub Actions or other) to generate per-page QR images and inject them into the corresponding HTML files before publishing.
Note on CLI defaults: The Python generator (scripts/generate_qr_svg.py) will read ROOT_DOMAIN from the environment if provided; otherwise it falls back to https://example.com. When building per-file public URLs the script appends the filename, so the effective fallback is https://example.com/index.html when run against index.html without configuration.
Local run examples
If you want to run the generator locally and ensure it uses your domain, set ROOT_DOMAIN in your shell and run the generator. Two examples below (POSIX shell and PowerShell):
POSIX / macOS / Linux (bash/zsh):
python3 -m venv .venv
source .venv/bin/activate
pip install -r scripts/requirements.txt
export ROOT_DOMAIN="https://yourdomain.example"
python3 scripts/generate_qr_svg.py --pattern "*.html" --out-dir scripts/generated_qr
python3 scripts/inject_qr_svg.py --svg-dir scripts/generated_qr --pattern "*.html"Windows PowerShell:
python -m venv .venv
& .\.venv\Scripts\Activate.ps1
pip install -r scripts/requirements.txt
$env:ROOT_DOMAIN = 'https://yourdomain.example'
python .\scripts\generate_qr_svg.py --pattern "*.html" --out-dir scripts/generated_qr
python .\scripts\inject_qr_svg.py --svg-dir scripts/generated_qr --pattern "*.html"Notes:
- You can also pass
--root-domaindirectly togenerate_qr_svg.py; whenROOT_DOMAINis set in the environment the CLI will use that by default so the explicit flag is optional. - High-level CI flow:
- Discover per-recipient files (e.g.
elsa.html,bob.html) in the repo. - Map each filename to its public URL:
https://<root-domain>/<filename>(ensure filenames are URL-safe). - Generate a QR SVG for that URL using a CLI or small script (
segno,qrcode,qrencode, Node libs, etc.). - Place the SVG (inline) or data-URI into the
.qrcode-boxregion of that HTML file (use a clear marker comment so replacements are idempotent). - Commit the modified files back to the repo from CI.
- Discover per-recipient files (e.g.
Notes on filenames and domain mapping
- Keep per-person filenames URL-safe (lowercase, hyphens; no spaces).
- CI scripts should be deterministic and idempotent so repeated runs don't create diffs.
Cleaning gift URLs
- If you include external product links in the list, remove tracking parameters (UTM, affiliate IDs) and other trackers before publishing. Prefer canonical clean links in the HTML.
How to preview / use locally
- Open any per-person HTML file in a browser (double-click or run a tiny server).
- Edit content directly in the file (or add
contenteditable="true"if you want in-browser editing; update README when you do). - Print or save as PDF.
Privacy
- No external tracking is included by default. The CI-based QR injection pattern avoids runtime calls to remote QR generators. If you add remote scripts or CDNs, document the privacy tradeoffs in this README.
The repository includes Python scripts to generate and inject QR codes into your HTML files.
Two-step process:
# 1. Generate QR SVG files
python3 scripts/generate_qr_svg.py --root-domain "https://yourusername.github.io/SimpleWish" --pattern "*.html" --out-dir scripts/generated_qr
# 2. Inject QR codes into HTML files
python3 scripts/inject_qr_svg.py --svg-dir scripts/generated_qr --pattern "*.html"Customization options:
# Use custom colors
python3 scripts/generate_qr_svg.py \
--root-domain "https://example.com" \
--pattern "*.html" \
--out-dir scripts/generated_qr \
--foreground-color "#1565C0" \
--background-color "#E3F2FD"
# Disable decoration
python3 scripts/generate_qr_svg.py \
--root-domain "https://example.com" \
--pattern "*.html" \
--out-dir scripts/generated_qr \
--no-decorate
# Use plain tree style
python3 scripts/generate_qr_svg.py \
--root-domain "https://example.com" \
--pattern "*.html" \
--out-dir scripts/generated_qr \
--tree-style plainSee python3 scripts/generate_qr_svg.py --help for all options.
- This repository builds and publishes a minimal Debian-based CI image to GitHub Container Registry (GHCR) and uses that curated image for all CI runs on Linux runners. The image is built from
.github/ci/Dockerfile(usingpython:3.11-slim) and pushed toghcr.io/<owner>/simplewish-ci:<sha>and:mainby the automated build workflow. - Workflows pin to the
mainimage tag and the SHA-tagged image for immutability. Cryptographic signing was previously used but has been removed from the automated build workflow and will be rethought.
TODO: Re-evaluate image signing and verification
- The repository previously used
cosign(keyless) to sign published images. That signing step has been removed from the automated build workflow. Consider one of the following approaches in future iterations:- Reintroduce cosign with a vetted key-management approach (e.g., short-lived KMS-backed keys and GitHub OIDC), or
- Use GitHub Container Registry immutability settings and repository variables to pin approved SHA tags, and document a manual verification procedure.
For now the CI image workflow publishes an image tagged with the commit SHA and, when on main, a :main tag; consuming workflows should pin to the SHA tag exposed via the CI_IMAGE_TAG repository variable.
Note about CI workflow linter warnings
- You may see linter/editor warnings about
vars.CI_IMAGE_TAGbeing unavailable forcontainer.imageat workflow-compile time. These are expected because the variable is created by the build workflow and may not exist at compile-time for consumer workflows. The repository intentionally usesworkflow_runand fallbacks to ensure jobs run even when the variable is not set.
Build and publish the CI image (done automatically on push to main):
# Build locally (optional)
docker build -f .github/ci/Dockerfile -t ghcr.io/$GITHUB_ACTOR/simplewish-ci:local .
# Push (requires GHCR login)
docker tag ghcr.io/$GITHUB_ACTOR/simplewish-ci:local ghcr.io/<owner>/simplewish-ci:main
docker push ghcr.io/<owner>/simplewish-ci:mainSecurity notes:
- The CI image is pinned by tag in workflows; for fully immutable runs the image is also published with the commit SHA as a tag (the build workflow does this).
- The build workflow also runs a vulnerability scan (Trivy). Image signing via
cosignwas previously used but is not performed by the automated build workflow anymore. - CI currently runs on Linux runners only and uses the curated GHCR image to minimize runtime attack surface. If you need stricter isolation, consider ephemeral self-hosted runners in a locked-down VPC.
Enjoy! Merry Christmas!