Skip to content

Commit e256537

Browse files
Merge pull request #89 from Kaushal-Loya/feat/dark-theme-implementation
[WOC] Implement Persistent Dark Theme with Premium Animated Toggle
2 parents b1637dd + 5f7cdbe commit e256537

10 files changed

Lines changed: 353 additions & 81 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
<img width="1265" height="1280" alt="image" src="https://github.com/user-attachments/assets/a3f2fbbe-63cb-4983-9079-c071bb1ed97c" />
44

5-
65
## Setup ENV
76

87
Create a `.env` file in the root directory of the project and add the following environment variables:

app/_components/HelpDocsButton.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ const HelpDocsButton = () => {
66
const pathname = usePathname();
77
if (pathname === "/") return null;
88
return (
9-
<a
10-
href="https://evolutionary-algorithms-on-click.github.io/user_docs/user-guide/ea-run.html"
11-
target="_blank"
12-
rel="noopener noreferrer"
13-
className="fixed bottom-6 right-6 z-50 flex items-center gap-2 bg-yellow-400 hover:bg-yellow-500 text-black font-bold py-3 px-5 rounded-full shadow-2xl transition-all duration-300 transform hover:scale-105 active:scale-95 border-2 border-black"
14-
id="help-docs-button"
15-
>
16-
<BookUp2 size={20} />
17-
<span className="hidden md:inline">Read our docs</span>
18-
19-
<ExternalLink size={16} className="opacity-70" />
20-
</a>
9+
<a
10+
href="https://evolutionary-algorithms-on-click.github.io/user_docs/user-guide/ea-run.html"
11+
target="_blank"
12+
rel="noopener noreferrer"
13+
className="fixed bottom-6 right-6 z-50 flex items-center gap-2 bg-yellow-400 hover:bg-yellow-500 text-black font-bold py-3 px-5 rounded-full shadow-2xl transition-all duration-300 transform hover:scale-105 active:scale-95 border-2 border-black"
14+
id="help-docs-button"
15+
>
16+
<BookUp2 size={20} />
17+
<span className="hidden md:inline">Read our docs</span>
18+
19+
<ExternalLink size={16} className="opacity-70" />
20+
</a>
2121
);
2222
};
2323

