Turn "a month of technology news" into an HTML deck that can be presented on stage, shared, and fact-checked.
The output isa single self-contained HTML + assets folder: no framework, no build step, runs offline, and can be deployed directly as a URL.
When to use
- A monthly community meetup needs a report on "what happened this month"
- Turning a period of industry news into slides
- You already have a deck and want to change the layout / add assets / add pages / deploy
Not applicable: plain-text monthly report articles, or long documents that need formal typesetting (use a document typesetting tool for those, not a slide engine).
Process
1. Sourcing
Read references/source-recipes.mdfirst. It contains field-tested scraping methods, including
viable paths for Reddit / YouTube / Medium / GitHub / conference websites after "the conventional approach is blocked".
If you can't scrape something, don't retry by trial and error—check that document first.
Iron rules:
- Version numbers must always come from official machine-readable sources(releases JSON / CHANGELOG), never trust articles
- Every video must be checked against
uploadDateto confirm it belongs to the current issue; RSS mixes in old videos - Every number must be traceable back to a URL
- If you can't scrape it, state the gap explicitly on the slide; don't fill it with guesses
At the same time, download assets to the local assets/(yt/ thumbnails,img/ article covers and OG images,demo/ project demos,
vid/ videos,spk/ speaker avatars).Assets must be usable offline, you can't gamble on the venue's network.
For batch downloads, use scripts/harvest_media.py, which takes a JSON manifest:
python3 scripts/harvest_media.py manifest.json --root assets
Supports youtube(falls back to hqdefault if maxres fails),github(OG card, automatically throttled to avoid 429),
readme(scans repo README for demo gif/webp, skips shields.io badges),url(direct link).
2. Decide content and order
Order by "how much the audience will care", not by time. Usually:
開場(總覽 / 三條主線)
官方(版本、部落格、repo 動態)
大事件(會議、發表會)
本月最亮眼的專案(給它兩三頁,不要跟其他擠一頁)
生態與社群(企業案例、熱門討論、開源專案)
收尾(行事曆、數字回顧)
Use data-sect to mark sections, anddata-title for short overview titles.
3. Make the slides
Copy from assets/deck-template.html , and choose layouts according to references/slide-patterns.md .
Two rules that will get you rejected:
- Every page must have a visual that is instantly understandable. Cover the explanatory text—can you tell what this page is about? A GitHub OG card (just the author avatar) doesn't count; look for demo GIFs in the README, actual screenshots, or product recordings.
- Don't write your own opinions. If a sentence contains "I think / the signal is / it's worth noting / the dividing line / narrative", assume it should be deleted. What remains is "who, when, did what, what the numbers are"— but also give 1–2 sentences of supporting facts, not just keywords.
Details and counterexamples are in references/slide-patterns.md。
4. Verification (cannot be skipped)
python3 scripts/verify_deck.py path/to/slides.html
Will start localhost, walk through every page, and report three kinds of problems that actually occur:
- overflow — auto-fit shrinks below 0.85, meaning this page has too much content; it should be split into pages, not fixed by scaling
- broken assets — Image or video 404
- orphan lines — Title line break leaves 1–2 orphan characters
and produce a contact sheet for visual inspection.Only say "it's done" after looking at that image.
There is an exit code: 0 clean / 1 has problems.
5. Font offline-ization
python3 scripts/refresh_fonts.py path/to/slides.html
Scan the characters actually used in the presentation, generate subset woff2 into assets/fonts/ and rewrite @font-face,
remove the CDN link. Only then, when there is no network at the venue, will it not fall back to system fonts.
⚠️ After changing the slide text, rerun, otherwise newly added characters will be missing.
6. Deployment
Read references/publish.md. Key points:
- For on-site presentation use
assets/serve.shvia localhost(copy into the presentation folder) —— YouTube refuses embedding fromfile://(error 153), only http(s) can play inline - When choosing a host, calculate traffic: Firebase free tier is 360 MB/day; a 60 MB presentation blows through it with about 6 people; GitHub Pages is 100 GB/month. For distributing to the audience, choose GitHub Pages
- ⚠️ Never enable Pages on a personal vault repo, that would make the entire vault public. Create a separate clean repo
- After deployment, test for real: spot-check each type of asset for 200, fonts loaded, YouTube inline playback
Files
| Path | Purpose |
|---|---|
assets/deck-template.html |
Presentation engine + eight layout templates. auto-fit, lazy media, chapter overview, protocol-aware YouTube |
references/source-recipes.md |
Sourcing recipes. Check here when blocked |
references/slide-patterns.md |
Choose layout, writing rules, engine behavior |
references/publish.md |
Deployment, host selection, on-site presentation |
assets/serve.sh |
Present using localhost (a requirement for YouTube inline playback) |
scripts/verify_deck.py |
Validation gate: overflow / broken images / orphan lines + contact sheet |
scripts/refresh_fonts.py |
Font subsetting |
scripts/harvest_media.py |
Batch-fetch assets to local according to the manifest |
Engine behavior quick reference
| Mechanism | Description |
|---|---|
| auto-fit | Measure .wrap height; if it exceeds, scale proportionally.When changing the engine, the negative margin cannot be omitted, otherwise flex centering will push the content off screen |
| lazy media | Only the current page and the previous/next page are decoded. Animated webp is very memory-hungry |
| inline video | .vtile video plays when navigated to, stops when leaving. Need muted loop playsinline preload="none" |
| YouTube | http(s) embeds in place,file:// opens a new tab. Esc or the close button dismisses the player (after focus enters the iframe, the parent cannot receive keyboard events, so there must be a close button on the parent side) |
| Page turning | Only the left and right edges of the screen are clickable; the middle is left for circling words and pointing at things |
| Keyboard | ← → page turning · O chapter overview · F fullscreen · Esc close player |
Common errors
| Symptom | Cause |
|---|---|
| Content is cut off but auto-fit does not work | On first measurement, images are not decoded yet and the height is wrong. Need to measure again in img.onload at |
| Dark page overflow | The dark page's padding is in .wrap , andavail must not subtract padding again |
| Subscript (badge) not showing | If the badge style is bound under .fig ,.thumb the container won't pick it up. Need to use a universal selector |
| Heading has orphan characters + jarring color blocks | <em> highlighting spans multiple lines. Use explicit <br> to control line breaks |
Change URL bar to #N without page jump |
The engine only reads the hash on initial load. Need to refresh or press O to jump using the overview |
| Chinese filenames 404 after deployment | GitHub Pages has issues with Chinese filenames; change to ASCII filenames |