testfly.me: the test bed we prove our own solutions on, and a paraglider gear publication

Project
In-house project
Year
2026
What we did
ProcessWire websites
Stack
ProcessWire, PHP, Static cache, Custom modules, Vanilla JS
Website
testfly.me

A site that makes a technology earn its place: 105 test reports in two languages served as static files, and the modules that grew here already run on client sites.

105
test reports
The long ones carry marks per section and subsection, the short ones a single overall mark; counted from the category indexes on 18 September 2026
B
30
years of test reports
The first reports are from 1996, long before this site
B
0.7KB
for the whole brand filter
That is the whole of reports.js; the shared library is another 34 KB, across every page
B
0
requests when filtering
No request, no reload: the cards are already in the HTML and the filter only toggles a class
B
testfly.me

The test bed

testfly.me is our own site, and that is exactly what makes it useful. There is no client to talk into an experiment, no approvals, and no need for “let us do it the usual way”. So we try new solutions boldly here, on a real site with real visitors, and only then offer them to clients.

The rule is simple: client projects get what has survived here for at least a year. What did not survive was an idea that only looked good on paper — but clients will never know ;)

At the same time, testfly.me is much more than a safe sandbox. It is a full-fledged bilingual publication about paragliding gear: detailed test reports on wings, harnesses and instruments, written since 1996 and paid for by nobody.

What was road-tested here

OutputTransformer. The module that tidies up the rendered HTML: it drops the extra whitespace, applies typography and runs replacements. Its first commit is in this repository, 24 December 2025. Nine months on a live site later, it had survived a rewritten cleanup, and in September 2026 we published it.

StaticPages. A static cache: ProcessWire builds a page once, and Apache serves it as an ordinary file from then on. Easy to check — the home page answers with an ETag and a last-modified date of 22 August 2026, so an ordinary visit never starts PHP at all.

The frontend build. In August 2026 we replaced the old gulp and rollup setup here with our own build package — the same one that now builds atis.pro. Breaking the build on your own site costs nothing; breaking it on a client’s costs a day.

Our own fields. Specification tables, mark calculation — each is a field type or a module written for this site, and later useful elsewhere.

A report: marks inside the text

A section’s mark sits next to its heading, a subsection’s next to its own. On the right: the test conditions — where it was flown, on which harness, at what all-up weight.A section’s mark sits next to its heading, a subsection’s next to its own. On the right: the test conditions — where it was flown, on which harness, at what all-up weight.
A section’s mark sits next to its heading, a subsection’s next to its own. On the right: the test conditions — where it was flown, on which harness, at what all-up weight.

The engine works out the mark, not the author

A wing’s overall mark is not a number somebody types at the end. The tester marks the small things: the brake handles on their own, the behaviour in a turn on its own, the behaviour under a collapse on its own. Those add up to a subsection’s mark, subsections to a section’s, sections to the whole aircraft’s.

Our Marks module does that arithmetic when the page is saved. The editor never types the total, and it cannot be changed without changing the individual marks. Which is also a defence against oneself: if the overall impression says “a nine” and the individual marks say 8.2, the report says 8.2.

Short reports work more simply: a handful of marks and one average. Two templates, one module, the same arithmetic.

Specifications as tables

Technical data and materials live in a matrix field: every row has its own columns, and the editor fills in a table rather than a paragraph. The Overview, Specs and Conclusion tabs switch in place, with no reload.Technical data and materials live in a matrix field: every row has its own columns, and the editor fills in a table rather than a paragraph. The Overview, Specs and Conclusion tabs switch in place, with no reload.
Technical data and materials live in a matrix field: every row has its own columns, and the editor fills in a table rather than a paragraph. The Overview, Specs and Conclusion tabs switch in place, with no reload.

The brand filter: 700 bytes and not one request

In a category with three dozen wings, the manufacturers’ logos sit in a strip above the list. Tapping a logo leaves only that maker’s wings; tapping it again brings the rest back. That is what the clip above shows.

No request is made. Every card is already in the HTML, and each carries a small JSON object in its data attribute: brand, certification, year, positioning. The filter compares the field it was asked for and toggles a class on the cards — the browser redraws the list itself.

The whole filter file is 699 bytes built. It knows nothing about brands: the field name comes from the link’s attribute, so the same code filters by year or by class without another line.

The data travels in the markup

<!-- each card carries what it can be filtered by -->
<div class="report item" data='{"brand":"ParAAvis","homologation":"EN B","year":2017}'>…</div>

<!-- the logo strip: the value is the link's own title -->
<a title="ParAAvis" data-filter><img src="/…/paraavis.png" alt="ParAAvis"></a>

// one call, any field of that JSON
dataFilter('.report.item', 'brand', 'ParAAvis')  // or 'year', 2017 — same code

Nothing but markup and a single call: the server hands over the finished list, the browser decides what to show.

Two languages, one page tree

The site runs in two languages: the Russian reports are read at home, the English ones by manufacturers curious about what was written on their wing. These are not two sites, nor copies: in ProcessWire a second language is a set of sibling fields on the same page, so marks, specifications and photographs are shared and only the text is translated.

First drafts come from a translator built into the admin, then they are edited by hand. For technical writing about wings that saves an evening per report, but it does not replace the author: paragliding terms come back confident and wrong.