Skip to content

build-site

Generate a website for your decks and collections.

Terminal window
./ritual build-site [options]

By default, deck card images use Scryfall URLs from card data. This can be overridden with the --cache-images option to download and use local images instead.

OptionDescription
-v, --verboseShow list of cards being fetched from Scryfall
--cache-imagesDownload and use local deck card images in dist/images instead of URLs
--decks [names...]Deck names or URLs to include in the site (default: the site.includeDecks config selection)
--collections [names...]Collection names to include in the site (default: the site.includeCollections config selection)
--wanted-lists [names...]Wanted list names to include in the site (default: the site.includeWantedLists config selection)
--collection-sort <field>Default sort order for collection pages (file-order, name, price, set-code, type, cmc, color-identity)
--deck-sort <field>Default sort order for deck pages (name, cmc, price, type, edhrec, color-identity)
--currencies <list>Comma-separated currencies to include on the site: usd, eur, tix (default: all three; first listed is default)
--allow-refreshRefresh the card cache when stale, including the fast Scryfall bulk download (answers the prompt for you)
--allow-refresh-no-bulkRefresh stale prices per-card but never trigger a bulk download
--no-refreshNever refresh the card cache; build from cached data as-is
--theme <name>Initial theme served to first-time visitors (built-in name or a custom name from --theme-file). Defaults to default.
--theme-file <path...>Load one or more custom theme JSON files; each is added to the runtime theme list under its declared name.

Build site for all decks and collections:

Terminal window
./ritual build-site

Build site for specific decks:

Terminal window
./ritual build-site --decks "Atraxa Superfriends" "Mono Red Aggro"

Build with verbose output:

Terminal window
./ritual build-site --verbose

Build with downloaded local deck card images:

Terminal window
./ritual build-site --cache-images

Build directly from a URL:

Terminal window
./ritual build-site --decks https://archidekt.com/decks/12345

Build with specific collections:

Terminal window
./ritual build-site --collections "Red Binder" "ECL"

Build with specific wanted lists:

Terminal window
./ritual build-site --wanted-lists "High Priority" "Trade Targets"

Build with custom collection sort order:

Terminal window
./ritual build-site --collections --collection-sort price

Build with EUR as the default price currency:

Terminal window
./ritual build-site --currencies eur

Build with only USD and EUR (no TIX):

Terminal window
./ritual build-site --currencies "usd,eur"

When the --decks, --collections, and --wanted-lists flags are omitted, build-site falls back to the publish lists in your site configurationsite.includeDecks, site.includeCollections, and site.includeWantedLists. Each defaults to the wildcard ["*"] (build everything), so a fresh project builds all lists with no extra configuration.

Setting a list to specific display names publishes only those lists and filters out the rest. For example, with:

"site": {
"includeDecks": ["Izzet Storm", "Atraxa Superfriends"],
"includeCollections": ["*"],
"includeWantedLists": []
}

build-site publishes only those two decks, every collection, and no wanted lists. The matching flag always overrides the config for that category in a single run, bypassing both the include* and exclude* lists — --decks "Mono Red Aggro" builds just that deck regardless of includeDecks or excludeDecks.

Each category also has an exclude* list (site.excludeDecks, site.excludeCollections, site.excludeWantedLists) that drops lists by display name even when the include* list selects them — exclusion always wins. The exclude lists default to empty and have no wildcard. For example, "includeDecks": ["*"] with "excludeDecks": ["Untuned Brew"] publishes every deck except “Untuned Brew”. The admin Manage Lists page toggles these per list; see publishing visibility.

You can edit these lists from the admin Settings page, with config-set, or by hand.

The generated site ships with multiple themes selectable at runtime, or setting a default theme at build time. The --theme flag controls which theme is the initial one served (i.e. what users see on first visit before they pick something). Ten Magic-flavored “guild” palettes are available alongside the default, each with a primary background color and a contrasting highlight color used for buttons, focus rings, and accents:

