|
@@ -262,44 +262,89 @@
|
|
|
/* Zoom modal */
|
|
/* Zoom modal */
|
|
|
.zoom-modal {
|
|
.zoom-modal {
|
|
|
position: fixed; inset: 0; z-index: 100;
|
|
position: fixed; inset: 0; z-index: 100;
|
|
|
- background: rgba(2, 6, 23, 0.9);
|
|
|
|
|
|
|
+ background: rgba(2, 6, 23, 0.92);
|
|
|
display: none;
|
|
display: none;
|
|
|
align-items: center; justify-content: center;
|
|
align-items: center; justify-content: center;
|
|
|
- padding: 2rem;
|
|
|
|
|
|
|
+ padding: 1.5rem;
|
|
|
backdrop-filter: blur(6px);
|
|
backdrop-filter: blur(6px);
|
|
|
}
|
|
}
|
|
|
.zoom-modal[aria-hidden="false"] { display: flex; }
|
|
.zoom-modal[aria-hidden="false"] { display: flex; }
|
|
|
.zoom-modal-content {
|
|
.zoom-modal-content {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- max-width: 96vw; max-height: 92vh;
|
|
|
|
|
- display: flex; align-items: center; justify-content: center;
|
|
|
|
|
- background: var(--bg-card); border-radius: 0.75rem;
|
|
|
|
|
|
|
+ background: var(--bg-card);
|
|
|
|
|
+ border-radius: 0.75rem;
|
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
|
|
|
- padding: 1rem;
|
|
|
|
|
|
|
+ padding: 0.75rem;
|
|
|
|
|
+ max-width: calc(100vw - 3rem);
|
|
|
|
|
+ max-height: calc(100vh - 3rem);
|
|
|
overflow: auto;
|
|
overflow: auto;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
}
|
|
}
|
|
|
.zoom-modal-content > img,
|
|
.zoom-modal-content > img,
|
|
|
.zoom-modal-content > svg {
|
|
.zoom-modal-content > svg {
|
|
|
display: block;
|
|
display: block;
|
|
|
- max-width: calc(96vw - 2rem);
|
|
|
|
|
- max-height: calc(92vh - 2rem);
|
|
|
|
|
- width: auto; height: auto;
|
|
|
|
|
border-radius: 0.5rem;
|
|
border-radius: 0.5rem;
|
|
|
}
|
|
}
|
|
|
|
|
+ .zoom-modal-content > svg {
|
|
|
|
|
+ width: min(1400px, calc(100vw - 5rem));
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ max-height: calc(100vh - 6rem);
|
|
|
|
|
+ }
|
|
|
|
|
+ .zoom-modal-content > img {
|
|
|
|
|
+ max-width: calc(100vw - 5rem);
|
|
|
|
|
+ max-height: calc(100vh - 6rem);
|
|
|
|
|
+ width: auto; height: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Prominent close button — fixed in viewport top-right */
|
|
|
.zoom-close {
|
|
.zoom-close {
|
|
|
- position: absolute; top: -3rem; right: 0;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.12);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- border: 1px solid rgba(255, 255, 255, 0.25);
|
|
|
|
|
- width: 40px; height: 40px; border-radius: 50%;
|
|
|
|
|
- font-size: 1.4rem; line-height: 1; cursor: pointer;
|
|
|
|
|
- display: flex; align-items: center; justify-content: center;
|
|
|
|
|
- transition: background 0.15s ease;
|
|
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: 1.25rem; right: 1.25rem;
|
|
|
|
|
+ z-index: 110;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ color: #0f172a;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ min-width: 52px;
|
|
|
|
|
+ height: 52px;
|
|
|
|
|
+ padding: 0 1.25rem 0 1rem;
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ font: 600 0.95rem/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ display: inline-flex; align-items: center; justify-content: center;
|
|
|
|
|
+ gap: 0.5rem;
|
|
|
|
|
+ box-shadow: 0 12px 28px rgba(0,0,0,0.45), 0 0 0 1px rgba(15,23,42,0.05);
|
|
|
|
|
+ transition: background 0.15s ease, transform 0.12s ease, box-shadow 0.15s ease;
|
|
|
|
|
+ }
|
|
|
|
|
+ .zoom-close .x {
|
|
|
|
|
+ display: inline-flex; align-items: center; justify-content: center;
|
|
|
|
|
+ width: 26px; height: 26px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ background: #0f172a; color: #ffffff;
|
|
|
|
|
+ font-size: 1rem; font-weight: 700; line-height: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ .zoom-close:hover {
|
|
|
|
|
+ background: #f1f5f9;
|
|
|
|
|
+ transform: scale(1.04);
|
|
|
|
|
+ box-shadow: 0 14px 32px rgba(0,0,0,0.5), 0 0 0 1px rgba(15,23,42,0.08);
|
|
|
|
|
+ }
|
|
|
|
|
+ .zoom-close:focus-visible {
|
|
|
|
|
+ outline: 3px solid #6366f1;
|
|
|
|
|
+ outline-offset: 3px;
|
|
|
}
|
|
}
|
|
|
- .zoom-close:hover { background: rgba(255, 255, 255, 0.22); }
|
|
|
|
|
@media (max-width: 600px) {
|
|
@media (max-width: 600px) {
|
|
|
- .zoom-modal { padding: 1rem; }
|
|
|
|
|
- .zoom-close { top: 0.4rem; right: 0.4rem; }
|
|
|
|
|
|
|
+ .zoom-modal { padding: 0.75rem; }
|
|
|
|
|
+ .zoom-modal-content { padding: 0.4rem; max-width: calc(100vw - 1.5rem); max-height: calc(100vh - 1.5rem); }
|
|
|
|
|
+ .zoom-modal-content > svg { width: calc(100vw - 2rem); max-height: calc(100vh - 2rem); }
|
|
|
|
|
+ .zoom-modal-content > img { max-width: calc(100vw - 2rem); max-height: calc(100vh - 2rem); }
|
|
|
|
|
+ .zoom-close {
|
|
|
|
|
+ top: 0.6rem; right: 0.6rem;
|
|
|
|
|
+ min-width: 44px; height: 44px;
|
|
|
|
|
+ padding: 0 0.9rem 0 0.7rem;
|
|
|
|
|
+ font-size: 0.85rem;
|
|
|
|
|
+ }
|
|
|
|
|
+ .zoom-close .x { width: 22px; height: 22px; font-size: 0.85rem; }
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|
|
|
</head>
|
|
</head>
|
|
@@ -809,9 +854,12 @@ docker compose up -d
|
|
|
</footer>
|
|
</footer>
|
|
|
|
|
|
|
|
<div id="zoom-modal" class="zoom-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="zoom-modal-label">
|
|
<div id="zoom-modal" class="zoom-modal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="zoom-modal-label">
|
|
|
|
|
+ <button type="button" class="zoom-close" aria-label="Close enlarged view">
|
|
|
|
|
+ <span class="x" aria-hidden="true">×</span>
|
|
|
|
|
+ <span>Close</span>
|
|
|
|
|
+ </button>
|
|
|
<div class="zoom-modal-content" role="document">
|
|
<div class="zoom-modal-content" role="document">
|
|
|
- <button type="button" class="zoom-close" aria-label="Close enlarged view">×</button>
|
|
|
|
|
- <span id="zoom-modal-label" class="sr-only" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)"></span>
|
|
|
|
|
|
|
+ <span id="zoom-modal-label" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)"></span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -824,17 +872,27 @@ docker compose up -d
|
|
|
const label = modal.querySelector('#zoom-modal-label');
|
|
const label = modal.querySelector('#zoom-modal-label');
|
|
|
let lastFocused = null;
|
|
let lastFocused = null;
|
|
|
|
|
|
|
|
- function openZoom(source) {
|
|
|
|
|
- // Drop any previously cloned media (keep close button + label).
|
|
|
|
|
|
|
+ function clearMedia() {
|
|
|
Array.from(content.children).forEach((child) => {
|
|
Array.from(content.children).forEach((child) => {
|
|
|
- if (child !== closeBtn && child !== label) child.remove();
|
|
|
|
|
|
|
+ if (child !== label) child.remove();
|
|
|
});
|
|
});
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function openZoom(source) {
|
|
|
|
|
+ clearMedia();
|
|
|
const media = source.querySelector('svg, img') || source;
|
|
const media = source.querySelector('svg, img') || source;
|
|
|
- if (!media) return;
|
|
|
|
|
|
|
+ if (!media || (media.tagName !== 'IMG' && media.tagName.toLowerCase() !== 'svg')) return;
|
|
|
const clone = media.cloneNode(true);
|
|
const clone = media.cloneNode(true);
|
|
|
- if (clone.tagName === 'IMG') {
|
|
|
|
|
- clone.removeAttribute('style');
|
|
|
|
|
- clone.removeAttribute('class');
|
|
|
|
|
|
|
+ clone.removeAttribute('class');
|
|
|
|
|
+ // Strip any inline styles from the source rendering (e.g. width:100%
|
|
|
|
|
+ // on the page diagram) so the modal's CSS rules can size the clone.
|
|
|
|
|
+ clone.removeAttribute('style');
|
|
|
|
|
+ if (clone.tagName.toLowerCase() === 'svg') {
|
|
|
|
|
+ // The cloned <svg> has no intrinsic size; pin it to viewBox-driven
|
|
|
|
|
+ // sizing via attributes so the modal can scale it up.
|
|
|
|
|
+ clone.setAttribute('width', '100%');
|
|
|
|
|
+ clone.setAttribute('height', '100%');
|
|
|
|
|
+ clone.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
|
}
|
|
}
|
|
|
content.appendChild(clone);
|
|
content.appendChild(clone);
|
|
|
label.textContent = source.getAttribute('data-zoom-label') || 'Enlarged view';
|
|
label.textContent = source.getAttribute('data-zoom-label') || 'Enlarged view';
|
|
@@ -847,9 +905,7 @@ docker compose up -d
|
|
|
function closeZoom() {
|
|
function closeZoom() {
|
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
|
document.documentElement.style.overflow = '';
|
|
document.documentElement.style.overflow = '';
|
|
|
- Array.from(content.children).forEach((child) => {
|
|
|
|
|
- if (child !== closeBtn && child !== label) child.remove();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ clearMedia();
|
|
|
if (lastFocused && typeof lastFocused.focus === 'function') {
|
|
if (lastFocused && typeof lastFocused.focus === 'function') {
|
|
|
lastFocused.focus();
|
|
lastFocused.focus();
|
|
|
}
|
|
}
|