takescake

Card-Specific Social Sharing: The ?art Parameter Story

2025-10-04

Card-Specific Social Sharing: The ?art Parameter Story

Have you ever wanted to share a specific printing of a Magic card - maybe a gorgeous Secret Lair alternate art or a vintage frame version - only to have the social media preview show the wrong artwork? We had this exact problem, and today I want to share how we solved it with a simple, elegant query parameter.

The Problem

Our card pages support multiple printings of the same card. For example, "Homeward Path" has been printed in:

  • Commander 2013 (original art)
  • Commander Legends
  • Secret Lair Drop (alternate art)
  • Judge Gift Cards
  • And more...

When users clicked on different prints in our gallery, they could view each one. But when they tried to share their favorite art variant, Discord/Twitter/Facebook always showed the default printing in the preview card.

User expectation: "I want to share this Secret Lair version!"
Actual result: Generic preview with Commander 2013 art

Why Social Media Previews Matter

Social media platforms like Discord, Twitter, and Facebook use Open Graph and Twitter Card metadata to generate rich previews when someone shares a link. These previews include:

  • Title (the card name)
  • Description (card text)
  • Image (card artwork)
  • URL (the link being shared)

The image is especially important for Magic cards because:

  1. Players often share cards based on artwork
  2. Alternate arts are collectible and worth discussing
  3. Visual recognition is how players identify cards

When the wrong artwork appears, it breaks the sharing experience.

The Simple Solution: Query Parameters

We implemented art-specific sharing using URL query parameters:

Default (No Parameter)

https://takescake.com/cards/cb8ec264-8223-41f2-8f2c-37c918a573fa

Shows the first/default printing (usually the oldest or most recent).

Specific Art Variant

https://takescake.com/cards/cb8ec264-8223-41f2-8f2c-37c918a573fa?art=b23a6f7e-c0b8-4e85-9270-254bfb221ae0

Shows the specific print with ID b23a6f7e-c0b8-4e85-9270-254bfb221ae0.

The ?art={card-id} parameter tells the page which printing to feature in social previews.

How It Works

1. URL Structure

Magic cards have two types of IDs:

  • Oracle ID: Identifies the card concept (e.g., "Homeward Path" as a game piece)
  • Card ID: Identifies a specific printing (e.g., "Homeward Path from Secret Lair")

Our URLs use oracle IDs as the path:

/cards/{oracle-id}

The query parameter specifies the card ID:

?art={card-id}

This makes sense conceptually: "Show me Homeward Path (oracle), specifically the Secret Lair printing (card)."

2. Metadata Generation

When generating Open Graph and Twitter Card metadata, we check for the art parameter:

export async function generateMetadata({ 
  params, 
  searchParams 
}: { 
  params: { oracle: string }, 
  searchParams: { art?: string } 
}): Promise<Metadata> {
  
  // Get all printings for this oracle ID
  const versions = listVersionsForOracle(all, params.oracle, 'released', 'desc', 'en');
  
  // Default to first printing
  let featuredCard = versions[0];
  
  // If ?art={id} provided, use that specific printing
  if (searchParams.art) {
    const specificCard = versions.find(v => v.id === searchParams.art);
    if (specificCard) {
      featuredCard = specificCard;
    }
  }
  
  // Use the featured card's image for social previews
  const cardImage = getCardImagePath(featuredCard.id, 'large');
  
  return {
    openGraph: {
      url: searchParams.art 
        ? `${baseUrl}?art=${searchParams.art}` 
        : baseUrl,
      images: [{ url: cardImage, ... }]
    },
    twitter: {
      images: [cardImage]
    }
  };
}

The key insight: we only change which card image is used. The title and description stay the same because oracle text doesn't change between printings.

3. Automatic URL Updates

When users click different prints in the gallery, we automatically update the URL using JavaScript:

useEffect(() => {
  if (typeof window === 'undefined' || !featuredId) return;
  
  const url = new URL(window.location.href);
  const currentArt = url.searchParams.get('art');
  
  // Update URL if featured print changed
  if (featuredId !== initialFeaturedId) {
    if (currentArt !== featuredId) {
      url.searchParams.set('art', featuredId);
      window.history.replaceState({}, '', url.toString());
    }
  } else {
    // Remove art param if back to default
    if (currentArt) {
      url.searchParams.delete('art');
      window.history.replaceState({}, '', url.toString());
    }
  }
}, [featuredId, initialFeaturedId]);

We use replaceState (not pushState) so the URL updates don't create browser history entries. Users can still use the back button normally.

4. Image Path Resolution

The tricky part was making sure the correct image was used. Our card images are stored as:

public/cards/{card-id}-normal.jpg

Each individual print has its own image file. The original implementation was looking up images by oracle ID, which only works for the default print.

The fix:

// Construct direct path to specific print's image
const directLocalPath = `/cards/${featuredCard.id}-normal.jpg`;
const directLocalAbsolute = `${siteUrl}${directLocalPath}`;

// Fallback to manifest lookup if needed
const manifestImg = getCardImagePath(manifest, featuredCard.oracle_id, 'large');

