mirror of
https://github.com/lockfale/OSINT-Framework.git
synced 2026-08-22 22:02:23 +02:00
feat(ui): implement dark mode as default with CSS variables and theme persistence
Closes THE-34, THE-35, THE-36. - Replaces all hardcoded colors in arf.css with CSS custom properties - Dark palette defined on :root (default); light palette on body.light-mode - Removes old .dark-Mode class; D3 inline styles now read CSS vars at render time - goDark() toggles light-mode class and persists preference to localStorage - Flash-prevention inline script in body applies stored theme before first paint - Toggle button label reflects current theme state Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
+56
-24
@@ -1,18 +1,51 @@
|
||||
/* === Color tokens === */
|
||||
:root {
|
||||
--color-bg: #1a1a2e;
|
||||
--color-surface: #16213e;
|
||||
--color-text-primary: #e0e0e0;
|
||||
--color-text-secondary: #888;
|
||||
--color-node-stroke: #4a9eff;
|
||||
--color-node-fill-branch: #2a4a7f;
|
||||
--color-node-fill-leaf: #1a1a2e;
|
||||
--color-link: #444;
|
||||
--color-noscript-bg: #2a1a00;
|
||||
--color-noscript-border: #7a4a00;
|
||||
--color-legend: #e0e0e0;
|
||||
--color-header: #e0e0e0;
|
||||
}
|
||||
|
||||
body.light-mode {
|
||||
--color-bg: #ffffff;
|
||||
--color-surface: #f5f5f5;
|
||||
--color-text-primary: #000000;
|
||||
--color-text-secondary: #999;
|
||||
--color-node-stroke: steelblue;
|
||||
--color-node-fill-branch: lightsteelblue;
|
||||
--color-node-fill-leaf: #ffffff;
|
||||
--color-link: #ccc;
|
||||
--color-noscript-bg: rgb(255, 228, 196);
|
||||
--color-noscript-border: rgb(255, 204, 153);
|
||||
--color-legend: #000000;
|
||||
--color-header: #000000;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #ffffff;
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text-primary);
|
||||
font-size: 14px;
|
||||
font-family: "Helvetica Neue", Helvetica;
|
||||
}
|
||||
|
||||
noscript p{
|
||||
noscript p {
|
||||
text-align: center;
|
||||
background-color: rgb(255, 228, 196);
|
||||
background-color: var(--color-noscript-bg);
|
||||
color: var(--color-text-primary);
|
||||
margin: 0 auto;
|
||||
width: 80vw;
|
||||
padding: 25px 0;
|
||||
margin-top: 80px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgb(255, 204, 153);
|
||||
border: 1px solid var(--color-noscript-border);
|
||||
margin-bottom: 20px;
|
||||
display: flex-wrap;
|
||||
}
|
||||
@@ -27,20 +60,7 @@ noscript p{
|
||||
font-weight: bold;
|
||||
top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dark-Mode{
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
.node text {
|
||||
fill: #fff;
|
||||
}
|
||||
.legend{
|
||||
color: #fff;
|
||||
}
|
||||
path.link{
|
||||
stroke: #444;
|
||||
}
|
||||
color: var(--color-header);
|
||||
}
|
||||
|
||||
.legend {
|
||||
@@ -49,7 +69,11 @@ noscript p{
|
||||
right: 0;
|
||||
width: 330px;
|
||||
font-size: 11px;
|
||||
color: #000;
|
||||
color: var(--color-legend);
|
||||
}
|
||||
|
||||
.legend a {
|
||||
color: var(--color-node-stroke);
|
||||
}
|
||||
|
||||
.node {
|
||||
@@ -58,18 +82,26 @@ noscript p{
|
||||
|
||||
.node circle {
|
||||
cursor: pointer;
|
||||
fill: #fff;
|
||||
stroke: steelblue;
|
||||
fill: var(--color-node-fill-leaf);
|
||||
stroke: var(--color-node-stroke);
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.node text {
|
||||
font-size: 12px;
|
||||
fill: rgb(0, 0, 0);
|
||||
fill: var(--color-text-primary);
|
||||
}
|
||||
|
||||
path.link {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke: var(--color-link);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-node-stroke);
|
||||
}
|
||||
|
||||
+20
-3
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
|
||||
<!-- Viewport Settings -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||
@@ -15,6 +15,14 @@
|
||||
browser to use the site.</p>
|
||||
</noscript>
|
||||
<body>
|
||||
<!-- Apply light-mode class immediately if stored preference is light -->
|
||||
<script>
|
||||
(function() {
|
||||
if (localStorage.getItem("theme") === "light") {
|
||||
document.body.classList.add("light-mode");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<div id="body">
|
||||
<div id="header">
|
||||
OSINT Framework
|
||||
@@ -28,8 +36,17 @@
|
||||
(D) - Google Dork, for more information: <a href="https://en.wikipedia.org/wiki/Google_hacking">Google Hacking</a><br>
|
||||
(R) - Requires registration<br>
|
||||
(M) - Indicates a URL that contains the search term and the URL itself must be edited manually<br></p>
|
||||
<button onclick="goDark()">Toggle dark mode</button></div>
|
||||
|
||||
<button id="theme-toggle" onclick="goDark()">Switch to light mode</button>
|
||||
<script>
|
||||
(function() {
|
||||
var btn = document.getElementById("theme-toggle");
|
||||
if (btn && localStorage.getItem("theme") === "light") {
|
||||
btn.textContent = "Switch to dark mode";
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<h3>Notes</h3>
|
||||
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.<br>
|
||||
|
||||
|
||||
+32
-20
@@ -18,6 +18,10 @@ var vis = d3.select("#body").append("svg")
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin[3] + "," + margin[0] + ")");
|
||||
|
||||
function getCSSVar(name) {
|
||||
return getComputedStyle(document.body).getPropertyValue(name).trim();
|
||||
}
|
||||
|
||||
d3.json("arf.json").then(function(json) {
|
||||
root = d3.hierarchy(json, function(d) {
|
||||
return d && d.children ? d.children.filter(function(c) { return c != null; }) : null;
|
||||
@@ -33,20 +37,11 @@ d3.json("arf.json").then(function(json) {
|
||||
}
|
||||
}
|
||||
|
||||
/* function toggleAll(d) {
|
||||
if (d.children) {
|
||||
d.children.forEach(toggleAll);
|
||||
toggle(d);
|
||||
}
|
||||
} */
|
||||
root.children.forEach(collapse);
|
||||
update(root);
|
||||
});
|
||||
|
||||
function update(source) {
|
||||
// var duration = d3.event && d3.event.altKey ? 5000 : 500;
|
||||
|
||||
// Compute the new tree layout.
|
||||
tree(root);
|
||||
var nodes = root.descendants().reverse();
|
||||
var links = root.links();
|
||||
@@ -54,7 +49,7 @@ function update(source) {
|
||||
// Normalize for fixed-depth.
|
||||
nodes.forEach(function(d) { d.y = d.depth * 180; });
|
||||
|
||||
// Update the nodes…
|
||||
// Update the nodes
|
||||
var node = vis.selectAll("g.node")
|
||||
.data(nodes, function(d) { return d.id || (d.id = ++i); });
|
||||
|
||||
@@ -66,7 +61,9 @@ function update(source) {
|
||||
|
||||
nodeEnter.append("circle")
|
||||
.attr("r", 1e-6)
|
||||
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
||||
.style("fill", function(d) {
|
||||
return d._children ? getCSSVar("--color-node-fill-branch") : getCSSVar("--color-node-fill-leaf");
|
||||
});
|
||||
|
||||
nodeEnter.append('a')
|
||||
.attr("target", "_blank")
|
||||
@@ -76,7 +73,9 @@ function update(source) {
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
|
||||
.text(function(d) { return d.data.name; })
|
||||
.style("fill", function(d) { return d.data.free ? 'black' : '#999'; })
|
||||
.style("fill", function(d) {
|
||||
return d.data.free ? getCSSVar("--color-text-primary") : getCSSVar("--color-text-secondary");
|
||||
})
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
nodeEnter.append("title")
|
||||
@@ -91,10 +90,15 @@ function update(source) {
|
||||
|
||||
nodeUpdate.select("circle")
|
||||
.attr("r", 6)
|
||||
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
|
||||
.style("fill", function(d) {
|
||||
return d._children ? getCSSVar("--color-node-fill-branch") : getCSSVar("--color-node-fill-leaf");
|
||||
});
|
||||
|
||||
nodeUpdate.select("text")
|
||||
.style("fill-opacity", 1);
|
||||
.style("fill-opacity", 1)
|
||||
.style("fill", function(d) {
|
||||
return d.data.free ? getCSSVar("--color-text-primary") : getCSSVar("--color-text-secondary");
|
||||
});
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
var nodeExit = node.exit().transition()
|
||||
@@ -108,7 +112,7 @@ function update(source) {
|
||||
nodeExit.select("text")
|
||||
.style("fill-opacity", 1e-6);
|
||||
|
||||
// Update the links…
|
||||
// Update the links
|
||||
var link = vis.selectAll("path.link")
|
||||
.data(links, function(d) { return d.target.id; });
|
||||
|
||||
@@ -129,7 +133,7 @@ function update(source) {
|
||||
.duration(duration)
|
||||
.attr("d", diagonal);
|
||||
|
||||
// Transition exiting nodes to the parent's new position.
|
||||
// Transition exiting links to the parent's new position.
|
||||
link.exit().transition()
|
||||
.duration(duration)
|
||||
.attr("d", function(d) {
|
||||
@@ -155,8 +159,16 @@ function toggle(d) {
|
||||
d._children = null;
|
||||
}
|
||||
}
|
||||
//Togle Dark Mode
|
||||
|
||||
// Toggle light/dark mode and persist preference.
|
||||
function goDark() {
|
||||
var element = document.body;
|
||||
element.classList.toggle("dark-Mode");
|
||||
}
|
||||
var body = document.body;
|
||||
var isLight = body.classList.toggle("light-mode");
|
||||
localStorage.setItem("theme", isLight ? "light" : "dark");
|
||||
var btn = document.getElementById("theme-toggle");
|
||||
if (btn) {
|
||||
btn.textContent = isLight ? "Switch to dark mode" : "Switch to light mode";
|
||||
}
|
||||
// Re-render to pick up new CSS variable values for D3 inline styles.
|
||||
update(root);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user