Building onepiecedex.com
I picked up the One Piece Card Game last year and, as tends to happen, building decks quickly became as fun as actually playing. The problem was the tooling. 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 was a constraint: no backend. No database, no API, no server to keep alive at 2am. The entire card catalog — roughly 4000 cards — ships with the app as a committed JSON snapshot, and everything else happens in the browser. That decision drove most of the interesting parts of the project.
The Stack
The site is built with SvelteKit 2 on Svelte 5, using the new runes for reactivity. I leaned on @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.
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
Here’s the constraint paying off. 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 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 simulator. 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 satisfying about a project where the deployment story is “it’s just files.” No server to patch, no database to back up, no API to rate-limit. The hardest engineering decision — refusing to add a backend — turned out to be the one that made everything downstream simpler.
Give it a spin at onepiecedex.com.