// Priority: direct path > manifest > Scryfall remote
const finalImage = directLocalAbsolute || manifestImg || scryfallFallback;

Now we construct the image path directly from the card ID, ensuring we always get the right artwork.

User Experience Flow

Here's what happens when a user wants to share a specific print:

Step 1: Browse Prints

User visits a card page and sees multiple printings in the gallery.

Step 2: Select Favorite Art

User clicks on their favorite alternate art (e.g., Secret Lair version).

Step 3: URL Updates

The browser's address bar automatically updates to include ?art={card-id}.

Step 4: Copy and Share

User copies the URL from the address bar.

Step 5: Preview Generated

When pasted into Discord/Twitter/Facebook, the social media platform:

  • Fetches the URL
  • Reads the Open Graph metadata
  • Sees the specific card image for that print
  • Displays the correct artwork in the preview!

Step 6: Others Click Through

When friends click the link, they see:

  • The same featured print
  • All other available prints in the gallery
  • Can browse and share their own favorite

Why This Design is Good

1. Simple Implementation

No complex routing. No database changes. Just a query parameter check in metadata generation.

2. Backward Compatible

Old links without ?art= parameter still work perfectly - they show the default print.

3. Shareable URLs

URLs are human-readable and can be copy-pasted, sent in messages, bookmarked, etc.

4. No Performance Cost

Parameter checking happens during SSR (server-side rendering) - zero client-side overhead.

5. SEO Friendly

Search engines see the canonical URL with the art parameter, improving indexing of popular alternate arts.

6. User Discoverable

Users don't need to be told about this feature - it just works when they click different prints.

Edge Cases Handled

Non-Existent Card IDs

If someone manually edits the URL with a fake card ID:

if (searchParams.art) {
  const specificCard = versions.find(v => v.id === searchParams.art);
  if (specificCard) {
    featuredCard = specificCard;
  }
  // If not found, fall back to default (no error)
}

We silently fall back to the default print. No errors, no confusion.

Missing Images

If the local image file doesn't exist for a specific print:

const directLocalAbsolute = `${siteUrl}${directLocalPath}`;
const manifestImg = getCardImagePath(...);
const scryfallFallback = card.image_uris?.large;

const finalImage = directLocalAbsolute || manifestImg || scryfallFallback;

We have multiple fallback layers ending with Scryfall's remote images.

Social Media Cache

Discord and other platforms cache Open Graph metadata for 24 hours. If we change images, it might take a day for updates to show.

Solution: The ?art= parameter creates a unique URL per print, so each gets its own cache entry.

Testing Social Media Previews

After implementing this feature, we tested using:

Discord

Paste URLs in any channel. Discord fetches and displays preview cards immediately.

Twitter Card Validator

https://cards-dev.twitter.com/validator

Enter the URL to see how Twitter will display it.

Facebook Debugger

https://developers.facebook.com/tools/debug/

Shows Open Graph metadata and preview rendering.

LinkedIn Post Inspector

https://www.linkedin.com/post-inspector/

Validates Open Graph tags and preview display.

All platforms now correctly show art-specific previews! 🎉

Real-World Examples

Example 1: Homeward Path Variants

Default Print (Commander 2013):

https://takescake.com/cards/cb8ec264-8223-41f2-8f2c-37c918a573fa

Secret Lair Drop:

https://takescake.com/cards/cb8ec264-8223-41f2-8f2c-37c918a573fa?art=b23a6f7e-c0b8-4e85-9270-254bfb221ae0

Example 2: Sol Ring Variations

With 50+ printings, Sol Ring is perfect for this feature:

Original Alpha:

https://takescake.com/cards/5effb651-f9e0-4c14-8192-bd0e132b8d5d?art=fd1b7c92-a11b-447a-bcef-03c7f5e8fd55

Kaladesh Inventions:

https://takescake.com/cards/5effb651-f9e0-4c14-8192-bd0e132b8d5d?art=895e7a6e-cd10-4a00-bfec-30e6330e193c

Each URL shows the exact printing in social previews.

Future Enhancements

We're considering additional improvements:

1. Share Buttons

Add explicit "Share this print" buttons with pre-filled social links:

<a href="https://twitter.com/intent/tweet?url={cardUrl}?art={cardId}">
  Share on Twitter
</a>

2. Set Symbol Badges

Add visual indicators showing which set each print is from.

3. Print Comparison Mode

Side-by-side view of multiple arts with shareable comparison URLs.

4. Collector Numbers

Include collector number in the URL for even more specificity.

For now, the simple ?art= parameter solves 99% of sharing needs.

Try It Yourself

Visit any card with multiple printings on takescake.com:

  1. Click different prints in the gallery
  2. Watch the URL update automatically
  3. Copy the URL and share it on Discord/Twitter
  4. See the correct artwork in the preview!

It's a small feature, but it makes sharing Magic cards feel natural and precise.


Technical Note: Implementation spans app/cards/[oracle]/page.tsx (metadata generation) and components/CardPrintGallery.tsx (URL sync). Zero performance impact, full backward compatibility.

Related Posts