Pull a card. The bog answers. Each card is an archetype of the swamp — The Heron, The Fog, The Frog King, The Lantern — with a short reading. The name spins into place via split-flap. Use as a daily oracle, a decision aid, or a quiet "I'm stuck, give me a metaphor" button.
tap the deck to pull a card↑
Reach for it when you want a moment of structured wonder — a creative
block, a stuck decision, a journal prompt, a daily ritual. Drop it on a
"/oracle" page, a 404, a sidebar of a blog. Use the daily mode
so the same visitor sees the same card each day (and a new one tomorrow).
Skip it when the page is task-focused and timed (checkouts, signups) — the whole point of the card is patience. The swamp does not rush.
Click any to draw it directly.
A deck container and a drawn-card container. Toggle one or the other based on state.
<div class="tarot" id="tarot"> <!-- deck (visible until first draw) --> <div class="tarot-deck" id="deck"> <div class="tarot-back s3"></div> <div class="tarot-back s2"></div> <div class="tarot-back top"> <div class="tarot-glyph">☾ SWAMP TAROT</div> </div> </div> <!-- drawn card (filled in by JS) --> <div class="tarot-drawn" id="drawn" hidden> <div class="tarot-card"> <div class="tc-num"></div> <div class="tc-art"></div> <div class="tc-name"></div> <div class="tc-mean"></div> </div> <p class="tc-read"></p> <button class="tc-btn">PULL ANOTHER</button> </div> </div>
The card uses a 3D flip-in. Three stacked card backs make the deck feel like it has weight.
.tarot-back { width: 200px; height: 280px; border-radius: 14px; background: linear-gradient(160deg, #2a1530, #0e0a1a); border: 1px solid rgba(255, 77, 141, .35); cursor: pointer; box-shadow: 0 12px 40px rgba(0, 0, 0, .7); } .tarot-back.s2 { transform: translate(4px, 4px) rotate(1.2deg); } .tarot-back.s3 { transform: translate(8px, 8px) rotate(2.4deg); } .tarot-card { width: 240px; height: 336px; border-radius: 16px; background: linear-gradient(160deg, #1c4a34, #0e2b1f); animation: flip-in .9s cubic-bezier(.34, 1.56, .64, 1); } @keyframes flip-in { 0% { transform: rotateY(180deg); opacity: 0; } 50% { transform: rotateY(90deg); opacity: .6; } 100% { transform: rotateY(0); opacity: 1; } } /* name slot — one .ch per character for split-flap */ .tc-name { display: flex; gap: 1px; font-family: 'Bungee'; } .tc-name .ch { display: inline-block; min-width: .55em; text-align: center; }
draw() picks a random card (or daily-seeded one), renders SVG art,
flips the card, then spins the name into place character by character (split-flap).
// Swamp Tarot — pull a card, flip + split-flap name reveal (function(){ const POOL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '; const tarot = document.getElementById('tarot'); const deck = document.getElementById('deck'); const drawn = document.getElementById('drawn'); // Pick: random by default, daily if data-daily on container function pick(){ if (tarot.dataset.mode === 'daily') { const d = new Date(); const seed = d.getFullYear()*1000 + d.getMonth()*40 + d.getDate(); return DECK[seed % DECK.length]; } return DECK[Math.floor(Math.random() * DECK.length)]; } // Split-flap name reveal (lifted from the Split-Flap component) function flapName(el, text){ el.innerHTML = ''; const spans = [...text].map(target => { const s = document.createElement('span'); s.className = 'ch'; s.textContent = POOL[Math.floor(Math.random()*POOL.length)]; s.dataset.target = target; el.appendChild(s); return s; }); spans.forEach((s, i) => { let ticks = 8 + i*3; const iv = setInterval(() => { s.textContent = POOL[Math.floor(Math.random()*POOL.length)]; if (--ticks <= 0) { s.textContent = s.dataset.target; clearInterval(iv); } }, 45); }); } function draw(card){ card = card || pick(); deck.hidden = true; drawn.hidden = false; drawn.querySelector('.tc-num').textContent = 'CARD · ' + card.n; drawn.querySelector('.tc-art').innerHTML = card.svg; drawn.querySelector('.tc-mean').textContent = card.meaning; drawn.querySelector('.tc-read').textContent = card.reading; flapName(drawn.querySelector('.tc-name'), card.name); } deck.addEventListener('click', () => draw()); drawn.querySelector('.tc-btn').addEventListener('click', () => draw()); // expose for #-link draws window.swampTarotDraw = draw; })();
15 archetypes. Each has a name, a one-line meaning, a paragraph reading, and an inline SVG so the whole component stays self-contained.
💡 Building your own thing? The same deck is available as a public JSON at
sophieren.com/swamp-tarot/deck.json —
fetch it directly, no need to copy the array below.
const DECK = [ { n:'I', name:'THE HERON', meaning:'patience that catches', reading:'You are not slow. You are waiting for the fish that the louder ones never see.' }, { n:'II', name:'THE BOG', meaning:'the work is heavy on purpose', reading:'Stuck is not the failure. The pull is the muscle. Walk slower, lift higher.' }, { n:'III', name:'THE LURE', meaning:'bait wears the face of fortune', reading:'The thing that shines may be a hook. Ask who is pulling, not just what glitters.' }, { n:'IV', name:'THE MIRROR', meaning:'still water sees you', reading:'You came for an answer; you found a face. Look anyway. The face was the answer.' }, { n:'V', name:'FROG KING', meaning:'claim it', reading:'The throne is a leaf. The crown is a sound. Take both. No one is coming to crown you.' }, { n:'VI', name:'THE LILY', meaning:'the offering that floats', reading:'Give without proof. The lily has no agenda and still everything orbits it.' }, { n:'VII', name:'THE FOG', meaning:'you cannot see; that is the answer', reading:'Stop deciding. The fog will lift in its own hour. Until then, breathe shallow and stand still.' }, { n:'VIII', name:'THE CROAK', meaning:'say the thing', reading:'The swamp is listening. The frog who calls finds the frog who answers. Silence finds only silence.' }, { n:'IX', name:'THE TIDE', meaning:'the next you is on the way', reading:'Something is coming. Do not name it yet. Open the door wider, that is your only job.' }, { n:'X', name:'THE MOSS', meaning:'slow growth is growth', reading:'One millimeter a year is a kingdom by the time anyone notices. Keep going.' }, { n:'XI', name:'THE REED', meaning:'bend, hollow, sing', reading:'What broke you is what makes you sound. Stop refusing the wind; start being the flute.' }, { n:'XII', name:'THE SNAKE', meaning:'cunning is not cruelty', reading:'Take the long way. The shortcut bites. You are allowed to be patient and clever at once.' }, { n:'XIII', name:'THE LANTERN', meaning:'a small light, repeated', reading:'Be the firefly. One blink will not light the swamp. A hundred thousand of them do.' }, { n:'XIV', name:'THE DRAGONFLY',meaning:'transformation', reading:'The version of you that flies is not the version that crawled. Stop apologizing for the molt.' }, { n:'XV', name:'THE STUMP', meaning:'what was felled became home', reading:'The thing you mourn is now a habitat. Loss did not end the story; it changed the renters.' } ];
| Where | Default | Effect |
|---|---|---|
HTML data-mode="daily" | random | Same card all day for the same visitor (seeded by date). Tomorrow rotates. |
DECK array | 15 cards | Add your own archetypes. Each card needs n / name / meaning / reading / svg. |
CSS @keyframes flip-in | .9s | Flip duration. Shorter = quicker reveal; longer = more theatre. |
JS flapName tick speed | 45ms | How fast each character cycles. Lower = snappier reveal. |
JS flapName ticks per char | 8 + i·3 | Each character locks 3 ticks later than the last. Higher = longer cascade. |
Feed the cats who keep the bog tidy. (And tip the witch who wrote the deck.)
🧋 TREAT THE SWAMP →