- Docs
- What To Build
- Tarot Reading App
Build a Tarot Reading App
In this tutorial you will build a tarot reading app with three modes: daily card, three-card spread (past/present/future), and a yes/no oracle. One HTML file, no build tools, no frameworks.
What you will build
- Three tabs for different reading types
- Card imagery with names, keywords, and interpretations
- Reversed card handling (cards can appear upside-down, changing their meaning)
The complete code
Create a file called tarot.html and paste this entire block:
<!DOCTYPE html>
<html>
<head>
<title>Tarot Reading</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; max-width: 700px; margin: 0 auto; padding: 20px; background: #0f0f1a; color: #e0e0e0; }
h1 { text-align: center; margin-bottom: 24px; color: #c9a0dc; }
.tabs { display: flex; gap: 8px; margin-bottom: 24px; justify-content: center; }
.tabs button {
padding: 10px 20px; border: 1px solid #333; border-radius: 8px;
background: #1a1a2e; color: #aaa; cursor: pointer; font-size: 14px;
}
.tabs button.active { background: #7c3aed; color: white; border-color: #7c3aed; }
.cards { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; margin-bottom: 24px; }
.card {
background: #1a1a2e; border: 1px solid #333; border-radius: 12px; padding: 16px;
width: 200px; text-align: center;
}
.card img { width: 100%; border-radius: 8px; margin-bottom: 12px; }
.card img.reversed { transform: rotate(180deg); }
.card .position { font-size: 12px; text-transform: uppercase; color: #7c3aed; font-weight: 600; margin-bottom: 8px; }
.card .name { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
.card .reversed-label { font-size: 11px; color: #e57373; margin-bottom: 4px; }
.card .keywords { font-size: 12px; color: #999; margin-bottom: 8px; }
.card .meaning { font-size: 13px; line-height: 1.5; color: #ccc; }
.answer-box { text-align: center; background: #1a1a2e; border: 1px solid #333; border-radius: 12px; padding: 24px; margin-bottom: 24px; }
.answer-box .answer { font-size: 48px; font-weight: 700; color: #c9a0dc; margin-bottom: 8px; }
.message { text-align: center; font-size: 15px; line-height: 1.6; color: #ccc; padding: 0 20px; margin-bottom: 24px; }
.draw-btn {
display: block; width: 100%; max-width: 300px; margin: 0 auto 24px; padding: 14px;
background: #7c3aed; color: white; border: none; border-radius: 12px;
font-size: 16px; font-weight: 600; cursor: pointer;
}
.draw-btn:hover { background: #6d28d9; }
.draw-btn:disabled { background: #444; cursor: not-allowed; }
.loading { text-align: center; padding: 40px; color: #888; }
</style>
</head>
<body>
<h1>Tarot Reading</h1>
<div class="tabs">
<button class="active" onclick="switchTab('daily')">Daily Card</button>
<button onclick="switchTab('three-card')">Past / Present / Future</button>
<button onclick="switchTab('yes-no')">Yes / No</button>
</div>
<button class="draw-btn" id="draw-btn" onclick="draw()">Draw Cards</button>
<div id="result"></div>
<script>
const API_KEY = 'YOUR_API_KEY'; // Replace with your key from roxyapi.com/pricing
let currentTab = 'daily';
function switchTab(tab) {
currentTab = tab;
document.querySelectorAll('.tabs button').forEach(b => b.classList.remove('active'));
event.currentTarget.classList.add('active');
document.getElementById('result').innerHTML = '';
document.getElementById('draw-btn').textContent =
tab === 'daily' ? 'Draw Card' :
tab === 'three-card' ? 'Draw Three Cards' : 'Ask the Oracle';
}
function renderCard(card, position) {
return `
<div class="card">
${position ? '<div class="position">' + position + '</div>' : ''}
${card.imageUrl ? '<img src="' + card.imageUrl + '" alt="' + card.name + '"' + (card.reversed ? ' class="reversed"' : '') + ' />' : ''}
<div class="name">${card.name}</div>
${card.reversed ? '<div class="reversed-label">Reversed</div>' : ''}
<div class="keywords">${(card.keywords || []).join(' · ')}</div>
<div class="meaning">${card.meaning || ''}</div>
</div>
`;
}
async function draw() {
const btn = document.getElementById('draw-btn');
btn.disabled = true;
const el = document.getElementById('result');
el.innerHTML = '<div class="loading">Shuffling the deck...</div>';
const endpoints = {
'daily': '/api/v2/tarot/daily',
'three-card': '/api/v2/tarot/spreads/three-card',
'yes-no': '/api/v2/tarot/yes-no'
};
const response = await fetch('https://roxyapi.com' + endpoints[currentTab], {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({})
});
const data = await response.json();
btn.disabled = false;
if (currentTab === 'daily') {
el.innerHTML =
'<div class="cards">' + renderCard(data.card) + '</div>' +
'<div class="message">' + data.dailyMessage + '</div>';
} else if (currentTab === 'three-card') {
el.innerHTML = '<div class="cards">' +
data.positions.map(p => renderCard(p.card, p.name)).join('') +
'</div>';
} else {
el.innerHTML =
'<div class="answer-box"><div class="answer">' + data.answer + '</div></div>' +
'<div class="cards">' + renderCard(data.card) + '</div>';
}
}
</script>
</body>
</html>
Replace YOUR_API_KEY with your key and double-click the file to open it. Click "Draw Cards" to pull your first reading.
How it works
The app has three modes, each calling a different Roxy endpoint:
Daily Card — POST /api/v2/tarot/daily returns one card with an interpretation. The card object has name, meaning, keywords, imageUrl, and reversed (true/false). The dailyMessage is a ready-made summary.
Three-Card Spread — POST /api/v2/tarot/spreads/three-card returns three cards in a positions array. Each position has a name (Past, Present, Future), an interpretation, and a card object. The renderCard() function handles displaying each card with its position label.
Yes/No Oracle — POST /api/v2/tarot/yes-no returns an answer (Yes, No, or Maybe) along with the card that determined it. The answer box displays the result in large text above the card.
Reversed cards: When card.reversed is true, the card's image is rotated 180 degrees via CSS (transform: rotate(180deg)) and a "Reversed" label appears. Reversed cards have different meanings — the API automatically adjusts the meaning text.
Make it yours
- Add card flip animations — start with cards face-down and animate them flipping over with a CSS 3D transform on click.
- Add the Celtic Cross spread — call
POST /api/v2/tarot/spreads/celtic-crossfor a full 10-card reading. The response has the samepositionsstructure with more cards. - Save reading history — store readings in
localStorageand let users review past readings. - Add a question input — for the Yes/No oracle, let users type their question and display it alongside the answer.
Next steps
- AI Astrology Chatbot — build a conversational AI that uses all 8 domains
- Tarot guide — all tarot endpoints explained
- API Reference — full endpoint schemas