ThemeBackgroundHighlight
defaultdark violetviolet
orzhovdark graywhite
izzetdark bluered
gruuldark greenred
rakdosdark grayred
selesnyaoff-whitegreen
azoriusoff-whiteblue
borosoff-whitered
dimirdark grayblue
simicdark bluegreen
golgaridark graygreen

Each theme also has an inverted variant accessible by appending -inverted to its name (e.g. azorius-inverted, boros-inverted). Inverted themes swap the background and highlight colors so the highlight becomes the dominant background and the original background becomes the accent — shade and intensity are adjusted so the resulting palette stays comfortable to read.

The app’s flame logo — both the header icon and the browser-tab favicon — is tinted from each theme’s accent, so switching themes recolors the icon to match (a vivid flame for saturated accents, a pale “white flame” for near-neutral ones).

Terminal window
./ritual build-site --theme izzet
./ritual build-site --theme boros-inverted

The header’s Theme button opens a picker popover listing every built-in palette with a preview swatch. Clicking a palette switches the base theme; if the visitor has in-progress customizations, the picker first asks for confirmation before discarding them. Only the chosen theme name (and any explicit overrides) is stored in localStorage — when a future build ships updated built-in palettes, visitors who haven’t customized see those updates automatically.

For per-variable tweaks, the picker has a Customize theme… entry that opens the in-browser theme editor. The editor exposes every CSS variable as a labeled control (OKLch sliders for colors, number inputs for sizes) grouped into tabs — including a Flame icon group for the six gradient stops of the app logo — lets the user start from any built-in palette, and live-previews changes across all pages (the flame logo and favicon recolor as you drag). The user’s edits persist in localStorage so subsequent visits restore their custom palette.

A Download JSON button in the editor exports the full set of variables as a .json file. To bake one of those palettes back into a build, pass it to --theme-file:

Terminal window
./ritual build-site --theme-file ./my-palette.json
./ritual build-site --theme-file ./palette-a.json --theme-file ./palette-b.json
./ritual build-site --theme-file ./my-palette.json --theme my-palette

Each --theme-file adds a custom theme alongside the built-ins under the name field declared in the JSON. Combining --theme-file with --theme <custom-name> makes that custom theme the initial default for new visitors. The JSON shape is:

{
"name": "my-palette",
"description": "Optional human-readable description",
"variables": {
"--bg-body": "oklch(20% 0.02 260)",
"--accent": "oklch(60% 0.15 320)"
}
}

Theme names must be lowercase letters, digits, and hyphens, and may not collide with any built-in theme name.

