From c641e4fc20469833fab0bfa61286d5f6a983d483 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 23 Jul 2026 01:09:44 -0400 Subject: [PATCH] Magnify cards on hover Hovering any card portals a large copy beside the cursor that grows to fit its full effect text, so small or clipped cards can be read at a glance. --- web/src/components/CardView.tsx | 29 +++++++++++++++++++++++++ web/src/styles.css | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/web/src/components/CardView.tsx b/web/src/components/CardView.tsx index c6fb638..710deda 100644 --- a/web/src/components/CardView.tsx +++ b/web/src/components/CardView.tsx @@ -1,3 +1,5 @@ +import { useState } from 'react' +import { createPortal } from 'react-dom' import type { Card } from '../types' import { artFor } from '../petArt' @@ -11,6 +13,8 @@ interface Props { bonus?: number damage?: number dead?: boolean + // preview marks the floating magnified copy so it doesn't magnify itself. + preview?: boolean } // CardView renders one physical card: pets get a power badge and a colored @@ -25,7 +29,11 @@ export function CardView({ bonus = 0, damage = 0, dead, + preview, }: Props) { + // Hover magnifier: show a large floating copy beside the cursor. The + // preview copy itself opts out so it can't recurse. + const [hover, setHover] = useState<{ x: number; y: number } | null>(null) const power = (card.power ?? 0) + bonus const classes = [ 'card', @@ -39,12 +47,33 @@ export function CardView({ .filter(Boolean) .join(' ') + // Place the magnified preview near the cursor, clamped into the viewport. + const previewEl = + hover && !preview + ? createPortal( +
+ +
, + document.body, + ) + : null + return (
setHover({ x: e.clientX, y: e.clientY })} + onMouseMove={preview ? undefined : (e) => setHover({ x: e.clientX, y: e.clientY })} + onMouseLeave={preview ? undefined : () => setHover(null)} > + {previewEl}
{card.tier ? `T${card.tier}` : 'ยท'} {card.suit && } diff --git a/web/src/styles.css b/web/src/styles.css index 43a1937..b0bb3b5 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1339,3 +1339,41 @@ h3 { flex-wrap: wrap; gap: 6px; } + +/* --- hover magnifier --- */ + +.card-magnify { + position: fixed; + z-index: 200; + pointer-events: none; + transform: scale(1.5); + transform-origin: top left; + filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.55)); + animation: magnify-in 90ms ease-out; +} + +/* The magnifier exists to be read, so let the preview card grow to fit its + full effect text instead of clipping like a fixed-height card. */ +.card-magnify .card { + height: auto; + min-height: 176px; + padding-bottom: 10px; +} + +.card-magnify .card-effect { + flex: 0 0 auto; + overflow: visible; + align-items: center; + margin-bottom: 6px; +} + +@keyframes magnify-in { + from { + opacity: 0; + transform: scale(1.1); + } + to { + opacity: 1; + transform: scale(1.5); + } +}