BMJ TCG
Six card games, a Portuguese-language card database, and a metagame that recalculates itself.
- Client
- In-house product
- Sector
- Media & product · Trading card games
- Year
- 2026
- Scope
- Product · Data architecture · UX/UI · Engineering · SEO · Automation
The problem
Brazilian competitive card game players had their information scattered across WhatsApp groups, loose spreadsheets and foreign-language sites. Anyone trying to find out what was actually winning tournaments had to read English and dig through decklists in three different places. There was no single source with translated cards, tournament results and a calculated metagame.
The real gap wasn't news coverage. It was organised data. An article about the meta solves one day; a queryable meta solves every day.
What I built
The site is a hub with one subdomain per game, each carrying that game's canonical colour and the same navigation skeleton: metagame, tournaments, calendar, card gallery, deckbuilder. A Pokémon player and a Yu-Gi-Oh! player find the same thing in the same place without learning two interfaces.
Underneath, a pipeline pulls cards from five databases and tournaments from five more, normalises and stores them. The tier list isn't hand-written: it's computed from the imported decklists and carries the delta against the previous snapshot, so you can see what rose and what fell without remembering last month. Images go through edge transformation, because wide tables on a phone are where this kind of site usually dies.
The metagame is computed, not written
Every new tournament recalculates the tier list. The table shows share, deck count, Top 8s, titles and a trend arrow against the previous snapshot. You can switch circuits and browse historical set snapshots, so a player can look at the meta from two months ago without depending on someone having written about it.
Cards in Portuguese are the product
The gallery separates international from Japanese sets, shows names in both Portuguese and English, set code, release date and card count. The in-house Pokédex groups by generation and flags variants. In a niche where every reference is in English, reading the card in your own language is the reason to come back.
Editorial with written rules
The release calendar shows an announced set without a confirmed date by quarter, never with an invented day. It is a small rule and exactly the kind of thing that separates a reference portal from an aggregator.
Inside
Project documentation
Art direction
Five card games sharing one site, each with its own look and a fanbase that does not mix. The art decision was to pick no side: the chassis is neutral black and each game brings its own brand colour on top. You know which game you are in before you read the page title.
Typography
- Aa Archivo Black
Headlines and tournament numbers. Heavy enough to hold a card name over busy artwork.
- Aa Barlow Condensed
Decklists and ranking tables. Condensed because card names are long and the column is not.
- Aa Archivo
Article and note body copy.
- Aa JetBrains Mono
Counts, tournament dates and metagame percentages — numbers that must align in a column.
Palette
- #070707 Background
- #161616 Panel
- #262626 Rule
- #1E8FEF Action
- #FFCB05 Pokémon
- #C99A3B Magic
- #32C8E0 Lorcana
- #E4342B Yu-Gi-Oh!
One variable — the game colour — swaps the accent across the whole panel. No component needs to know which game it is in.
Why this stack
Astro up front because tournament and card pages barely change once published: render once, serve static HTML, and the reader does not pay for a framework they never use. Svelte only where there is real state — deckbuilder, metagame filters, search. Cloudflare Workers with D1 and R2 because the importer has to run next to the database, and card images are many and heavy; serving them from R2 through Images costs a fraction of a traditional host.
Details
Why not WordPress here
A tournament is not a post. It is a row in a table, with entrants, decks and results that join. In a post-shaped CMS that becomes custom fields on custom fields until nobody can query anything.
A column that fits a phone
A decklist is 60 rows. The condensed type and mono numerals were chosen so it fits with no horizontal scroll — the point where most TCG sites give up.
In numbers
What was built, counted on the site itself.
card games covered, each on its own subdomain
external sources integrated: 5 for cards, 5 for tournaments
Pokémon in the in-house Pokédex, linked to 13,843 cards