Sfoglia il codice sorgente

fix(ui): make zoomed flow diagram fill the modal and add prominent close button

The cloned <svg> inherits no intrinsic size, so without an explicit width
the modal's flex centering collapsed it to ~300x150 — visually a small
white rectangle inside the dark backdrop.

Move the close button out of the content container and pin it as a fixed
viewport-anchored pill ("✕ Close") in the top-right, with a strong
shadow so it reads on either light or dark imagery. Size cloned media
explicitly: SVG fills min(1400px, viewport - margin) with viewBox-driven
height, images are bounded by both viewport-relative max-width and
max-height, and inline styles/classes from the source rendering are
stripped so modal CSS controls sizing without interference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ClaudePriv@chiappa.zhdk.ch 4 ore fa
parent
commit
fd26cda17a
1 ha cambiato i file con 88 aggiunte e 32 eliminazioni
  1. 88 32
      ui/public/about.html

+ 88 - 32
ui/public/about.html

@@ -262,44 +262,89 @@
         /* Zoom modal */
         .zoom-modal {
             position: fixed; inset: 0; z-index: 100;
-            background: rgba(2, 6, 23, 0.9);
+            background: rgba(2, 6, 23, 0.92);
             display: none;
             align-items: center; justify-content: center;
-            padding: 2rem;
+            padding: 1.5rem;
             backdrop-filter: blur(6px);
         }
         .zoom-modal[aria-hidden="false"] { display: flex; }
         .zoom-modal-content {
             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);
-            padding: 1rem;
+            padding: 0.75rem;
+            max-width: calc(100vw - 3rem);
+            max-height: calc(100vh - 3rem);
             overflow: auto;
+            display: flex;
+            align-items: center;
+            justify-content: center;
         }
         .zoom-modal-content > img,
         .zoom-modal-content > svg {
             display: block;
-            max-width: calc(96vw - 2rem);
-            max-height: calc(92vh - 2rem);
-            width: auto; height: auto;
             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 {
-            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) {
-            .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>
 </head>
@@ -809,9 +854,12 @@ docker compose up -d
 </footer>
 
 <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">&times;</span>
+        <span>Close</span>
+    </button>
     <div class="zoom-modal-content" role="document">
-        <button type="button" class="zoom-close" aria-label="Close enlarged view">&times;</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>
 
@@ -824,17 +872,27 @@ docker compose up -d
     const label = modal.querySelector('#zoom-modal-label');
     let lastFocused = null;
 
-    function openZoom(source) {
-        // Drop any previously cloned media (keep close button + label).
+    function clearMedia() {
         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;
-        if (!media) return;
+        if (!media || (media.tagName !== 'IMG' && media.tagName.toLowerCase() !== 'svg')) return;
         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);
         label.textContent = source.getAttribute('data-zoom-label') || 'Enlarged view';
@@ -847,9 +905,7 @@ docker compose up -d
     function closeZoom() {
         modal.setAttribute('aria-hidden', 'true');
         document.documentElement.style.overflow = '';
-        Array.from(content.children).forEach((child) => {
-            if (child !== closeBtn && child !== label) child.remove();
-        });
+        clearMedia();
         if (lastFocused && typeof lastFocused.focus === 'function') {
             lastFocused.focus();
         }