Generates a single-page application in the dist/ directory containing:

  • index.html — SPA shell that loads the application
  • app.js — Bundled SPA with client-side routing
  • index.json — Deck and collection listing used by the index page
  • decks/{slug}.json — Full deck data loaded on demand
  • decks/{slug}.txt — Exportable deck lists
  • collections/{slug}.json — Full collection data with pricing loaded on demand
  • collections/{slug}.md — Original collection Markdown file for download
  • collections/{slug}.csv — Collection exported as CSV for importing into other sites
  • wanted/{slug}.json — Full wanted list data with pricing loaded on demand
  • wanted/{slug}.md — Original wanted list Markdown file for download
  • styles.css — Bundled CSS
  • Responsive design for desktop and mobile
  • Dark mode support
  • Client-side hash routing (#/ for index, #/deck/{slug} for deck pages, #/collection/{slug} for collection pages, #/wanted/{slug} for wanted list pages)
  • Navigation bar with “Decks”, “Collections”, and “Wanted Lists” links always visible
  • Page transition animations

Deck and collection pages offer four view modes, toggled via buttons in the toolbar:

  • Binder (▦) — dense card image grid
  • List (☰) — compact text rows showing name, mana cost, and price
  • Row (⧗) — horizontally scrolling fan of overlapping cards
  • Column (▥) — vertical stacked columns of cards

In binder, row, and column modes, hovering a card shows its name and price in an overlay at the bottom of the card image.

A card size selector (L / M / S) appears in the toolbar for binder, row, and column modes. The three sizes are:

SizeCard width
L (Large, default)190 px
M (Medium)140 px
S (Small)100 px

Card size applies uniformly across all three image views. In row and column modes, the row width and column width shift automatically to match the selected card size.

Any list page (deck, collection, or wanted list) lets you select cards across every view mode and act on the whole selection at once. Hovering a card in binder, row, or column mode reveals a translucent checkbox in its top-left corner; clicking it marks the card with a checkmark in the current theme’s accent color. In list view the checkbox sits at the far left of each row. You can also Ctrl-click (or ⌘-click on macOS) anywhere on a card in any view to toggle its selection without opening the card modal.

A card shown with a quantity (e.g. in a deck, or a grouped duplicate in a collection) is selected as all of its copies at once, and the count reflects the individual copies (selecting 4× Lightning Bolt counts as four). When you later remove some — but not all — of a group’s copies (from the dialog below), its checkbox shows a dash instead of a checkmark to indicate the partial selection.

Once at least one card is selected, a Selected (N) button appears in the toolbar (N is the running count of selected copies for the list you’re viewing). The selection survives changes to grouping, sorting, and view mode. Opening the button reveals a menu of bulk actions over that list’s selection:

  • Copy as Text — copies a quantity-prefixed list (N Card Name (SET:Collector Number)) to the clipboard, matching the deck Copy button’s format
  • Copy as CSV — copies the same selection as CSV with a Name,Set,Collector Number,Finish,Condition,Quantity header, matching the Download CSV output but scoped to the selected cards
  • Add to Trade — adds the selected cards to the active Trade Planner (deck and collection cards go to the offering side, wanted-list cards to the receiving side). Name-only cards (no pinned printing) prompt for a printing one at a time, exactly like the single-card add
  • Clear selection — deselects the current list’s cards only

Identical printings are merged and their quantities summed in both copy formats.

Selections are held globally, so they persist as you navigate between lists. Whenever anything is selected, an All Selected (N) button is shown in the top navbar (N is the total across every list) — on every page, including the index and Trade Planner, not just list pages. Its menu offers the same actions but over the entire cross-list selection, and its Clear all selections entry wipes every list at once. This makes it easy to gather cards from several decks, collections, and wanted lists and then copy or trade them together.

The menu’s View all selections… entry opens a dialog listing every selected card — with its quantity, printing, foil/etched finish, and condition — alongside the list it came from. The cards can be shown in selection order or grouped by source, hovering a row previews the card art, and each row’s ✕ removes a single copy (so a group drops to ). The dialog repeats the Copy as Text / Copy as CSV and Clear all selections actions.

Deck pages include a “Lowest Price” toggle that swaps all cards to their cheapest available printing. When enabled, card images and prices update to reflect the lowest-priced version. Only printings with a listed price are considered.

On the home page deck list and in the Quick Switch dialog, each deck cover shows the deck’s format rather than a raw card count. Format is read from the deck’s format: frontmatter field, falling back to section heuristics (a Commander section implies Commander; an Oathbreaker / Signature Spell section implies Oathbreaker).

  • Commander and Oathbreaker decks display just the format name (e.g. Commander).
  • For other supported formats (Standard, Modern, Pioneer, Legacy, Vintage, Pauper, Historic, Brawl, Duel Commander, Pre-Modern, Limited), the format name appears alone when the main-deck card count matches the format’s expected size (60 for most, 100 for Commander/Duel Commander, 40 for Limited).
  • When the main-deck count is unusual for the format, a smaller parenthetical card count is shown next to the format name — e.g. Modern (62 cards) for a Modern deck with 62 mainboard cards.
  • Decks without a recognized format (no format: field and no Commander/Oathbreaker section) fall back to the original N cards display.

The main-deck count includes the commander/oathbreaker section plus the mainboard, but excludes sideboard, maybeboard, and token sections so a 60-card format with a sideboard still reports 60.

Collections and wanted lists continue to display a plain N cards count, since their card count is the primary fact about them.

Every tab of the home page — Decks, Collections, and Wanted Lists — includes a shared filter toolbar (matching the look of the deck and collection page toolbars) for re-sorting and grouping the list. Each tab keeps its own selections, and state is per-session — selections reset on reload.

Sort options (default: Alphabetical):

  • Alphabetical — A–Z by name (uses locale-aware case-insensitive comparison).
  • Recently updated — newest first, derived from the most recent changelog entry, falling back to the source file’s mtime. Items with no timestamp sort last.
  • Current price — highest current total first, in the active currency.
  • Lowest price — highest “lowest possible” total first (the sum of the cheapest available printing of each card), in the active currency. Decks only — this option does not appear on the Collections or Wanted Lists tabs, where a per-card cheapest-printing total isn’t meaningful.

A Reverse toggle next to the selects flips the resulting order.

Grouping applies only to the Decks tab, which adds a Group selector (default: None):

  • None — single flat grid.
  • Format — splits decks into one section per format (Commander, Modern, Standard, …) with the format label as the section heading. Decks without a recognized format land in a final Other bucket. The active sort is applied within each group.

The Collections and Wanted Lists tabs have no format dimension to group by, so the Group selector is omitted from their toolbars entirely — they show only the Sort selector (Alphabetical, Recently updated, Current price) and the Reverse toggle.

The generated site includes a Prices dropdown in the header for switching between USD (TCGPlayer), EUR (Cardmarket), and TIX (MTGO) at runtime. The dropdown only shows currencies selected by the --currencies flag. When switching currencies:

  • All displayed prices update to the selected currency
  • Deck totals and section totals recalculate
  • Collection prices recompute using the card’s finish-specific price in the new currency
  • The “Lowest Price” toggle finds the cheapest printing per the active currency — images update accordingly
  • Price bracket grouping labels adapt to the active currency symbol

The --currencies flag controls which currencies are available on the site. The first currency listed becomes the default shown on load. Users can switch between available currencies at any time using the dropdown.

The generated site displays a disclaimer below the header showing the date prices were retrieved. Prices are fetched from Scryfall at build time and reflect values as of the build date. The disclaimer reads: “Prices accurate as of <date>”.

Every deck, collection, and wanted-list page has an Update Prices button on the right of its filter toolbar (also shown while editing). It is a no-op until pressed; clicking it batch-fetches current prices for that page’s cards directly from Scryfall (into an in-memory, per-tab session cache) and the displayed per-card prices and totals update in place. Nothing is written to disk — the refresh lives only in the current browser tab.

If a refresh only updates some cards (for example, a card Scryfall no longer returns by id), the remaining cards keep their older build-time price. When prices on a page end up with mixed dates, a small expandable warning appears listing the cards whose prices are now older than the rest. Refreshing again so every card is covered clears the warning. The same session cache is shared with the card search in the public editor and the Trade Planner, so a card fetched once is reused without another request.

A build pulls card data and prices from three places, in order:

  1. Automatic bulk download — if the cache is empty, more than a week old, or missing more than 100 of the requested cards, the full Scryfall bulk dataset is downloaded first (equivalent to ./ritual cache preload-all).

  2. Bulk price-refresh prompt — otherwise, if more than 100 cards have prices older than 24 hours, build-site offers a bulk redownload (fresh prices for everything in one request) instead of refreshing each card individually:

    287 of 320 card(s) have prices older than 24 hours.
    Redownloading the Scryfall bulk card cache (includes fresh prices) would be faster than refreshing each card individually.
    Redownload the latest Scryfall card cache now? [y/N]
  3. Per-card fetch — every card whose cached price is stale (>24h) is then refetched individually; cards with fresh prices are reused from cache.

When stdin is not a TTY (e.g. a CI pipeline) the prompt can’t be answered, so it resolves to its default of No and per-card refreshing proceeds as normal.

The three --*-refresh flags answer the prompt non-interactively and control the bulk download:

FlagBulk download (steps 1 & 2)Per-card refresh of stale prices (step 3)
--allow-refreshAllowedYes
--allow-refresh-no-bulkSuppressedYes
--no-refreshSuppressedNo (uses cached prices as-is)
Terminal window
./ritual build-site --allow-refresh # fastest full refresh
./ritual build-site --allow-refresh-no-bulk # refresh prices without the big download
./ritual build-site --no-refresh # build entirely from the existing cache

Note: --allow-refresh-no-bulk and --no-refresh also suppress the automatic bulk download (step 1). On an empty or very stale cache this forces every card to be fetched individually, which is slow and can hit Scryfall rate limits — use them when you already have a populated cache.

These flags are also what bun run dev serve-site requires — see Development → Dev Workflow.

A Quick switch button (centered in the site header on desktop, right-aligned on mobile) opens a command-palette-style dialog for jumping between any deck, collection, or wanted list on the site. The same dialog also opens with the keyboard shortcut Ctrl+K (or Cmd+K on macOS).

When the search field is empty, the dialog lists every deck, collection, and wanted list. As soon as you start typing, results are grouped into four priority tiers:

  1. Lists — matches against deck, collection, and wanted-list names (highest priority).
  2. Commanders — matches against the commander of any deck. Selecting one opens the deck containing that commander.
  3. Cards — matches against the name of any card in any list. Selecting one opens the list containing that card. The same card can produce multiple entries (one per containing list), and each row identifies the destination list in its subtitle.
  4. Printings — matches against the set:collector code of any card (e.g. mkm:42). The set:collector code is shown as the primary label (uppercased, e.g. MKM:42) and the card’s name appears alongside in muted italics for context. Selecting one opens the list containing that printing.

Each row shows a thumbnail (the list’s featured art for list rows, the card art for commander and card rows), a kind tag (Deck / Collection / Wanted / Commander / Card), and the destination context. Commander and card matches require per-list detail data; the dialog pre-fetches that data the first time it opens, so card matches start appearing once the data has loaded.

Keyboard controls inside the dialog:

KeyAction
/ Move the highlighted result
EnterOpen the highlighted entry
EscClose the dialog

Clicking a result also opens it. Clicking the darkened backdrop closes the dialog. The dialog closes automatically after navigation.

Both deck and collection pages share a unified card detail modal. Clicking any card opens a modal showing:

  • Card image with flip support for double-faced cards
  • Card name, type line, mana cost, and oracle text
  • Price, set info, rarity, and other metadata
  • A “View on Scryfall” link to the card’s Scryfall page
  • An “Other Printings” button showing a paginated binder-style grid (8 per page) of all known printings, sorted by release date (newest first) by default, each linking to Scryfall with prices. Sorting can be changed via a dropdown to release date, set name, or price, with a toggle to reverse the sort direction.

When a card cannot be priced in a selected currency (e.g., a paper-only card has no TIX price, or an MTGO-only card has no USD/EUR price), it is omitted from price totals. A collapsible warning banner appears at the top of the deck page listing cards with missing prices for the active currency. The banner updates reactively when switching currencies.

On the index page, deck and collection entries with missing prices display the total as “At least $X.XX (missing N cards)” instead of the raw total, making it clear the price is incomplete. The “lowest price” variant is hidden when a deck has missing prices to avoid confusion.

When --collections is specified, collection files from the collections/ directory are included in the generated site. Each collection card must have a set code and collector number (e.g., - Sol Ring (C19:221)). Cards without this information are skipped with a warning.

Collection pages show:

  • Total collection value based on specific printing and finish prices
  • Individual card prices, conditions, finishes, and set/collector number in the card detail modal
  • A “View on Scryfall” link in the card detail modal that opens the card’s Scryfall page
  • An “Other Printings” button that shows a paginated binder-style grid (8 per page) of all known printings of the card, sorted by release date (newest first) by default, each linking to Scryfall. Sorting can be changed via a dropdown to release date, set name, or price, with a toggle to reverse the sort direction.
  • Cards displayed individually by default (not grouped), with a “Group Duplicates” toggle
  • File order as the default sort, with options for name, price, set code, type, mana value, and color identity
  • Grouping by section (the default when the collection has two or more sections), type, mana value, color identity, price brackets, or ungrouped
  • Price bracket grouping with three strategies: Archidekt-style brackets, every $5, or every $10
  • A “No Price Data” group that appears at the bottom when grouping by price, collecting cards without price data for their finish
  • Download as original Markdown or CSV for importing into other sites
  • Section/group price totals that update dynamically

When --wanted-lists is specified, wanted list files from the wanted/ directory are included in the generated site. Unlike collections, wanted list entries can have varying levels of specificity — from just a card name to a fully pinned printing and finish.

Wanted list pages show:

  • Total wanted list value based on current card prices
  • Prices always reflect the cheapest option for each entry’s state:
    • Name only entries use the cheapest printing across all sets
    • Printing entries use the cheapest finish of that exact printing
    • Fully specified entries use the exact printing and finish specified
  • Individual card prices in the card detail modal
  • State indicator showing whether each card is name-only, printing-specific, or fully specified
  • Grouping by section (the default when the wanted list has two or more sections), type, mana value, color identity, price brackets, printing (whether a card is pinned to a specific printing), or ungrouped
  • Download as original Markdown
  • No condition display (wanted lists track desired cards, not owned cards)

Deck pages include:

  • Total deck price (mainboard + sideboard) displayed at the top, updating when the “Lowest Price” toggle is enabled
  • When extras are visible, a separate parenthetical “all cards” total is shown next to the main price
  • “Lowest Price” toggle that swaps all cards to their cheapest available printing (images and prices update)
  • Card detail modal with Scryfall link, other printings (paginated, sortable by release date, set name, or price), and full card details
  • Section/group price totals shown next to card counts
  • Grouping by type, section, mana value, color identity, price brackets, printing (whether a card is pinned to a specific printing), or ungrouped — applies to mainboard only
  • Price bracket grouping with three strategies: Archidekt-style brackets, every $5, or every $10
  • Sideboard always displayed in its own section at the bottom, ungrouped
  • Extras (maybeboard, tokens) displayed below sideboard, toggled via “Hide Extras” checkbox
  • Only printings with a listed price are considered for price analysis
  • View Changes button (when a changelog exists) opens a paginated modal showing the edit history

When a deck or collection has a .changes.md changelog file (created by the admin editor when saving changes), its edit history is included in the generated site.

  • A View Changes button appears next to the download buttons in the page header
  • Clicking it opens a modal dialog showing paginated change entries, sorted most recent first
  • Each page shows one editing session with its timestamp and a list of additions, removals, and other changes
  • Prev/Next buttons allow paging through older and newer changes
  • Card names in the change list are clickable links that open the card detail modal
  • Hovering a card name shows a preview image of the card

Cards referenced in changelogs that are no longer in the deck or collection are automatically resolved during the build so their card data is available for previews and modals.

Although the generated site is static (no server), the navbar has an Edit toggle (top-right) that opens the same editor used in the admin site, running entirely in the browser, for whichever deck, collection, or wanted list you’re viewing. (The toggle is present site-wide but disabled on pages with nothing to edit, such as the index.) Edits are ephemeral — nothing is saved to a server and nothing is persisted unless you explicitly choose to.

  • Edited vs. published — while editing, the navbar grows a second row that makes it clear you are viewing a local copy, with an Original / Edited toggle to switch between your changes and the published version, and a Discard button to drop them. Press Done (the same navbar toggle) to leave edit mode.
  • Card search — adding cards searches Scryfall directly (preferring the shared session cache), the same as the Trade Planner.
  • Move a card to another list — the per-card menu, the per-list Selected menu, and the cross-list All Selected navbar menu each offer a single Move to list… item that opens a picker listing your other decks, collections, and wanted lists. Moving a card removes it from the list you’re editing (it disappears from the edited view) and records the move in your exported change file. Moving a printing-less card into a collection (which needs a specific printing) opens the same printing picker the Trade Planner uses. Because the public site has no server, the destination list is only updated when the change file is later imported into the admin editor and saved.
  • Export your edits — the Export… panel offers two ways to keep your changes:
    • Download change list (JSON) or Copy JSON — a portable change file that the admin editor can later import (see Import changes) to apply your edits to the real list. Imported changes are re-targeted to the current card IDs and surfaced for review before saving.
    • Download updated file — a full deck .txt (or collection/wanted .md/.csv) with the edits already applied.
  • Load changes — the Load Changes… button (next to Export…) opens a dialog where you can upload or paste a change-list JSON (one exported from this site or from the admin editor) and apply it to the list you’re editing. The changes load as pending edits, re-targeted to the current card IDs; any that can’t be matched to a card in the list are reported and skipped. This is the same machinery the admin editor uses to import changes.
  • Save to this browser (opt-in) — the Export panel can also save the current edit session to localStorage. This never happens automatically. When you return to a list with a saved session, the editor offers to Restore it (applied through the same safe re-target path as import); Clear saved edits removes it.

After building, use the serve command to preview locally:

Terminal window
./ritual serve

To build and serve in a single step, use serve-site:

Terminal window
./ritual serve-site

The generated site includes a Trade Planner page accessible via the “Trade” link in the site navigation at #/trade. This is a fully client-side, ephemeral tool — no data is persisted between page refreshes.

The page provides a two-column layout for planning a trade:

The left column is for cards you are offering. It searches cards from the collections included in this site. A “Include Decks in Search” toggle (disabled by default) extends the search to include cards from your decks as well.

  • Type a card name in the search box to get autocomplete suggestions showing card name and source list
  • Each result is deduplicated per source — if the same card appears in multiple collections, each collection shows up as a separate autocomplete result
  • Cards show: thumbnail image, name, set code and collector number, finish, condition, and price
  • If a deck card has no specific printing pinned, selecting it opens the printing picker so you can choose one (the deck source is preserved on the resulting trade row)
  • Sort by card name or price (toggle ascending/descending independently)
  • Price total shown at the bottom of the column

Quantity caps: Each trade row’s quantity stepper caps at the maximum number of that exact variant available in its source — for collections this is the count of identical note-less entries (same name, set, collector number, finish, condition); for decks it is the sum across mainboard/sideboard/etc. for that printing in that deck. When only one copy exists the stepper is hidden and a fixed quantity of 1 is displayed.

Editing picker-sourced rows: Trade rows added via the printing picker (Scryfall searches on the right, deck cards without a pinned printing on the left) get a small yellow pencil button to the left of the quantity controls. Clicking it re-opens the printing picker for that card; choosing a printing replaces the row in place while preserving its quantity.

The right column is for cards the other party is offering. By default it searches your site’s wanted lists. A “Search Scryfall” toggle switches to a direct Scryfall search.

Wanted list mode (default): Search across all wanted lists on this site instance. Results show card name and source wanted list name.

Scryfall mode: When enabled, autocomplete calls the Scryfall API directly from the browser. Selecting a card name opens a printing picker that shows all available printings, paginated 8 at a time. The picker has a set-code filter input (e.g. typing mkm or lea narrows the results) and hovering an entry shows the full card art preview. Choose a printing and finish, then click “Add to Trade” to add the card.

The toolbar’s Update prices button refetches current prices from Scryfall for the cards currently loaded on the trade page (only — not your full collection). It batches requests through Scryfall’s /cards/collection endpoint (75 IDs per request) and updates each row’s price and finish in place. A toast confirms how many cards were updated.

Hovering over a card thumbnail (in the trade list, autocomplete suggestions, or printing picker) shows an enlarged preview of the card art that follows the mouse cursor.

On narrow screens (≤768px), the two-column layout collapses to a single-pane view. Tab buttons at the top switch between “My Cards” and “Their Cards”. Each pane fills the full screen width, with its own search, sort controls, card list, and price total.