app/_data/theory.js

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,132 @@
11
export const theoryData = {
22
populationSize: {
3-
title: "Population Size",
4-
explanation: "The number of individuals in each generation. A larger population increases genetic diversity and the chance of finding a global optimum but requires more computational power.",
3+
title: "Population Size",
4+
explanation:
5+
"The number of individuals in each generation. A larger population increases genetic diversity and the chance of finding a global optimum but requires more computational power.",
56
},
67
generations: {
7-
title: "Generations",
8-
explanation: "The total number of iterations the evolutionary process will run. More generations allow the population to converge on better solutions but increase execution time.",
8+
title: "Generations",
9+
explanation:
10+
"The total number of iterations the evolutionary process will run. More generations allow the population to converge on better solutions but increase execution time.",
911
},
1012
cxpb: {
11-
title: "Crossover Probability",
12-
explanation: "The probability that two parent individuals will exchange genetic material. High values promote the recombination of good traits from different individuals.",
13+
title: "Crossover Probability",
14+
explanation:
15+
"The probability that two parent individuals will exchange genetic material. High values promote the recombination of good traits from different individuals.",
1316
},
1417
mutpb: {
15-
title: "Mutation Probability",
16-
explanation: "The probability that an individual's genes will be randomly altered. This introduces new genetic material, helping the population escape local optima.",
18+
title: "Mutation Probability",
19+
explanation:
20+
"The probability that an individual's genes will be randomly altered. This introduces new genetic material, helping the population escape local optima.",
1721
},
1822
hof: {
19-
title: "Hall of Fame",
20-
explanation: "A special archive that stores the absolute best individuals found throughout the entire run, ensuring that the top solutions are never lost during evolution.",
23+
title: "Hall of Fame",
24+
explanation:
25+
"A special archive that stores the absolute best individuals found throughout the entire run, ensuring that the top solutions are never lost during evolution.",
2126
},
2227
mu: {
23-
title: "Mu (μ)",
24-
explanation: "The number of individuals to be selected as parents for the next generation. It defines the size of the breeding pool.",
28+
title: "Mu (μ)",
29+
explanation:
30+
"The number of individuals to be selected as parents for the next generation. It defines the size of the breeding pool.",
2531
},
2632
lambda: {
27-
title: "Lambda (λ)",
28-
explanation: "The number of children to be generated in each step. This determines the exploratory capacity of the algorithm per generation.",
33+
title: "Lambda (λ)",
34+
explanation:
35+
"The number of children to be generated in each step. This determines the exploratory capacity of the algorithm per generation.",
2936
},
3037
tournamentSize: {
31-
title: "Tournament Size",
32-
explanation: "Used in selection: 'k' individuals are picked at random, and the best one wins. A larger tournament size increases selection pressure.",
38+
title: "Tournament Size",
39+
explanation:
40+
"Used in selection: 'k' individuals are picked at random, and the best one wins. A larger tournament size increases selection pressure.",
3341
},
3442
phi1: {
35-
title: "Cognitive Coefficient (φ1)",
36-
explanation: "In PSO, this scales the influence of a particle's personal best performance on its current velocity.",
43+
title: "Cognitive Coefficient (φ1)",
44+
explanation:
45+
"In PSO, this scales the influence of a particle's personal best performance on its current velocity.",
3746
},
3847
phi2: {
39-
title: "Social Coefficient (φ2)",
40-
explanation: "In PSO, this scales the influence of the swarm's global best performance on a particle's current velocity.",
48+
title: "Social Coefficient (φ2)",
49+
explanation:
50+
"In PSO, this scales the influence of the swarm's global best performance on a particle's current velocity.",
4151
},
4252
dimensions: {
43-
title: "Dimensions",
44-
explanation: "The number of variables or parameters that the algorithm is trying to optimize simultaneously.",
53+
title: "Dimensions",
54+
explanation:
55+
"The number of variables or parameters that the algorithm is trying to optimize simultaneously.",
4556
},
4657
algorithmStrategy: {
47-
title: "Algorithm Strategy",
48-
explanation: "The high-level logic governing evolution. 'eaSimple' is a basic generational model, while 'eaMuPlusLambda' and 'eaMuCommaLambda' use more advanced selection and replacement mechanics common in Evolution Strategies.",
58+
title: "Algorithm Strategy",
59+
explanation:
60+
"The high-level logic governing evolution. 'eaSimple' is a basic generational model, while 'eaMuPlusLambda' and 'eaMuCommaLambda' use more advanced selection and replacement mechanics common in Evolution Strategies.",
4961
},
5062
psoStrategy: {
51-
title: "PSO Strategy",
52-
explanation: "Defines the velocity and position update logic for particles. 'original' uses the standard PSO formulas, while variants like 'multiswarm' or 'speciation' help the algorithm handle more complex optimization landscapes.",
63+
title: "PSO Strategy",
64+
explanation:
65+
"Defines the velocity and position update logic for particles. 'original' uses the standard PSO formulas, while variants like 'multiswarm' or 'speciation' help the algorithm handle more complex optimization landscapes.",
5366
},
5467
benchmarkFunction: {
55-
title: "Benchmark Function",
56-
explanation: "Mathematical functions used to evaluate optimization performance. Each function (like Ackley, Rastrigin, or Rosenbrock) represents a unique 'landscape' with different challenges for the algorithm to solve.",
68+
title: "Benchmark Function",
69+
explanation:
70+
"Mathematical functions used to evaluate optimization performance. Each function (like Ackley, Rastrigin, or Rosenbrock) represents a unique 'landscape' with different challenges for the algorithm to solve.",
5771
},
5872
weights: {
5973
title: "Weights",
60-
explanation: "Coefficients that determine the importance of different objectives in the fitness function. If you have multiple goals, weights help the algorithm prioritize between them (e.g., maximizing accuracy vs. minimizing complexity).",
74+
explanation:
75+
"Coefficients that determine the importance of different objectives in the fitness function. If you have multiple goals, weights help the algorithm prioritize between them (e.g., maximizing accuracy vs. minimizing complexity).",
6176
},
6277
matingFunction: {
6378
title: "Mating (Crossover)",
64-
explanation: "The mechanism of biological recombination. It combines genetic information from two parents to generate offspring, aiming to produce better solutions by merging high-performing traits.",
79+
explanation:
80+
"The mechanism of biological recombination. It combines genetic information from two parents to generate offspring, aiming to produce better solutions by merging high-performing traits.",
6581
},
6682
mutationFunction: {
6783
title: "Mutation",
68-
explanation: "Introduces random changes to individuals. This prevents premature convergence and ensures the algorithm explores new areas of the search space, maintaining genetic diversity.",
84+
explanation:
85+
"Introduces random changes to individuals. This prevents premature convergence and ensures the algorithm explores new areas of the search space, maintaining genetic diversity.",
6986
},
7087
selectionFunction: {
7188
title: "Selection",
72-
explanation: "The 'survival of the fittest' phase. It determines which individuals from the current population will be kept as parents for the next generation based on their fitness scores.",
89+
explanation:
90+
"The 'survival of the fittest' phase. It determines which individuals from the current population will be kept as parents for the next generation based on their fitness scores.",
7391
},
7492
datasetUrl: {
7593
title: "Dataset Source",
76-
explanation: "The raw data used for training and evaluation. In ML tuning, the algorithm uses this data to test how well different model parameters perform.",
94+
explanation:
95+
"The raw data used for training and evaluation. In ML tuning, the algorithm uses this data to test how well different model parameters perform.",
7796
},
7897
targetColumn: {
7998
title: "Target Variable",
80-
explanation: "The specific column in your dataset that you want the model to predict (the dependent variable).",
99+
explanation:
100+
"The specific column in your dataset that you want the model to predict (the dependent variable).",
81101
},
82102
mlEvalFunction: {
83103
title: "ML Evaluation",
84-
explanation: "The metric used to judge the performance of the machine learning model (e.g., Accuracy, F1-score, or MSE). The EA optimizes this value.",
104+
explanation:
105+
"The metric used to judge the performance of the machine learning model (e.g., Accuracy, F1-score, or MSE). The EA optimizes this value.",
85106
},
86107
individualSize: {
87108
title: "Individual Size",
88-
explanation: "The length of the chromosome or the number of genes representing a solution. For example, in bit manipulation, it's the number of bits per individual.",
109+
explanation:
110+
"The length of the chromosome or the number of genes representing a solution. For example, in bit manipulation, it's the number of bits per individual.",
89111
},
90112
minMaxBoundaries: {
91113
title: "Search Boundaries",
92-
explanation: "The constraints on the search space. Defines the minimum and maximum values that a gene (or particle position) can take.",
114+
explanation:
115+
"The constraints on the search space. Defines the minimum and maximum values that a gene (or particle position) can take.",
93116
},
94117
gpPrimitiveSet: {
95118
title: "Primitive Set",
96-
explanation: "The building blocks for Genetic Programs. It includes functions (operators like +, -, *, /) and terminals (constants like 1, 2, or variables like 'x'). Crossing these creates complex tree structures.",
119+
explanation:
120+
"The building blocks for Genetic Programs. It includes functions (operators like +, -, *, /) and terminals (constants like 1, 2, or variables like 'x'). Crossing these creates complex tree structures.",
97121
},
98122
treeGenerator: {
99123
title: "Tree Generator",
100-
explanation: "The algorithm used to create the initial programs. 'genFull' creates balanced trees where every branch reaches max depth, while 'genHalfAndHalf' provides a mix of short and deep trees for better diversity.",
124+
explanation:
125+
"The algorithm used to create the initial programs. 'genFull' creates balanced trees where every branch reaches max depth, while 'genHalfAndHalf' provides a mix of short and deep trees for better diversity.",
101126
},
102127
bloatLimits: {
103128
title: "Bloat Control",
104-
explanation: "Prevents programs from growing excessively large without improving fitness. By setting a height limit, we ensure the solutions remain computationally efficient and readable.",
105-
}
129+
explanation:
130+
"Prevents programs from growing excessively large without improving fitness. By setting a height limit, we ensure the solutions remain computationally efficient and readable.",
131+
},
106132
};

