Clásicos Básicos is a living museum of classic gaming in Spanish: 870+ entries across PC, Amiga, Spectrum, Amstrad and NES, plus forums, a podcast and the CLABA TV channel. The hard part was never the design. It was keeping an archive that size from eating the page.
A big catalogue is not a big blog
WordPress handles 870 entries without breaking a sweat. What it does not handle is the way almost every theme queries them. A game entry is not a post: it has a platform, a genre, a year, a developer, a publisher and half a dozen more fields. The temptation is to store all of it as post meta and filter with meta_query. That works with fifty entries and falls over at eight hundred.
The reason is how wp_postmeta is built: one row per field per entry, indexed on meta_key but not on the value. Filtering by “Amiga” makes MySQL walk every row with that key and join it back against wp_posts. Each extra filter is another self-join on the same table. Three filters at once, and a query that should take milliseconds takes hundreds.
What WordPress does index out of the box is taxonomies. So platform, genre and decade are taxonomies; everything else — release year, developer — stays in meta, because it gets displayed, not filtered. That one decision, taken before a single template was written, is the difference between an archive that responds and an archive you have to rescue with caching.
The three flags almost nobody sets
On a paginated listing, WP_Query does three things by default that you do not need:
- no_found_rows: without it, WordPress counts every result with SQL_CALC_FOUND_ROWS just to print “page 3 of 44”. If the pager needs the total, cache it separately and refresh it when a new entry lands.
- update_post_meta_cache: preloads all post meta for the listed entries, even though the card only shows title, cover art and platform.
- update_post_term_cache: preloads terms for every taxonomy. If the card shows the platform, ask for those terms only, not all six vocabularies.
None of these is a hack: they are documented WP_Query arguments. They default to on because WordPress cannot know what the template will render. We can.
A template must never ask for data it is not going to render. It sounds obvious, and it is exactly what a generic theme does on every archive page.
Paginate properly, don't load and hide
An 870-entry archive gets paginated. It does not get loaded whole and hidden with CSS, and it does not get paginated with infinite scroll alone. Infinite scroll breaks two things at once: Google's crawler does not fire scroll events, so it only ever sees the first batch; and a visitor arriving from search cannot link to what they are looking at.
Here the pagination is the classic kind, with real crawlable URLs, and links to the first, last and neighbouring pages so no entry sits more than three clicks from the front page. You can layer progressive loading on top as a convenience, but the skeleton underneath has to work with JavaScript switched off.
The weight is in the cover art, not the HTML
A listing page with 24 entries is cheap HTML and expensive bandwidth: 24 pieces of cover art. Three decisions take almost all of the savings.
- A dedicated image size for the card, generated on upload with add_image_size, instead of serving the original and letting the browser scale it down.
- loading=lazy on everything below the fold and — this matters — NOT on the first row: the first cover is usually the LCP element, and deferring it means deferring the metric Google measures.
- Explicit width and height on every image. Without them the browser reserves no space, cards jump as the covers arrive, and that is CLS in its purest form.
Cloudflare sits in front and caches the archive pages, which change rarely: one new entry a week does not justify hitting PHP on every visit. Invalidation fires on publish rather than on a timer, so a freshly added entry shows up immediately.
The retro look cannot be paid for by performance
The site has CRT lines, pixels and synthwave, and that is half the reason the community feels it is theirs. But the nods live on the surface: typefaces, glows, grids. The structure, the hierarchy and the accessibility are those of any serious site. A CRT scanline is a pseudo-element with a repeating gradient, not an animated canvas. A neon glow is a text-shadow, not an image.
It is easy to overdo the retro thing and end up with something illegible, and that was the line we did not want to cross: the archive belongs to the community, and plenty of people browse it on a five-year-old phone.
What we take from this
Almost every performance problem in a content-heavy WordPress is not a WordPress problem: it comes from modelling the content as if it were a blog. Once platform and genre are taxonomies, the listing asks only for what it paints, and images carry their dimensions, an 870-entry archive behaves like a fifty-entry one.