Building onepiecedex.com
I picked up the One Piece Card Game last year and found myself constantly sending screenshots of decklists to friends. The problem was the tooling - there were no simple ways to send interactive decklists. Most of the deck builders I tried wanted me to make an account, sat behind a slow backend, or buried the cards I wanted under a wall of ads. I just wanted to filter a card pool, drop cards into a list, and send my friend a link. So I built onepiecedex.com.
The core idea centered on one constraint: no backend. The entire card catalog — roughly 4000 cards — ships with the app as a committed JSON snapshot, and everything else happens in the browser. That constraint drove some very interesting parts of the project.
The Stack
The site is built with SvelteKit 2 on Svelte 5, using the new runes for reactivity. I leveraged @sveltejs/adapter-static to output a fully static site. Styling is Tailwind 4 with daisyUI on top, Vite handles the build, and tests run on Vitest. The whole thing deploys to DigitalOcean’s App Platform as a static site — merging to master ships to production, and that’s the entire release process. Oh - and it’s free to host.
The card data comes from the community-maintained, MIT-licensed punk-records dataset. A build script pulls it down, normalizes each record into the shape the app expects, and rewrites the snapshot. When a new set drops, I run one command, review the diff, and commit. No scraping at runtime, no broken images when someone else’s site goes down.
Making 4000 Cards Feel Fast
Rendering 4000 card images at once is a great way to make a browser freeze up. The grid is virtualized, so it only ever renders the tiles actually in the viewport plus a small buffer. Scroll down and tiles are recycled as they leave the screen. The result is that the catalog feels instant even though the underlying list is large.
State flows through Svelte stores, which kept the components simple. cards, filters, and deck are the writable stores everything else derives from — the filtered card list, the per-card deck counts, the legality check. A card tile doesn’t reach into global state to figure out how many copies it has in your deck; it’s handed that number from a derived store. Thin components, one source of truth.
The Fun Part: No Backend, but Shareable
Because there’s no server, “share this deck” can’t mean “save it to a database and hand out an ID.” Instead, the deck and your current filters get encoded into the URL hash as compact, URL-safe base64. The link is the state. Open it and the app reconstructs everything — your deck, your leader, the filters you had applied — with zero network calls. Sort order lives inside the filter state too, so it rides along in the share URL for free.
Saved decks live in browser localStorage, and the last deck you were working on is restored when you come back. Deck building enforces the real rules as you go: 50 cards in the main deck plus a leader, copy limits by card number, with live legality feedback so you know the moment a deck is illegal. Exporting is a single “Copy list” button — it drops a plain-text decklist on your clipboard in the format OPTCG SIM imports, so you can paste a deck straight into the sim. Import works the same way in reverse.
The viewing experience adapts to the device. On desktop you hover a card for a larger preview and click to add it. On mobile, where hover doesn’t exist, tapping a card opens a full-screen readable view with add, quantity, and set-as-leader controls. Same data, two interaction models.
Wrapping Up
There’s something satisifying about the simplicity of this project. No server to patch, no database to back up, no API to rate-limit. The hardest engineering decision — choosing no backend — turned out to be the one that made everything downstream simpler.
Check it out at onepiecedex.com.