app/components/ClientLayout.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use client";
2+
3+
import { ThemeProvider } from "../contexts/ThemeContext";
4+
import Header from "./Header";
5+
6+
export default function ClientLayout({ children }) {
7+
return (
8+
<ThemeProvider>
9+
<Header />
10+
{children}
11+
</ThemeProvider>
12+
);
13+
}

app/components/Header.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import ThemeToggle from "./ThemeToggle";
4+
import Link from "next/link";
5+
import Image from "next/image";
6+
import { usePathname } from "next/navigation";
7+
8+
export default function Header() {
9+
const pathname = usePathname();
10+
const isHome = pathname === "/";
11+
12+
return (
13+
<header className="fixed top-0 left-0 right-0 z-[100] flex justify-between items-center px-6 py-4 pointer-events-none">
14+
<div
15+
className={`transition-all duration-300 pointer-events-auto ${isHome ? "opacity-0 -translate-x-4" : "opacity-100 translate-x-0"}`}
16+
>
17+
<Link href="/" className="flex items-center gap-2 group">
18+
<div className="bg-foreground text-background p-1.5 rounded-lg rotate-[-4deg] group-hover:rotate-0 transition-transform duration-200">
19+
<Image
20+
src="/EvOCicon.png"
21+
alt="EvOC Icon"
22+
width={24}
23+
height={24}
24+
className="invert dark:invert-0"
25+
/>
26+
</div>
27+
<span className="font-bold text-xl tracking-tight hidden sm:block">
28+
EvOC
29+
</span>
30+
</Link>
31+
</div>
32+
33+
<div className="pointer-events-auto">
34+
<ThemeToggle />
35+
</div>
36+
</header>
37+
);
38+
}

