z-index audit + aperture fill + resize:end debounce + landscape sig-grid cap
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- #id_aperture_fill: position:fixed→absolute (clips to .room-page, avoids h2/navbar); z-index 105→90 (below blur backdrops at z-100); landscape override removed (inset:0 works both orientations) - _base.scss: landscape footer z-index:100 (matches navbar); corrects unset z-index - _room.scss: fix stale "navbar z-300" comment; landscape sig-deck-grid columns repeat(9,1fr)→repeat(9,minmax(0,90px)) to cap card size on wide viewports - room.js: add resize:end listeners for scaleTable + sizeSigModal; new IIFE dispatches resize:end 500ms after resize stops so both functions re-measure settled layout - tray.js: extract _reposition() from inline resize handler; wire to both resize and resize:end so tray repositions correctly after rapid resize or orientation change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,8 @@
|
|||||||
} else {
|
} else {
|
||||||
scaleTable();
|
scaleTable();
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', scaleTable);
|
window.addEventListener('resize', scaleTable);
|
||||||
|
window.addEventListener('resize:end', scaleTable);
|
||||||
}());
|
}());
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
@@ -83,8 +84,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('load', sizeSigModal);
|
window.addEventListener('load', sizeSigModal);
|
||||||
window.addEventListener('resize', sizeSigModal);
|
window.addEventListener('resize', sizeSigModal);
|
||||||
|
window.addEventListener('resize:end', sizeSigModal);
|
||||||
|
}());
|
||||||
|
|
||||||
|
// Dispatch a custom 'resize:end' event 500 ms after the last 'resize' fires.
|
||||||
|
// scaleTable, sizeSigModal, and Tray._reposition all subscribe to it so they
|
||||||
|
// re-measure with settled viewport dimensions after rapid resize sequences.
|
||||||
|
(function () {
|
||||||
|
var t;
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(function () { window.dispatchEvent(new Event('resize:end')); }, 500);
|
||||||
|
});
|
||||||
}());
|
}());
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
|
|||||||
@@ -290,6 +290,42 @@ var Tray = (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force-close and reposition to settled bounds. Called on both 'resize'
|
||||||
|
// (snap without transition to avoid flicker during continuous events) and
|
||||||
|
// 'resize:end' (re-measures after the viewport has stopped moving).
|
||||||
|
function _reposition() {
|
||||||
|
_cancelPendingHide();
|
||||||
|
_open = false;
|
||||||
|
if (_btn) _btn.classList.remove('open');
|
||||||
|
if (_wrap) _wrap.classList.remove('wobble', 'snap', 'tray-dragging');
|
||||||
|
|
||||||
|
if (_isLandscape()) {
|
||||||
|
// Ensure tray is visible before measuring bounds.
|
||||||
|
if (_tray) _tray.style.display = 'grid';
|
||||||
|
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; _wrap.style.width = ''; }
|
||||||
|
_computeBounds();
|
||||||
|
_computeCellSize();
|
||||||
|
if (_wrap) {
|
||||||
|
_wrap.classList.add('tray-dragging');
|
||||||
|
_wrap.style.top = _maxTop + 'px';
|
||||||
|
void _wrap.offsetWidth; // flush reflow so position lands before transition restored
|
||||||
|
_wrap.classList.remove('tray-dragging');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_tray) _tray.style.display = 'none';
|
||||||
|
if (_wrap) { _wrap.style.top = ''; _wrap.style.height = ''; }
|
||||||
|
_computeBounds();
|
||||||
|
_applyVerticalBounds();
|
||||||
|
_computeCellSize();
|
||||||
|
if (_wrap) {
|
||||||
|
_wrap.classList.add('tray-dragging');
|
||||||
|
_wrap.style.left = _maxLeft + 'px';
|
||||||
|
void _wrap.offsetWidth; // flush reflow
|
||||||
|
_wrap.classList.remove('tray-dragging');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
_wrap = document.getElementById('id_tray_wrap');
|
_wrap = document.getElementById('id_tray_wrap');
|
||||||
_btn = document.getElementById('id_tray_btn');
|
_btn = document.getElementById('id_tray_btn');
|
||||||
@@ -403,42 +439,8 @@ var Tray = (function () {
|
|||||||
};
|
};
|
||||||
_btn.addEventListener('click', _onBtnClick);
|
_btn.addEventListener('click', _onBtnClick);
|
||||||
|
|
||||||
window.addEventListener('resize', function () {
|
window.addEventListener('resize', _reposition);
|
||||||
// Always close on resize: bounds change invalidates current position.
|
window.addEventListener('resize:end', _reposition);
|
||||||
// Cancel any in-flight close animation, then force-close state.
|
|
||||||
_cancelPendingHide();
|
|
||||||
_open = false;
|
|
||||||
if (_btn) _btn.classList.remove('open');
|
|
||||||
if (_wrap) _wrap.classList.remove('wobble', 'snap', 'tray-dragging');
|
|
||||||
|
|
||||||
if (_isLandscape()) {
|
|
||||||
// Ensure tray is visible before measuring bounds.
|
|
||||||
if (_tray) _tray.style.display = 'grid';
|
|
||||||
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; _wrap.style.width = ''; }
|
|
||||||
_computeBounds();
|
|
||||||
_computeCellSize();
|
|
||||||
// Snap to closed without transition (resize fires continuously).
|
|
||||||
if (_wrap) {
|
|
||||||
_wrap.classList.add('tray-dragging');
|
|
||||||
_wrap.style.top = _maxTop + 'px';
|
|
||||||
void _wrap.offsetWidth; // flush reflow so position lands before transition restored
|
|
||||||
_wrap.classList.remove('tray-dragging');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_tray) _tray.style.display = 'none';
|
|
||||||
if (_wrap) { _wrap.style.top = ''; _wrap.style.height = ''; }
|
|
||||||
_computeBounds();
|
|
||||||
_applyVerticalBounds();
|
|
||||||
_computeCellSize();
|
|
||||||
// Snap to closed without transition.
|
|
||||||
if (_wrap) {
|
|
||||||
_wrap.classList.add('tray-dragging');
|
|
||||||
_wrap.style.left = _maxLeft + 'px';
|
|
||||||
void _wrap.offsetWidth; // flush reflow
|
|
||||||
_wrap.classList.remove('tray-dragging');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset() — restores module state; used by Jasmine afterEach
|
// reset() — restores module state; used by Jasmine afterEach
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ body {
|
|||||||
border-left: 0.1rem solid rgba(var(--secUser), 0.3);
|
border-left: 0.1rem solid rgba(var(--secUser), 0.3);
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
#id_footer_nav {
|
#id_footer_nav {
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
|
|||||||
@@ -40,6 +40,27 @@ html:has(.gate-backdrop) {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Aperture fill — solid --duoUser layer that covers the game table (.room-page).
|
||||||
|
// Uses position:absolute so it's clipped to .room-page bounds (overflow:hidden),
|
||||||
|
// naturally staying below the h2 title + navbar/footer in both orientations.
|
||||||
|
// Sits at z-90: below blur backdrops (z-100) which render on top via backdrop-filter.
|
||||||
|
// Fades in/out via opacity transition when a backdrop class is present.
|
||||||
|
#id_aperture_fill {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(var(--duoUser), 1);
|
||||||
|
z-index: 90;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:has(.gate-backdrop) #id_aperture_fill,
|
||||||
|
html:has(.sig-backdrop) #id_aperture_fill,
|
||||||
|
html:has(.role-select-backdrop) #id_aperture_fill {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.gate-backdrop {
|
.gate-backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -781,8 +802,8 @@ $card-h: 60px;
|
|||||||
// Landscape mobile — aggressively scale down to fit short viewport
|
// Landscape mobile — aggressively scale down to fit short viewport
|
||||||
@media (orientation: landscape) and (max-width: 1440px) {
|
@media (orientation: landscape) and (max-width: 1440px) {
|
||||||
// Sink navbar below gate/role-select overlays when a modal is open.
|
// Sink navbar below gate/role-select overlays when a modal is open.
|
||||||
// Landscape navbar z-index is 300 (_base.scss); gate-backdrop/overlay are
|
// Landscape navbar z-index is 100 (_base.scss); gate-backdrop/overlay are
|
||||||
// 100/120, so the sidebar bleeds over the modal without this override.
|
// 100/120 — same level causes paint-order ties so we drop it to 50.
|
||||||
html:has(.gate-backdrop) body .container .navbar,
|
html:has(.gate-backdrop) body .container .navbar,
|
||||||
html:has(.role-select-backdrop) body .container .navbar {
|
html:has(.role-select-backdrop) body .container .navbar {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
@@ -959,7 +980,7 @@ html:has(.sig-backdrop) {
|
|||||||
|
|
||||||
.sig-card {
|
.sig-card {
|
||||||
aspect-ratio: 5 / 8;
|
aspect-ratio: 5 / 8;
|
||||||
border-radius: 3px;
|
border-radius: 0.4rem;
|
||||||
background: rgba(var(--priUser), 0.97);
|
background: rgba(var(--priUser), 0.97);
|
||||||
border: 1px solid rgba(var(--secUser), 0.3);
|
border: 1px solid rgba(var(--secUser), 0.3);
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1061,7 +1082,7 @@ html:has(.sig-backdrop) {
|
|||||||
.sig-overlay { padding-left: 4rem; }
|
.sig-overlay { padding-left: 4rem; }
|
||||||
.sig-modal { max-width: none; }
|
.sig-modal { max-width: none; }
|
||||||
.sig-deck-grid {
|
.sig-deck-grid {
|
||||||
grid-template-columns: repeat(9, 1fr);
|
grid-template-columns: repeat(9, minmax(0, 90px));
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="room-page" data-room-id="{{ room.id }}"
|
<div class="room-page" data-room-id="{{ room.id }}"
|
||||||
{% if room.table_status %}data-select-role-url="{% url 'epic:select_role' room.id %}"{% endif %}>
|
{% if room.table_status %}data-select-role-url="{% url 'epic:select_role' room.id %}"{% endif %}>
|
||||||
|
<div id="id_aperture_fill"></div>
|
||||||
<div class="room-shell">
|
<div class="room-shell">
|
||||||
<div id="id_game_table" class="room-table">
|
<div id="id_game_table" class="room-table">
|
||||||
{% if room.table_status == "ROLE_SELECT" %}
|
{% if room.table_status == "ROLE_SELECT" %}
|
||||||
|
|||||||
Reference in New Issue
Block a user