The Celtic Cross Challenge — Solving a Complex Layout with CSS Grid

 


Ten Cards, One Ancient Pattern

The Celtic Cross is the most complex tarot spread. Ten cards arranged in a specific pattern — six forming a cross in the center, four stacked vertically on the right — each position carrying its own meaning. This layout has been used for centuries, and I needed to reproduce it faithfully on the web.

The problem is that this layout breaks every convention of typical web design. It's not a horizontal list, not a neat grid of equal cells, and not a scrollable feed. Cards need to be placed at specific coordinates. And to make it more interesting, the second card must overlap the first at a 90-degree angle. How do you build that on the web?

Why CSS Grid Over Flexbox

My first instinct was Flexbox. It handles one-dimensional layouts beautifully, and it worked perfectly for the One Card and Three Card spreads — just line up the cards horizontally with some spacing and you're done.

But building the Celtic Cross cross-shape with Flexbox requires nesting flex containers several levels deep. You'd need separate containers for the left cross region and right column, then subdivide the cross into rows and columns, each with its own flex settings. The result is four or five levels of nested divs, each requiring individual configuration.

CSS Grid approaches the problem from a fundamentally different angle. You define a two-dimensional grid first, then place each item directly into the cell you want. Declare a 4×4 grid, then specify "Card 1 goes in row 2, column 2; Card 3 goes in row 1, column 2; Card 7 goes in row 1, column 4." Empty cells are fine. No nesting required.

I asked the AI to generate both approaches so I could compare. The Grid version was shorter, more readable, and dramatically easier to modify. When I wanted to adjust a card's position, Grid required changing two property values. Flexbox potentially required restructuring the HTML. For maintainability, Grid won decisively.

Mapping the Cross Onto a 4×4 Grid

The final implementation places ten cards on a 4-column, 4-row grid. Mapping the centuries-old Celtic Cross pattern onto web grid coordinates felt like solving a puzzle.

The central card (Card 1, representing the present situation) sits at row 2, column 2. Card 2 (the challenge), which overlaps it, occupies the same position. Card 3 (the subconscious) goes to row 3, column 2. Card 4 (the past) sits at row 2, column 1. Card 5 (possibilities) is at row 1, column 2. Card 6 (the near future) lands at row 2, column 3. The right-side column places Cards 7 through 10 in column 4, from row 4 up to row 1.

AI made this mapping process remarkably efficient. "Map the traditional Celtic Cross layout onto a 4×4 CSS Grid and give me the grid-row and grid-column values for each card number" — and back came a clean table. I verified the tarot specifics myself, but the speed of getting the initial structure was a huge time saver.

The Overlapping Card Problem

The most unusual element of the Celtic Cross is Card 2. Representing the "immediate challenge," it sits on top of Card 1, rotated 90 degrees. The two cards form a cross (+) shape together — that's the tradition.

In CSS Grid, placing two elements in the same cell naturally causes overlap. The tricky part is the rotation. Card 2 doesn't just sit on top of Card 1 — it needs to be rotated 90 degrees. Applying transform: rotate(90deg) gets the visual result right.

But a rotated card swaps its width and height, causing it to overflow into adjacent cells. I solved this by ensuring the grid cells had enough padding to accommodate the card's diagonal length, combined with overflow: visible.

Z-index management added another layer of complexity. Card 2 must always appear above Card 1. A simple z-index bump handles the static case, but combined with card-flip animations, things get interesting. If z-index changes mid-flip, you get an ugly visual flicker. The solution was to adjust z-index only after the flip animation completes — a small timing detail that eliminated the jarring effect.

Mobile Responsiveness: Shrink the Size, Keep the Structure

With the desktop layout working, mobile adaptation was next. Ten cards in a cross pattern need significant screen real estate. On a phone's narrow viewport, should I redesign the layout entirely?

I explored several alternatives — listing cards vertically, using a scrollable area, showing one card at a time via tabs. But I landed on "keep the cross layout, just make it smaller."

The reason comes from tarot itself. In the Celtic Cross, each card's position carries meaning. Card 1 crossed by Card 2 visually represents "a challenge laid across the present situation." Left is the past, right is the future, top is consciousness, bottom is the subconscious. Break the spatial relationships and you weaken the interpretive context.

Cards shrank from 120px wide on desktop to 70px on mobile. Grid gaps and font sizes scaled proportionally. 70px is about the minimum size where card images remain recognizable — any smaller and the illustrations become unreadable blurs.

Instead of defining discrete breakpoints with media queries, I used CSS clamp() for smooth, fluid scaling. This was actually an AI suggestion, and after testing it, I found it far cleaner than the media query approach. Cards resize smoothly across any screen width without defining a single breakpoint.

Why Card Position Matters in Tarot

Working on this part deepened my appreciation for tarot's design. Each position in the Celtic Cross is part of a symbolic system refined over centuries. Position 1 is "the querent's current situation," Position 2 is "the immediate challenge," Position 3 is "the subconscious influence," and so on.

Why does this matter for development? Because a card's position completely changes the AI interpretation context. The same "Tower" card in Position 1 (present) means "you're currently going through upheaval," but in Position 5 (possibilities) it becomes "major change may lie ahead." This is why position data had to be a first-class citizen in the component architecture.

Layout isn't just visual arrangement — it's directly connected to data structure. Realizing this was the biggest takeaway from building the Celtic Cross. The fact that CSS Grid's coordinate system maps so naturally onto tarot's meaning system was a fascinating discovery. When a technical tool aligns well with a domain's conceptual structure, implementation becomes surprisingly intuitive.

What CSS Grid Opened Up

After completing the Celtic Cross, I gained a much broader appreciation for CSS Grid's flexibility. It handles not just regular rows and columns, but irregular placement, overlapping elements, and empty spaces with equal grace. If I ever add new reading modes, I just need to define a new grid mapping.

This work expanded my view of "layout" as a development challenge. Most web pages follow the document flow with simple layouts. But for games, data visualization, and domain-specific apps like this one, Grid's coordinate-based placement is a powerful tool. The Celtic Cross was the project that let me experience that power firsthand.

What's Next

With the layout complete, the app's visual shell is nearly finished. But the core feature of a tarot app is still missing — AI-powered interpretations. In the next part, I'll cover how selected cards and position data get passed to an AI to generate personalized tarot readings, the journey from Groq API to Cloudflare Workers AI, and the art of prompt engineering a "good tarot reader."

댓글

이 블로그의 인기 게시물

사랑을 직접 올리지 않는 설계

감정을 변수로 옮기다 — 3계층 감정 모델

시작의 충동 — "타로 웹앱을 만들어볼까?"