diff --git a/src/apps/gameboard/tests/integrated/test_views.py b/src/apps/gameboard/tests/integrated/test_views.py index 3eac2f6..7ad443f 100644 --- a/src/apps/gameboard/tests/integrated/test_views.py +++ b/src/apps/gameboard/tests/integrated/test_views.py @@ -413,6 +413,52 @@ class GameboardViewTest(TestCase): def test_game_kit_has_dice_set_placeholder(self): [_] = self.parsed.cssselect("#id_game_kit #id_kit_dice_set") + def test_deck_token_renders_card_stack_svg_not_fa_id_badge(self): + """Sprint A.4 — `.token.deck-variant` icon is the inline SVG card-stack + (`.deck-stack-icon`), not the old `` + placeholder. Stack contains 3 rect children w. `.deck-stack-icon__card` + classes the CSS keys off for the rest-stack + hover fan-out.""" + deck = self.parsed.cssselect("#id_game_kit #id_kit_earthman_deck")[0] + # New SVG icon present + [svg] = deck.cssselect("svg.deck-stack-icon") + cards = svg.cssselect(".deck-stack-icon__card") + self.assertEqual( + len(cards), 3, + "Card-stack icon must render 3 rect cards (top + 2 fan-out)", + ) + # Old FA icon removed + self.assertEqual( + len(deck.cssselect("i.fa-id-badge")), 0, + "fa-regular fa-id-badge must be gone from deck-variant token", + ) + + def test_polarized_deck_tooltip_has_x2_decoration(self): + """Earthman is the only is_polarized=True deck today (per A.0 migration). + Its tooltip's card-count line should carry a `(×2)` suffix in --terUser + per [[project-card-deck-icon]]'s `is_polarized` tooltip-decoration rule.""" + deck = self.parsed.cssselect("#id_game_kit #id_kit_earthman_deck")[0] + tt = deck.cssselect(".tt")[0] + [x2] = tt.cssselect(".tt-x2") + self.assertIn("×2", x2.text_content()) # ×2 + + def test_nonpolarized_deck_tooltip_lacks_x2_decoration(self): + """Non-polarized decks (Tarot RWS, future Minchiate) don't get the + `(×2)` decoration — the suffix signals 'double-polarized = 6 segments + = fills 2× as many seats' which only applies to polarized decks.""" + from apps.epic.models import DeckVariant + # Use the migration-renamed RWS deck (formerly fiorentine-minchiate). + rws = DeckVariant.objects.get(slug="tarot-rider-waite-smith") + self.user.unlocked_decks.add(rws) + response = self.client.get("/gameboard/") + import lxml.html + parsed = lxml.html.fromstring(response.content) + deck = parsed.cssselect("#id_game_kit #id_kit_tarot_deck")[0] + tt = deck.cssselect(".tt")[0] + self.assertEqual( + len(tt.cssselect(".tt-x2")), 0, + "Non-polarized RWS deck must not show (×2) in tooltip", + ) + class GameboardDeckInUseTest(TestCase): """Sprint 2: game kit applet renders in-use state for a deck assigned to an active seat.""" diff --git a/src/static_src/scss/_gameboard.scss b/src/static_src/scss/_gameboard.scss index 1465098..dcbfe66 100644 --- a/src/static_src/scss/_gameboard.scss +++ b/src/static_src/scss/_gameboard.scss @@ -79,6 +79,51 @@ body.page-gameboard { .kit-item { font-size: 1.5rem; } .kit-item { opacity: 0.6; } + + // Sprint A.4 — card-deck stack icon (.deck-stack-icon) replaces the + // fa-regular fa-id-badge for .token.deck-variant. 3 stacked card-back + // rects, 5° CW rest tilt, fan-out on hover/active/focus of the parent + // .token (animation + tooltip portal trigger lockstep on the same + // pseudo-class set). See [[project-card-deck-icon]]. + .deck-stack-icon { + display: inline-block; + width: 1.5rem; // match the prior fa-id-badge visual weight + height: 2.4rem; // 5:8 tarot card aspect + color: rgba(var(--terUser), 1); // stroke color via currentColor + overflow: visible; // fan-out exceeds the viewBox bounds + filter: drop-shadow(0.08rem 0.08rem 0.15rem rgba(0, 0, 0, 0.6)); + + .deck-stack-icon__stack { + transform: rotate(5deg); + transform-origin: 50% 50%; + transform-box: fill-box; + transition: transform 0.25s ease; + } + + .deck-stack-icon__card { + fill: rgba(var(--priUser), 1); + stroke: currentColor; + stroke-width: 1; + transform-origin: 50% 50%; + transform-box: fill-box; + transition: transform 0.25s ease; + } + // Rest: tightly stacked w. tiny vertical offsets (suggests stack + // depth without separating the cards visually). + .deck-stack-icon__card--1 { transform: translateY(-0.4px); } + .deck-stack-icon__card--3 { transform: translateY( 0.4px); } + } + + // Hover/active/focus on the parent .token fans cards 2 + 3 out from + // under card 1; card 1 stays put. Tooltip portal is wired to the + // same `.token:hover` trigger via JS so the splay + tooltip-appearance + // co-activate. + .token.deck-variant:hover .deck-stack-icon, + .token.deck-variant:active .deck-stack-icon, + .token.deck-variant:focus .deck-stack-icon { + .deck-stack-icon__card--2 { transform: translate(-5px, -2px) rotate(-12deg); } + .deck-stack-icon__card--3 { transform: translate( 5px, -2px) rotate( 12deg); } + } } } diff --git a/src/static_src/scss/_tooltips.scss b/src/static_src/scss/_tooltips.scss index 1406c5c..bb7a553 100644 --- a/src/static_src/scss/_tooltips.scss +++ b/src/static_src/scss/_tooltips.scss @@ -18,6 +18,12 @@ // regardless of whether justify-content cascade reaches this far // (belt + suspenders for the space-between we want). .tt-price { font-size: 1rem; color: rgba(var(--priGn), 1); margin-left: auto !important; } + // Sprint A.4 — polarization-multiplier decoration in deck tooltips. + // `(×2)` suffix appended inside .tt-description for is_polarized decks + // (Earthman today); --terUser color signals "double-polarized = 6 segments = + // fills 2× as many seats" per [[project-card-deck-icon]]'s `is_polarized` + // tooltip-decoration rule. Inline w. the card-count text on the same line. + .tt-x2 { color: rgba(var(--terUser), 1); font-weight: 600; } } .token-tooltip, diff --git a/src/templates/apps/gameboard/_partials/_applet-game-kit.html b/src/templates/apps/gameboard/_partials/_applet-game-kit.html index 8f4fe48..3f95882 100644 --- a/src/templates/apps/gameboard/_partials/_applet-game-kit.html +++ b/src/templates/apps/gameboard/_partials/_applet-game-kit.html @@ -85,19 +85,22 @@ {% endif %} {% for deck in deck_variants %}
- + {# Sprint A.4 — card-deck stack icon replaces the old fa-id-badge. #} + {# 3 stacked card-back rects (5° rest tilt); the .token:hover/:active #} + {# /:focus pseudo-classes drive the fan-out + tooltip portal in lockstep. #} + {% include "apps/gameboard/_partials/_deck_stack_icon.html" %}
{% if deck.in_use_room_name %}{% elif deck.pk == equipped_deck_id %}{% else %}{% endif %}

{{ deck.name }}{% if deck.is_default %} (Default){% endif %}

-

{{ deck.card_count }}-card Tarot deck

+

{{ deck.card_count }}-card Tarot deck{% if deck.is_polarized %} (×2){% endif %}

{% if deck.description %}

{{ deck.description }}

{% endif %}

Stock version (0 substitutions)

{% empty %} -
+
{% include "apps/gameboard/_partials/_deck_stack_icon.html" %}
{% endfor %}
diff --git a/src/templates/apps/gameboard/_partials/_deck_stack_icon.html b/src/templates/apps/gameboard/_partials/_deck_stack_icon.html new file mode 100644 index 0000000..fd1eab1 --- /dev/null +++ b/src/templates/apps/gameboard/_partials/_deck_stack_icon.html @@ -0,0 +1,20 @@ +{# Sprint A.4 — card-deck stack SVG icon. Replaces fa-regular fa-id-badge #} +{# wherever a deck is represented by an icon (currently game_kit applet's #} +{# .token.deck-variant; future: room.html pile + deck-bag). #} +{# #} +{# 3 rect-cards stacked tightly at rest; .deck-stack-icon SCSS rotates the #} +{# whole stack 5° CW + drives the fan-out on hover/active/focus of the #} +{# parent .token (or whatever wraps it). The card-back design here is #} +{# placeholder (solid --priUser fill + currentColor stroke). Detailed art #} +{# (Earthman planet-impact illustration, future custom decks) drops in later #} +{# per [[project-card-deck-icon]]. #} +