BMJ Studios
← All work

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
Visit the site → bmjtcg.com.br
bmjtcg.com.br
BMJ TCG — homepage

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

Pokémon TCG panel: metagame, tournaments and cards on the game’s own subdomain
Pokémon TCG panel: metagame, tournaments and cards on the game’s own subdomain
Tier list computed from imported decklists, with the delta against the previous snapshot
Tier list computed from imported decklists, with the delta against the previous snapshot
Tournament page: bracket, standings and each player’s decklist
Tournament page: bracket, standings and each player’s decklist
Deckbuilder: build, validate against the format and share — the part that needs real state
Deckbuilder: build, validate against the format and share — the part that needs real state
Card gallery with names in Portuguese and English, set code and release date
Card gallery with names in Portuguese and English, set code and release date

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.

AstroSvelteCloudflare WorkersD1R2Cloudflare Images

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.

6

card games covered, each on its own subdomain

10

external sources integrated: 5 for cards, 5 for tournaments

1.025

Pokémon in the in-house Pokédex, linked to 13,843 cards

Stack

AstroSvelteCloudflare WorkersD1R2Cloudflare Images

I open your site for free and tell you what is holding it back.

Send me the link and I go through it today: speed, checkout, and what Google and the AI engines actually read off your page. I tell you what is in the way — or that it is fine, if it is. No site yet? Tell me what you sell.

Send the link on WhatsApp →

Send the link by email

I answer myself, same dayIf it is not a fit, I say so straight awayFixed price per project, no monthly fee