tray cards: shadow, hover-tilt w. focus persistence, role-card tooltip — TDD
- _tray.scss: drop-shadow on cell child elements (img → filter:drop-shadow so the silhouette is the shadow caster, div → box-shadow); 7° hover-tilt on .tray-role-card > img (-7°) and .tray-sig-card > .sig-stage-card (+7° via the standalone `rotate` property so the existing -5° baseline transform composes); :focus persists the tilt after click; cursor: pointer
- tray.js: set tabIndex=0 on placeCard's role cell + on template-rendered .tray-role-card / .tray-sig-card cells at init() so :focus latches the hover state; clear tabindex in reset() for Jasmine afterEach
- TraySpec: 4 new specs covering placeCard tabindex, reset cleanup, init-time tabindex on template-rendered sig & role cards, no-tabindex on bare cells
- New tray-tooltip.js (#id_tooltip_portal) — Phase 1 of the apps.tooltips integration: hovering .tray-role-card > img copies its sibling .tt's innerHTML into the page-root portal, anchors above/below the trigger, & clamps to the viewport horizontally; mousemove outside the union of [trigger, portal] rects clears the portal (Game-Kit pattern, no btns)
- room.html: #id_tooltip_portal mounted at room-page root (outside tray's overflow:hidden); .tt block rendered inline inside .tray-role-card via {% tooltip %} templatetag w. title=role display name & description="[Placeholder description]"
- epic/views.py: my_tray_role_tooltip context dict ({title, description}) keyed off the seated role
- TrayTooltipSpec: 8 specs covering portal population, .active class, sibling-.tt fallback, viewport-edge clamp left/right, and union-rect mouseleave
- 2 FTs in test_component_tray_tooltip.py: hover role img → portal title=Player + description=Placeholder; mouseleave → portal clears
Phase 2 (sig-card tooltip mirroring #id_fan_fyi_panel via a DRY refactor) deferred per plan.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ $handle-rect-w: 10000px;
|
||||
$handle-rect-h: 72px;
|
||||
$handle-exposed: 48px;
|
||||
$handle-r: 1rem;
|
||||
$tray-bevel: 0.3rem; // inner bevel ring; grid must sit inside this
|
||||
|
||||
#id_tray_wrap.role-select-phase {
|
||||
#id_tray_handle { visibility: hidden; pointer-events: none; }
|
||||
@@ -128,9 +129,18 @@ $handle-r: 1rem;
|
||||
}
|
||||
|
||||
.tray-role-card {
|
||||
padding: 0;
|
||||
padding: 0.5rem; // breathing room around role art (post-bleed-trim)
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
// Dotted borders on .tray-cell would otherwise shrink the content box
|
||||
// and push a `width/height: 100%` img off-centre. Flex centring +
|
||||
// sizing the img to the full grid track keeps it visually centred
|
||||
// while preserving the dotted grid markings.
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
&:focus { outline: none; }
|
||||
|
||||
img {
|
||||
display: block;
|
||||
@@ -138,7 +148,14 @@ $handle-r: 1rem;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
transform: scale(1.4); // crop SVG's internal margins
|
||||
transition: rotate 0.25s ease;
|
||||
}
|
||||
|
||||
// Hover/touch tilts the scrawl 7° counter-clockwise; :focus persists the
|
||||
// tilt after a click (cell receives tabindex="0" from tray.js).
|
||||
&:hover > img,
|
||||
&:focus > img {
|
||||
rotate: -7deg;
|
||||
}
|
||||
|
||||
// Cell stays static; only the scrawl image fades in.
|
||||
@@ -156,9 +173,19 @@ $handle-r: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
&:focus { outline: none; }
|
||||
|
||||
.sig-stage-card.sea-sig-card {
|
||||
--sig-card-w: calc(var(--tray-cell-size, 48px) * 5 / 8);
|
||||
// `rotate` is independent of `transform`, so the existing -5° baseline
|
||||
// (set on .sig-stage-card.sea-sig-card via transform) is preserved and
|
||||
// this rotates 7° clockwise on top of it.
|
||||
transition: rotate 0.25s ease;
|
||||
}
|
||||
&:hover > .sig-stage-card,
|
||||
&:focus > .sig-stage-card {
|
||||
rotate: 7deg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +207,7 @@ $handle-r: 1rem;
|
||||
}
|
||||
|
||||
#id_tray {
|
||||
--tray-bevel: #{$tray-bevel}; // exposed to JS via getComputedStyle for cell-size math
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 0.5rem; // small gap so tray appears slightly off-screen on drag start
|
||||
@@ -190,9 +218,10 @@ $handle-r: 1rem;
|
||||
border-left:2.5rem solid rgba(var(--quaUser), 1);
|
||||
border-top: 2.5rem solid rgba(var(--quaUser), 1);
|
||||
border-bottom: 2.5rem solid rgba(var(--quaUser), 1);
|
||||
padding: $tray-bevel; // inset grid inside the bevel ring on every felt-facing side
|
||||
box-shadow:
|
||||
-0.25rem 0 0.5rem rgba(0, 0, 0, 0.55),
|
||||
inset 0 0 0 0.3rem rgba(var(--quiUser), 0.45), // prominent bevel ring at wall edge
|
||||
inset 0 0 0 $tray-bevel rgba(var(--quiUser), 0.45), // prominent bevel ring at wall edge
|
||||
inset 0.6rem 0 1.5rem -0.5rem rgba(0, 0, 0, 1), // left wall depth
|
||||
inset 0.6rem 0 1.5rem -0.5rem rgba(var(--quiUser), 0.5), // left wall depth (hue)
|
||||
inset 0 0.6rem 1.5rem -0.5rem rgba(0, 0, 0, 1), // top wall depth
|
||||
@@ -217,6 +246,20 @@ $handle-r: 1rem;
|
||||
border-right: 2px dotted rgba(var(--priUser), 0.35);
|
||||
border-bottom: 2px dotted rgba(var(--priUser), 0.35);
|
||||
position: relative;
|
||||
|
||||
// Whatever a cell holds (role-card img, sig stage card, future Celtic Cross
|
||||
// / natus wheel / dice) gets a soft drop shadow to lift it off the felt.
|
||||
// Applied to the child rather than the cell itself so the dotted grid
|
||||
// borders stay shadow-free.
|
||||
> * {
|
||||
box-shadow: 1px 1px 5px rgba(0, 0, 0, 1);
|
||||
}
|
||||
// Img children (SVG/PNG with transparent regions): use filter drop-shadow
|
||||
// instead so the shadow traces the rendered silhouette, not the SVG viewport.
|
||||
> img {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Tray: landscape reorientation ─────────────────────────────────────────
|
||||
@@ -285,7 +328,7 @@ $handle-r: 1rem;
|
||||
|
||||
box-shadow:
|
||||
0 0.25rem 0.5rem rgba(0, 0, 0, 0.55),
|
||||
inset 0 0 0 0.3rem rgba(var(--quiUser), 0.45), // prominent bevel ring
|
||||
inset 0 0 0 $tray-bevel rgba(var(--quiUser), 0.45), // prominent bevel ring
|
||||
inset 0 -0.6rem 1.5rem -0.5rem rgba(0, 0, 0, 1), // bottom wall depth
|
||||
inset 0 -0.6rem 1.5rem -0.5rem rgba(var(--quaUser), 0.5), // bottom wall depth (hue)
|
||||
inset 0.6rem 0 1.5rem -0.5rem rgba(0, 0, 0, 1), // left wall depth
|
||||
@@ -308,9 +351,11 @@ $handle-r: 1rem;
|
||||
grid-auto-rows: var(--tray-cell-size, 48px);
|
||||
// Anchor grid to the handle-side (bottom) of the tray so the first row
|
||||
// is visible when partially open; additional rows grow upward.
|
||||
// Inset by --tray-bevel so the grid sits inside the bevel ring rather
|
||||
// than under it (matches the portrait padding inset of the same width).
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
bottom: var(--tray-bevel, 0.3rem);
|
||||
left: var(--tray-bevel, 0.3rem);
|
||||
}
|
||||
|
||||
// In landscape the first row sits at the bottom; border-top divides it from
|
||||
|
||||
Reference in New Issue
Block a user