Context
For the upcoming business card, the back side has 8 small QR codes pointing to short URLs on doyled-it.com. Each QR is sized at ~8.4mm — the smallest reliable scan size — and that's only achievable if the URL inside each QR is short. Hence: redirects.
| Slug |
Destination |
/m |
mailto:michael@doyled-it.com |
/cal |
https://cal.com/doyled-it |
/gh |
https://github.com/doyled-it |
/p |
tel:+17754506522 |
/li |
https://linkedin.com/in/michaeldoyleml |
/s |
https://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ |
/c |
https://doyled-it.com/card (see #2) |
/w |
reserved (placeholder for future work-email slot) |
Why redirects rather than direct URLs?
- QR module size scales with URL length. Short slugs let every QR be a v1 (21×21) code at ~8.4mm, the smallest reliable scan size.
- Destinations are editable later without reprinting cards (e.g., switching jobs, moving to a different scheduling tool, etc.).
- Free server-log analytics on which channels people actually scan.
Approach
Add a single 11ty template that paginates over a JSON data file. Each entry generates a static HTML stub with both <meta http-equiv="refresh"> AND a JS location.replace() fallback. The JS path matters for mailto: and tel: schemes — some browsers refuse those in meta-refresh, but allow them via JS.
Files to add
src/_data/redirects.json
[
{ "slug": "m", "url": "mailto:michael@doyled-it.com" },
{ "slug": "cal", "url": "https://cal.com/doyled-it" },
{ "slug": "gh", "url": "https://github.com/doyled-it" },
{ "slug": "p", "url": "tel:+17754506522" },
{ "slug": "li", "url": "https://linkedin.com/in/michaeldoyleml" },
{ "slug": "s", "url": "https://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ" },
{ "slug": "c", "url": "https://doyled-it.com/card" },
{ "slug": "w", "url": "mailto:michael@doyled-it.com" }
]
src/redirect.njk
---
pagination:
data: redirects
size: 1
alias: r
permalink: "/{{ r.slug }}/index.html"
eleventyExcludeFromCollections: true
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Redirecting…</title>
<meta name="robots" content="noindex">
<meta http-equiv="refresh" content="0; url={{ r.url | safe }}">
<link rel="canonical" href="{{ r.url | safe }}">
</head>
<body>
<p>Redirecting to <a href="{{ r.url | safe }}">{{ r.url }}</a>…</p>
<script>location.replace({{ r.url | dump | safe }});</script>
</body>
</html>
Slug-conflict check
None of the existing pages collide with the chosen slugs:
- existing pages:
index, baseball, contact, golf, music, projects, resume, resume-print, words
- redirect slugs:
m, cal, gh, p, li, s, c, w
Test plan
Notes
- CNAME unchanged — same
doyled-it.com domain.
- No CI changes needed — the existing
npm run build will pick up the new template via 11ty's pagination.
- Once these merge, do a smoke test by hitting
https://doyled-it.com/m, /cal, etc. before committing to the print order.
Out of scope
Related
Context
For the upcoming business card, the back side has 8 small QR codes pointing to short URLs on
doyled-it.com. Each QR is sized at ~8.4mm — the smallest reliable scan size — and that's only achievable if the URL inside each QR is short. Hence: redirects./mmailto:michael@doyled-it.com/calhttps://cal.com/doyled-it/ghhttps://github.com/doyled-it/ptel:+17754506522/lihttps://linkedin.com/in/michaeldoyleml/shttps://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ/chttps://doyled-it.com/card(see #2)/wWhy redirects rather than direct URLs?
Approach
Add a single 11ty template that paginates over a JSON data file. Each entry generates a static HTML stub with both
<meta http-equiv="refresh">AND a JSlocation.replace()fallback. The JS path matters formailto:andtel:schemes — some browsers refuse those in meta-refresh, but allow them via JS.Files to add
src/_data/redirects.json[ { "slug": "m", "url": "mailto:michael@doyled-it.com" }, { "slug": "cal", "url": "https://cal.com/doyled-it" }, { "slug": "gh", "url": "https://github.com/doyled-it" }, { "slug": "p", "url": "tel:+17754506522" }, { "slug": "li", "url": "https://linkedin.com/in/michaeldoyleml" }, { "slug": "s", "url": "https://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ" }, { "slug": "c", "url": "https://doyled-it.com/card" }, { "slug": "w", "url": "mailto:michael@doyled-it.com" } ]src/redirect.njkSlug-conflict check
None of the existing pages collide with the chosen slugs:
index,baseball,contact,golf,music,projects,resume,resume-print,wordsm,cal,gh,p,li,s,c,wTest plan
npm run buildsucceeds without errors_site/m/index.html(and the other 7) exist after build<meta refresh>and the JS fallbackcurl -sI http://localhost:8080/m/returns 200 and the body contains the correct destinationdoyled-it.com: scan a printed test QR for each slug from a real phone and verify the destination opens correctly (mailto, tel, https all behave)_site/sitemap.xmlfor the redirect stubs (they haveeleventyExcludeFromCollections: trueAND<meta name="robots" content="noindex">)Notes
doyled-it.comdomain.npm run buildwill pick up the new template via 11ty's pagination.https://doyled-it.com/m,/cal, etc. before committing to the print order.Out of scope
/cardchatbot page — see Build /card mini-agent for business card 'ASK ME' QR #2.Related
docs/business-card-design.md(added in docs(card): add business card design spec and visual mockup #3)docs/business-card-mockup.html— open in any browser/card: Build /card mini-agent for business card 'ASK ME' QR #2