Matemarketing 2026 Programme — a static replica
▶ Live replica — runs on any static host, no build, no install. Code: NikitaBoyarkin/mm-program-page.
Situation
A conference programme page is a typical SPA: the HTML ships an empty <div id="app">, the
programme arrives from an API and the browser draws it. You cannot copy such a page by saving the
rendered markup — the saved file has neither the talks nor the grid. The usual answer to this task
is «I made it look similar», and there is no way to check that claim.
Task
Take the interface apart into measurable quantities — column widths, insets, row height, type sizes — and rebuild it with no build step, no backend and no npm. More importantly, replace «looks similar» with something checkable: measurements, tests and an explicit list of deviations.
Actions
- Reconnaissance. Established that the page is a Vue app and the programme comes from
GET /api/event-program/event/6/: 335 KB of JSON, 3 days, 91 talks, 11 tracks, 9 halls. Design tokens (borders#E5E7EB, grid surface#F9FAFB, brand#5C34F6) came from the visual config and the shipped CSS rather than from picking colours by eye. - Data.
tools/fetch_program.pydownloads the payload, strips HTML from descriptions, falls back to the first paragraph whenshort_descriptionis empty, and writeswindow.MM_PROGRAM. Modes:--fetch,--check(detects drift between snapshot and API) and--dry-run. - Layout. An
index.htmlskeleton plus curated CSS (an extract of the shipped CSS withdata-v-*stripped and Vuetify internals dropped) and a dependency-freeapp.js: the time × hall grid, multi-slot cards, day tabs, 11 track chips,localStoragefavourites, a modal with a focus trap, and per-day.icsexport. - Verification. Four independent passes: code review (15 findings, 2 high), security audit
(0 blocking), browser tests in headless Chrome, and a visual audit with pixel measurements.
The findings added URL validation for images, fallbacks for broken banners and avatars,
.icsline folding, tab/ARIA wiring and a CSP.
Result
The grid is replicated by measurement, not by feel:
| Metric (1440 px) | Original | Replica |
|---|---|---|
| Time column | 99 px | 99–100 px |
| Hall column | 420 px | 420 px |
| Card and radius | 391 px, 16 | 391 px, 16 |
| Banner | 4:3, 291 px | 4:3, 291 px |
| Row height | 550 px | 550 px |
| Grid top | y = 414 | y = 414 |
| Heading (ink box) | 638×22 | 650×22 |
| Document height | 6605 px | 6589 px |
Verification: tests/test_data.py — 9 tests, 2,370 data-invariant assertions;
tests/test_page.py — 7 tests against real rendering (per-day counts, grid-area integrity,
screenshots at 1440 and 390 px, graceful degradation without data). Both suites are
mutation-checked: a broken counter, a shifted grid and an empty title each fail the matching test.
Limitations
- No backend: no auth, no personal cabinet, no server-side favourites, no chat or player.
- Images and the commercial heading typeface are not redistributed — they load from the original CDN. Because of CORS the original Loos Wide does not load, so the heading is drawn by the free Unbounded, fitted to the ink box (650×22 against 638×22).
- The header counter shows the number of talks that day, not a fake «people online»: presence in a static page cannot be invented.
- The data is a snapshot.
python3 tools/fetch_program.py --checkreports when the original programme changes.
Documentation
- Code and tests —
index.html,assets/app.js,tests/,tools/fetch_program.py - CONTRACT.md — data schema, DOM contract and behaviour list
- docs/DECISIONS.md — every deviation from the original, with reasons
- Live replica — opens with no install