diff --git a/src/static_src/scss/_billboard.scss b/src/static_src/scss/_billboard.scss index d4e181b..6416f73 100644 --- a/src/static_src/scss/_billboard.scss +++ b/src/static_src/scss/_billboard.scss @@ -582,21 +582,21 @@ body.page-billposts { } // Sprint A.6 — FLIP btn for non-polarized image-equipped decks in the - // applet. Nested INSIDE the .my-sign-applet-card.--image (which has - // position: relative) so absolute positioning anchors to the card bounds. - // Hidden during the rotateY animation via the [data-flipping] hook on - // the parent card — same pattern as the my_sign page (`_card-deck.scss:889`) - // and the tarot-fan view (`_card-deck.scss:459`). + // applet. Nested INSIDE .my-sign-applet-card.--image (which has + // position: relative) so absolute positioning anchors to the card. + // Polish-5: shares `@include flip-btn-base` + `%flip-btn-mid-flip` w. + // the other 2 surfaces (my_sign main + game-kit fan) via `_card-deck.scss`. .my-sign-applet-card .my-sign-applet-flip-btn { - position: absolute; + @include flip-btn-base; z-index: 10; bottom: 0.6rem; left: 0.6rem; - margin: 0; } + // Btn is nested INSIDE the card, so the [data-flipping] hook is a direct + // ancestor — no `:has()` needed (unlike the my_sign + fan surfaces where + // the btn is a sibling of the flipping card under a common parent). .my-sign-applet-card[data-flipping] .my-sign-applet-flip-btn { - opacity: 0; - pointer-events: none; + @extend %flip-btn-mid-flip; } // Stat block — mirrors the stage card's footprint (same 5:8 aspect + diff --git a/src/static_src/scss/_card-deck.scss b/src/static_src/scss/_card-deck.scss index 8ef0614..117b219 100644 --- a/src/static_src/scss/_card-deck.scss +++ b/src/static_src/scss/_card-deck.scss @@ -3,6 +3,47 @@ // Shared card display classes (.fan-card, .fan-card-corner, .fan-card-face, .fan-nav) // extracted from _game-kit.scss; sig-select overlay extracted from _room.scss. +// ── Shared FLIP-btn primitives ─────────────────────────────────────────────── +// +// The FLIP btn lives on 3 surfaces (my_sign main stage, my_sign applet, game +// kit fan). Polish-5 2026-05-25 PM unification per user spec: hover-reveal +// everywhere (was display-toggle on my_sign + always-visible on applet); +// `display: none` mid-flip for INSTANT vanish (was opacity-fade — felt sluggish +// since clicks happen faster than the 0.3s ease). The reveal-on-hover transition +// stays smooth for everyday hover, but the moment a FLIP starts the btn pops +// out of layout entirely so it can't visually interfere w. the rotateY mid-spin. +// +// @mixin flip-btn-base Position absolute + zero margin + hidden default +// (opacity:0 + pointer-events:none) + 0.3s opacity +// transition for the smooth hover-reveal. Surfaces +// add z-index + position offsets (most use bottom- +// left-of-card; fan uses transform-based carousel- +// shift since its btn is at wrap-level). +// %flip-btn-revealed opacity:1 + pointer-events:auto — applied by +// each surface's hover-trigger selector chain. +// %flip-btn-mid-flip display:none — applied by each surface's +// `[data-flipping]` selector chain. Instant vanish +// (display isn't animatable); supersedes the +// hover-reveal entirely while rotation is in +// flight. Each surface's selector chain differs +// because the btn-to-card DOM relationship varies +// (inside the card on my_sign + applet post-polish- +// 5; sibling under common wrap for the fan). +@mixin flip-btn-base { + position: absolute; + margin: 0; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; +} +%flip-btn-revealed { + opacity: 1; + pointer-events: auto; +} +%flip-btn-mid-flip { + display: none; +} + // ── Shared stage-card polarity rules ───────────────────────────────────────── // // Used by .sea-stage-card (Sea Select polarity is fixed by the deck-stack the @@ -519,34 +560,32 @@ // Positioned at bottom-left of the focused card slot (carousel-shifted, so its // translateX matches .tarot-fan's leftward shift). .fan-flip-btn { - position: absolute; + @include flip-btn-base; z-index: 25; top: 50%; left: 50%; + // Carousel-shifted bottom-left-of-focused-card. The btn sits at wrap level + // (not inside any card) so positioning has to derive from the carousel + // geometry vars rather than the bottom-left-of-card pattern the other 2 + // surfaces use. --fan-stage-shift + --fan-card-w/h scale w. breakpoint + // via the parent `.tarot-fan-wrap`. transform: translate(calc(-50% - var(--fan-stage-shift) - var(--fan-card-w) / 2 + 1.5rem), calc(-50% + var(--fan-card-h) / 2 - 1.5rem)); - margin: 0; - opacity: 0; - pointer-events: none; - transition: opacity 0.3s ease; } -// Reveal when the focused card OR the FLIP button itself is hovered. Without -// the `.fan-flip-btn:hover` clause the button (z-index 25, sitting on top of -// the card) steals :hover from the card the moment the cursor moves onto it, -// flipping :has() false, fading the button to opacity:0 + pointer-events:none, -// and letting the in-flight click pass through to the dialog backdrop (which -// closes the modal). Keeping the button in the trigger list pins it visible -// while the cursor is on it. +// Hover-reveal. The `.fan-flip-btn:hover` clause pins the btn visible while +// the cursor is on it — without it, the btn (z-index 25, on top of the card) +// steals :hover from the card the moment the cursor moves onto it, retracting +// the reveal + letting the in-flight click pass through to the dialog backdrop +// (which closes the modal). `.fan-touch-revealed` is the touch-device fallback +// (no :hover on touchscreens) — game-kit.js toggles it on first card tap. .tarot-fan-wrap:has(.fan-card--active:hover) .fan-flip-btn, .tarot-fan-wrap:has(.fan-flip-btn:hover) .fan-flip-btn, .tarot-fan-wrap.fan-touch-revealed .fan-flip-btn { - opacity: 1; - pointer-events: auto; -} -.tarot-fan-wrap:has(.fan-card[data-flipping]) .fan-flip-btn { - opacity: 0; - pointer-events: none; + @extend %flip-btn-revealed; } +// Mid-flip-hide selector for the fan is consolidated into the unified +// 3-surface rule below the `.my-sign-flip-btn` / `.my-sign-applet-flip-btn` +// declarations. See `_card-deck.scss` polish-5 FLIP-btn block. .fan-nav { position: absolute; @@ -867,23 +906,10 @@ html:has(.sig-backdrop) { // pair aligned in the centred row. .sig-stat-block { align-self: center; } - // FLIP was positioned via `left: calc(1.5rem + 0.4rem)` (default - // rule below) assuming the card sat flush against the stage's - // padded-left edge — true on the picker's left-anchored layout but - // wrong here w. `justify-content: center` (the card moves to - // wherever the group's left edge lands). - // Re-derive FLIP's offsets from the centred geometry: - // group width = card + gap + stat = 2 * --sig-card-w + 0.75rem - // card's left edge (in stage) = (100% - group width) / 2 - // card's bottom edge (in stage) = 50% - (cardHeight / 2) - // = 50% - --sig-card-w * 0.8 - // (cardHeight = w × 8/5 = w × 1.6) - // The +0.4rem on each lands FLIP just inside the card's bottom-left - // corner, matching the picker-side positioning intent. - .my-sign-flip-btn { - left: calc((100% - 2 * var(--sig-card-w) - 0.75rem) / 2 + 0.4rem); - bottom: calc(50% - var(--sig-card-w) * 0.8 + 0.4rem); - } + // Polish-5: FLIP-btn centered-mode offset override DROPPED. The btn is + // now positioned INSIDE the card (`.sig-stage-card { position: relative }` + // + btn `bottom: 0.6rem; left: 0.6rem`), so it follows the card naturally + // wherever the stage positions it — no per-layout-mode geometric calc. // Landing collapses since the hex is server-side gone — just DEL is // left + that's `position: absolute`. `position: static` here drops @@ -963,34 +989,44 @@ html:has(.sig-backdrop) { background: rgba(var(--duoUser), 1); } -.my-sign-flip-btn { - position: absolute; +// Polish-5: my_sign main + applet FLIP btns share one positioning rule. +// Both live INSIDE their card (`.sig-stage-card` / `.my-sign-applet-card`, +// both `position: relative`) so `bottom: 0.6rem; left: 0.6rem` anchors +// universally to card-bottom-left w/o needing surface-specific calc(). +.my-sign-flip-btn, +.my-sign-applet-flip-btn { + @include flip-btn-base; z-index: 25; - bottom: 0.4rem; - // .sig-stage has padding-left: 1.5rem; this offset places the btn just - // inside the stage card's bottom-left corner (the card sits flex-end / - // flex-start, anchored to the stage's left padding). - left: calc(1.5rem + 0.4rem); - margin: 0; - display: none; + bottom: 0.6rem; + left: 0.6rem; } -// FLIP btn appears only when the stage is frozen (post-OK confirm). Hover-only -// previews don't reveal the polarity toggle — the user hasn't committed yet. -.my-sign-stage.sig-stage--frozen .my-sign-flip-btn { - display: inline-flex; +// Hover-reveal on the parent card. `:has(.flip-btn:hover)` pins the btn +// visible while the cursor is on it — without this clause, the btn (z-index +// 25, on top of the card) steals :hover from the card the moment the cursor +// moves onto it, retracting the reveal + breaking the click flow. Same +// pattern the fan carousel uses. +// +// my_sign main still gates on `.sig-stage--frozen` — hover-only previews +// don't reveal the polarity toggle until the user has committed a sig. +.my-sign-stage.sig-stage--frozen .sig-stage-card:hover .my-sign-flip-btn, +.my-sign-stage.sig-stage--frozen .sig-stage-card:has(.my-sign-flip-btn:hover) .my-sign-flip-btn, +.my-sign-applet-card:hover .my-sign-applet-flip-btn, +.my-sign-applet-card:has(.my-sign-applet-flip-btn:hover) .my-sign-applet-flip-btn { + @extend %flip-btn-revealed; } -// Sprint A.5 — hide FLIP btn during the flip animation. `data-flipping="1"` -// is set on .sig-stage-card by _flipPolarityAnimated (polarized) AND -// _flipToBackAnimated (non-polarized) for the 500ms animation duration; CSS -// :has() selects the parent .my-sign-stage when any child carries that attr -// and zeros the btn so it doesn't visually interfere w. the rotateY mid-spin. -// Mirrors the tarot-fan view's pattern (`_card-deck.scss:459` — -// `.tarot-fan-wrap:has(.fan-card[data-flipping]) .fan-flip-btn`). -.my-sign-stage:has(.sig-stage-card[data-flipping]) .my-sign-flip-btn { - opacity: 0; - pointer-events: none; +// Unified mid-flip-hide across all 3 surfaces. `[data-flipping]="1"` is set on +// the card by each surface's FLIP handler for the 500ms rotation duration; +// `%flip-btn-mid-flip`'s `display: none` makes the btn vanish INSTANTLY (per +// user spec — no ease-out logic competing w. the click). Selector chains +// differ per surface because the btn-to-card DOM relationship varies (btn is +// INSIDE the card on my_sign + applet post-polish-5; sibling under .tarot- +// fan-wrap for the fan). +.sig-stage-card[data-flipping] .my-sign-flip-btn, +.my-sign-applet-card[data-flipping] .my-sign-applet-flip-btn, +.tarot-fan-wrap:has(.fan-card[data-flipping]) .fan-flip-btn { + @extend %flip-btn-mid-flip; } // ─── Mini card grid ─────────────────────────────────────────────────────────── @@ -2120,6 +2156,17 @@ $_sea-title-els: '.fan-card-name, .sig-qualifier-above, .sig-qualifier-below, .f color: rgba(var(--priUser), 1); .fan-card-arcana, .fan-card-corner { color: rgba(var(--priUser), 1); } + + // Polish-5: image-mode override mirrors the sea-sig-card pattern. The + // `$invert-frame: true` arg above sets `--secUser` bg + `--priUser` + // border, which then OUT-CASCADES the shared image-mode comma-list rule + // (same 0,2,0 specificity but later in source). Re-state transparency + // here so image-mode drawn cards (Minchiate today) don't show a beige + // card-shape behind the PNG art. + &.sig-stage-card--image { + background: transparent; + border: 0; + } } .sea-stage--gravity .sea-stage-card { @include stage-card-polarity( diff --git a/src/templates/apps/billboard/_partials/_applet-my-sign.html b/src/templates/apps/billboard/_partials/_applet-my-sign.html index 928c900..d7f98ad 100644 --- a/src/templates/apps/billboard/_partials/_applet-my-sign.html +++ b/src/templates/apps/billboard/_partials/_applet-my-sign.html @@ -74,24 +74,13 @@ {# only the polarity axis (FLIP), never the orientation axis #} {# (SPIN), so always render the upright/emanation face. #}
Emanation
-{{ card.name }}
-{{ card.get_arcana_display }}
-Emanation
-Reversal
-Emanation
-Reversal
-Emanation
-Reversal
-{{ label_text }}
+{% if card %}{{ card.name }}{% endif %}
+{% if card %}{{ card.get_arcana_display }}{% endif %}
+