I built a website, mtg.emshea.com, that lets you search for Magic: The Gathering cards. My goal for this project was to start learning Vue.js to expand what I can do with web frontends. This post will talk about how I built it using Vue.
About the website
On the site, users can find cards by set (ex, Throne of Eldraine) and mana color, or by card name.
Searching by card and set (on desktop):
Searching by card name (on mobile):
Users can share links to specific pages or specific cards, for example:
- Green Amonkhet cards, page 2: https://mtg.emshea.com/?color=green&set=akh&page=2
- Wishful Merfolk: https://mtg.emshea.com/?name=Wishful%20Merfolk
To help you read individual cards, hovering over cards on desktop will zoom in on the card image. For cards with special layouts, hovering or clicking on the cards will flip or rotate the card.
Examples of special layouts:
- Budoka Gardener // Dokai, Weaver of Life (on click)
- Azor's Gateway // Sanctum of the Sun (on click)
- Alive // Well (on hover, desktop only)
- Appeal // Authority (on click, desktop only)
How it's built
The site is a single page web app built using Vue.js and Bootstrap. It's hosted in S3 and distributed through CloudFront. Magic card data is pulled from the Scryfall API. The source code is available here.
Since I'm new to working in Vue, I kept the structure fairly simple. The main view contains most of the core code, including the filters and page navigation header, with the logic for rendering cards broken out as a component.
Filters, search, and page navigation
When the page is loaded, the set names for all Magic core and expansion sets are pulled dynamically and populated in the set dropdown.
setInitialParams function is called. This function first checks to see if query string parameters have been passed, for example:
If so, the query string parameters are set to the relevant data variables (set/color, and page number, or card name). If no parameters have been passed, the initial variables are set to a random color and a random set within the most recent 15 sets.
Once variables are set, either
pullCards (when filtered by set/color) or
searchByName is called. The variable data is passed to these functions to call the Magic API and return the card data.
The same process takes place when a user starts a new search using the set/color dropdowns or the search by name input field, with an added step to populate the URL query string parameters with the newly selected variables. Scryfall provides an API endpoint that generates a list of autocomplete suggestions when a user searches by card name.
When search results include more than 20 cards, the results are paginated. Users can page through search results or select a specific page using navigation buttons. Page numbers are added to the query string parameters.
The logic for rendering cards is abstracted from the main view as a component, mtgCard. The main view loops through the search results and passes the individual card data to the mtgCard component.
Within the component, cards are rendered conditionally based on card data. The first condition checks if the card is single-sided or two-sided. For two-sided cards, both card faces are set as card images. Clicking on the card toggles which image is visible and enables users to flip between the two images. For single-sided cards, the card layout is stored as a class. For those with special layouts, this class is used to assign layout-specific CSS animations. For example, flip layout cards will rotate updside down on click (example).
I would like to add more advanced filtering and search functionality, like filtering by card type (planeswalker, creature, sorcery, etc.) and keyword searches that display all cards that include that keyword.
For future projects, I've started playing with text detection for cards, and I'm working on image recognition to be able to identify a card by the card art. Beyond card images, there's a world of interesting AI/ML applications with Magic, like what others have worked on for drafting, deck optimization, and game play.
Check out the source code
Special thanks to Tania Rascia's excellent blog post on learning to interact with APIs.