From c4531cb05346b0845b129dbd65234a28b0ad4748 Mon Sep 17 00:00:00 2001 From: s0lray Date: Thu, 26 Mar 2026 18:12:49 -0400 Subject: [PATCH] Add mobile-only theme toggle in header and notes overlay panel (THE-118) On mobile (<=768px), the legend, notes, and about sections consumed most of the viewport, leaving the D3 tree nearly unusable. This moves the theme toggle into the header as a compact "Light Mode"/"Dark Mode" button, hides the below-tree content, and adds a floating "Notes" button that opens all that content in a full-screen overlay panel (matching the existing tool detail panel pattern). Desktop layout is completely unchanged. Co-Authored-By: Paperclip --- public/css/arf.css | 169 +++++++++++++++++++++++++++++++++++++++++++-- public/index.html | 41 +++++++---- public/js/arf.js | 35 ++++++++++ 3 files changed, 228 insertions(+), 17 deletions(-) diff --git a/public/css/arf.css b/public/css/arf.css index 18153d4..b539fce 100644 --- a/public/css/arf.css +++ b/public/css/arf.css @@ -260,18 +260,177 @@ a { text-align: center; } +/* Mobile theme toggle (hidden on desktop) */ +#mobile-theme-toggle { + display: none; +} + +/* Mobile notes button (hidden on desktop) */ +#mobile-notes-btn { + display: none; +} + +/* Notes overlay (hidden on desktop) */ +#notes-overlay { + display: none; +} + +/* Notes panel (hidden on desktop) */ +#notes-panel { + display: none; +} + /* === Mobile responsive === */ @media (max-width: 768px) { #header { font-size: var(--font-size-lg); top: 10px; + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-3); + padding: var(--space-2) var(--space-3); } + #header hr { + display: none; + } + + #header-title { + flex: 1; + } + + #mobile-theme-toggle { + display: inline-block; + background: none; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-family: var(--font-family); + padding: var(--space-1) var(--space-2); + cursor: pointer; + white-space: nowrap; + flex-shrink: 0; + } + + #mobile-theme-toggle:hover { + color: var(--color-text-primary); + border-color: var(--color-text-secondary); + } + + /* Hide legend and notes content on mobile */ .legend { - position: relative; - width: auto; - max-width: 100%; - padding: var(--space-2) var(--space-3); - box-sizing: border-box; + display: none; + } + + #notes-content { + display: none; + } + + /* Floating notes button */ + #mobile-notes-btn { + display: block; + position: fixed; + bottom: var(--space-4); + left: 50%; + transform: translateX(-50%); + background: var(--color-surface); + color: var(--color-text-primary); + border: 1px solid var(--color-border); + border-radius: 20px; + padding: var(--space-2) var(--space-5); + font-size: var(--font-size-base); + font-family: var(--font-family); + font-weight: bold; + cursor: pointer; + box-shadow: var(--shadow-md); + z-index: 100; + } + + #mobile-notes-btn:hover { + background: var(--color-bg); + } + + /* Notes overlay */ + #notes-overlay { + display: none; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 299; + } + + #notes-overlay.visible { + display: block; + } + + /* Notes panel */ + #notes-panel { + display: flex; + flex-direction: column; + position: fixed; + top: 0; + right: 0; + width: 100vw; + height: 100vh; + background: var(--color-surface); + z-index: 300; + transform: translateX(100%); + transition: transform 250ms ease; + will-change: transform; + } + + #notes-panel.open { + transform: translateX(0); + } + + #notes-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-4); + border-bottom: 1px solid var(--color-border); + flex-shrink: 0; + } + + #notes-panel-title { + font-size: var(--font-size-lg); + font-weight: bold; + color: var(--color-text-primary); + } + + #notes-panel-close { + background: none; + border: none; + color: var(--color-text-secondary); + font-size: 20px; + line-height: 1; + cursor: pointer; + padding: 2px var(--space-1); + border-radius: var(--radius-sm); + } + + #notes-panel-close:hover { + color: var(--color-text-primary); + background: var(--color-bg); + } + + #notes-panel-body { + flex: 1; + padding: var(--space-4); + overflow-y: auto; + font-size: var(--font-size-base); + color: var(--color-text-primary); + line-height: 1.6; + } + + #notes-panel-body h3 { + margin-top: var(--space-4); + margin-bottom: var(--space-2); + } + + #notes-panel-body h3:first-child { + margin-top: 0; } } diff --git a/public/index.html b/public/index.html index 236bab2..a968bdd 100644 --- a/public/index.html +++ b/public/index.html @@ -26,7 +26,8 @@
@@ -106,25 +107,41 @@
-

Notes

- OSINT framework focused on gathering information from free tools or resources. The intention is to help people find free OSINT resources. Some of the sites included might require registration or offer more data for $$$, but you should be able to get at least a portion of the available information for no cost.
+
+

Notes

+ OSINT framework focused on gathering information from free tools or resources. The intention is to help people find free OSINT resources. Some of the sites included might require registration or offer more data for $$$, but you should be able to get at least a portion of the available information for no cost.
-

I originally created this framework with an information security point of view. Since then, the response from other fields and disciplines has been incredible. I would love to be able to include any other OSINT resources, especially from fields outside of infosec. Please let me know about anything that might be missing!

+

I originally created this framework with an information security point of view. Since then, the response from other fields and disciplines has been incredible. I would love to be able to include any other OSINT resources, especially from fields outside of infosec. Please let me know about anything that might be missing!

-

For Update Notifications

- Follow me on Twitter: @jnordine
- Watch or star the project on Github: https://github.com/lockfale/osint-framework +

For Update Notifications

+ Follow me on Twitter: @jnordine
+ Watch or star the project on Github: https://github.com/lockfale/osint-framework -

Suggestions, Comments, Feedback

- Feedback or new tool suggestions are extremely welcome! Please feel free to reach out on Twitter or submit an issue on Github. +

Suggestions, Comments, Feedback

+ Feedback or new tool suggestions are extremely welcome! Please feel free to reach out on Twitter or submit an issue on Github. +
+ + + + + +
+
+
+ Notes + +
+
+
diff --git a/public/js/arf.js b/public/js/arf.js index 3b05819..4ad1503 100644 --- a/public/js/arf.js +++ b/public/js/arf.js @@ -766,6 +766,41 @@ function goDark() { if (btn) { btn.textContent = isLight ? "Switch to dark mode" : "Switch to light mode"; } + var mBtn = document.getElementById("mobile-theme-toggle"); + if (mBtn) { + mBtn.textContent = isLight ? "Dark Mode" : "Light Mode"; + } // Re-render to pick up new CSS variable values for D3 inline styles. update(root); } + +// Mobile notes panel toggle +function toggleNotesPanel() { + var panel = document.getElementById("notes-panel"); + var overlay = document.getElementById("notes-overlay"); + if (!panel) return; + + var isOpen = panel.classList.toggle("open"); + if (overlay) overlay.classList.toggle("visible", isOpen); + + // Populate panel body on first open + if (isOpen && !panel._populated) { + var body = document.getElementById("notes-panel-body"); + var source = document.getElementById("notes-content"); + var legend = document.querySelector(".legend"); + if (body) { + body.innerHTML = ""; + if (legend) body.innerHTML += legend.innerHTML; + if (source) body.innerHTML += source.innerHTML; + } + panel._populated = true; + } +} + +// Close notes panel when overlay is clicked +(function() { + var overlay = document.getElementById("notes-overlay"); + if (overlay) { + overlay.addEventListener("click", function() { toggleNotesPanel(); }); + } +})();