app/components/ThemeToggle.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"use client";
2+
3+
import { Moon, Sun } from "lucide-react";
4+
import { useTheme } from "../contexts/ThemeContext";
5+
6+
export default function ThemeToggle() {
7+
const { theme, toggleTheme, mounted } = useTheme();
8+
9+
if (!mounted)
10+
return (
11+
<div className="h-9 w-[68px] rounded-full bg-background border border-foreground/10 opacity-0" />
12+
);
13+
14+
return (
15+
<div className="flex items-center">
16+
<button
17+
onClick={toggleTheme}
18+
className="relative h-9 w-[68px] rounded-full bg-background border-2 border-foreground hover:bg-foreground/[0.03] cursor-pointer transition-all duration-200 overflow-hidden flex items-center group shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.1)] active:translate-y-[1px] active:shadow-none"
19+
aria-label="Toggle Theme"
20+
>
21+
{/* Sliding indicator */}
22+
<div
23+
className={`absolute top-0.5 bottom-0.5 w-[28px] rounded-full transition-all duration-300 ease-in-out ${
24+
theme === "light"
25+
? "left-0.5 bg-yellow-400 border border-black"
26+
: "left-[35.5px] bg-blue-500 border border-white/20"
27+
}`}
28+
/>
29+
30+
{/* Icons */}
31+
<div className="relative flex items-center justify-between w-full px-2 z-10 pointer-events-none">
32+
<Sun
33+
size={14}
34+
strokeWidth={2.5}
35+
className={`transition-all duration-300 ${theme === "light" ? "text-black" : "text-foreground/30"}`}
36+
/>
37+
<Moon
38+
size={14}
39+
strokeWidth={2.5}
40+
className={`transition-all duration-300 ${theme === "dark" ? "text-white" : "text-foreground/30"}`}
41+
/>
42+
</div>
43+
</button>
44+
</div>
45+
);
46+
}

app/contexts/ThemeContext.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client";
2+
3+
import React, { createContext, useContext, useEffect, useState } from "react";
4+
5+
const ThemeContext = createContext();
6+
7+
export const ThemeProvider = ({ children }) => {
8+
const [theme, setTheme] = useState("light");
9+
const [mounted, setMounted] = useState(false);
10+
11+
useEffect(() => {
12+
let savedTheme = "light";
13+
try {
14+
savedTheme = localStorage.getItem("theme") || "light";
15+
} catch (e) {
16+
console.error(e);
17+
}
18+
setTheme(savedTheme);
19+
document.documentElement.setAttribute("data-theme", savedTheme);
20+
document.body.setAttribute("data-theme", savedTheme);
21+
setMounted(true);
22+
}, []);
23+
24+
const toggleTheme = () => {
25+
const nextTheme = theme === "light" ? "dark" : "light";
26+
setTheme(nextTheme);
27+
document.documentElement.setAttribute("data-theme", nextTheme);
28+
document.body.setAttribute("data-theme", nextTheme);
29+
try {
30+
localStorage.setItem("theme", nextTheme);
31+
} catch (e) {
32+
console.error(e);
33+
}
34+
};
35+
36+
return (
37+
<ThemeContext.Provider value={{ theme, toggleTheme, mounted }}>
38+
{children}
39+
</ThemeContext.Provider>
40+
);
41+
};
42+
43+
export const useTheme = () => useContext(ThemeContext);

0 commit comments

Comments
 (0)