fixed the navbar and edited the themes config and edited the home page and enhancced the canves background joints and lines with more shapes
All checks were successful
Build frontend / build (push) Successful in 37s

This commit is contained in:
mouazkh
2026-01-22 17:37:39 +03:00
parent 28d089534b
commit 4ec438b9f5
8 changed files with 516 additions and 328 deletions

View File

@ -1,4 +1,3 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom"; import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
import { AnimatePresence, LayoutGroup } from "framer-motion"; import { AnimatePresence, LayoutGroup } from "framer-motion";
@ -25,7 +24,6 @@ import DepartmentDetail7 from "./Components/Sections/DepartmentDetail7/Departmen
import DepartmentDetail8 from "./Components/Sections/DepartmentDetail8/DepartmentDetail8"; import DepartmentDetail8 from "./Components/Sections/DepartmentDetail8/DepartmentDetail8";
import DepartmentDetail9 from "./Components/Sections/DepartmentDetail9/DepartmentDetail9"; import DepartmentDetail9 from "./Components/Sections/DepartmentDetail9/DepartmentDetail9";
const MainPage = ({ theme }) => { const MainPage = ({ theme }) => {
return ( return (
<div className="min-h-screen bg-transparent"> <div className="min-h-screen bg-transparent">
@ -62,15 +60,14 @@ function RouterView({ theme, toggleTheme }) {
/> />
<Route path="/departments/:id" element={<DepartmentDetail />} /> <Route path="/departments/:id" element={<DepartmentDetail />} />
<Route path="/department-detail2" element={<DepartmentDetail2 />} /> <Route path="/department-detail2" element={<DepartmentDetail2 />} />
<Route path="/department-detail3" element={<DepartmentDetail3 />} /> <Route path="/department-detail3" element={<DepartmentDetail3 />} />
<Route path="/department-detail4" element={<DepartmentDetail4 />} /> <Route path="/department-detail4" element={<DepartmentDetail4 />} />
<Route path="/department-detail5" element={<DepartmentDetail5/>} /> <Route path="/department-detail5" element={<DepartmentDetail5 />} />
<Route path="/department-detail6" element={<DepartmentDetail6/>} /> <Route path="/department-detail6" element={<DepartmentDetail6 />} />
<Route path="/department-detail7" element={<DepartmentDetail7/>} /> <Route path="/department-detail7" element={<DepartmentDetail7 />} />
<Route path="/department-detail8" element={<DepartmentDetail8/>} />
<Route path="/department-detail9" element={<DepartmentDetail9/>} />
<Route path="/department-detail8" element={<DepartmentDetail8 />} />
<Route path="/department-detail9" element={<DepartmentDetail9 />} />
</Routes> </Routes>
</AnimatePresence> </AnimatePresence>
</LayoutGroup> </LayoutGroup>
@ -82,19 +79,21 @@ function Layout({ theme, toggleTheme }) {
const excludedExactPaths = [ const excludedExactPaths = [
"/department-detail2", "/department-detail2",
"/department-detail3" , "/department-detail3",
"/department-detail4" , "/department-detail4",
"/department-detail5" , "/department-detail5",
"/department-detail6" , "/department-detail6",
"/department-detail7", "/department-detail7",
"/department-detail8", "/department-detail8",
"/department-detail9" "/department-detail9",
]; ];
const excludedPrefixes = ["/departments/"]; const excludedPrefixes = ["/departments/"];
const isExcludedExact = excludedExactPaths.includes(location.pathname); const isExcludedExact = excludedExactPaths.includes(location.pathname);
const isExcludedPrefix = excludedPrefixes.some((p) => location.pathname.startsWith(p)); const isExcludedPrefix = excludedPrefixes.some((p) =>
location.pathname.startsWith(p),
);
const hideNavbar = isExcludedExact || isExcludedPrefix; const hideNavbar = isExcludedExact || isExcludedPrefix;
@ -114,13 +113,16 @@ function Layout({ theme, toggleTheme }) {
} }
const App = () => { const App = () => {
const [theme, setTheme] = useState("light"); const [theme, setTheme] = useState("dark");
useEffect(() => { useEffect(() => {
console.log("Current theme:", theme); console.log("Current theme:", theme);
console.log("HTML has dark class:", document.documentElement.classList.contains('dark')); console.log(
"HTML has dark class:",
document.documentElement.classList.contains("dark"),
);
const canvas = document.querySelector('.background-canvas'); const canvas = document.querySelector(".background-canvas");
if (canvas) { if (canvas) {
console.log("Canvas found:", canvas); console.log("Canvas found:", canvas);
console.log("Canvas dimensions:", canvas.width, "x", canvas.height); console.log("Canvas dimensions:", canvas.width, "x", canvas.height);
@ -131,14 +133,16 @@ const App = () => {
useEffect(() => { useEffect(() => {
const savedTheme = localStorage.getItem("theme"); const savedTheme = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)",
).matches;
if (savedTheme === "dark" || (!savedTheme && prefersDark)) { if (savedTheme === "light") {
setTheme("light");
document.documentElement.classList.remove("dark");
} else {
setTheme("dark"); setTheme("dark");
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
setTheme("light");
} }
}, []); }, []);
@ -156,9 +160,9 @@ const App = () => {
}; };
return ( return (
<BrowserRouter> <BrowserRouter>
<Layout theme={theme} toggleTheme={toggleTheme} /> <Layout theme={theme} toggleTheme={toggleTheme} />
</BrowserRouter> </BrowserRouter>
); );
}; };

View File

@ -1,121 +1,125 @@
html { html {
overflow-x: hidden !important; overflow-x: hidden !important;
height: 100% !important; height: 100% !important;
} }
body { body {
margin: 0 !important; margin: 0 !important;
padding: 0 !important; padding: 0 !important;
width: 100% !important; width: 100% !important;
min-height: 100% !important; min-height: 100% !important;
position: relative !important; position: relative !important;
overflow-x: hidden !important; overflow-x: hidden !important;
overflow-y: auto !important; overflow-y: auto !important;
} }
.background-canvas { .background-canvas {
position: fixed !important; position: fixed !important;
top: 0 !important; top: 0 !important;
left: 0 !important; left: 0 !important;
width: 100vw !important; width: 100vw !important;
height: 100vh !important; height: 100vh !important; /* Fallback */
z-index: -1 !important; height: 100dvh !important; /* Dynamic Viewport Height for mobile */
pointer-events: none !important; z-index: -1 !important;
overflow: hidden !important; pointer-events: none !important;
overflow: hidden !important;
background-color: var(
--bg-color
); /* Ensure background color is consistent even if canvas lags */
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0px !important; width: 0px !important;
height: 0px !important; height: 0px !important;
background: transparent !important; background: transparent !important;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
background: transparent !important; background: transparent !important;
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: transparent !important; background: transparent !important;
border-radius: 0px !important; border-radius: 0px !important;
} }
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: transparent !important; background: transparent !important;
} }
* { * {
scrollbar-width: none !important; scrollbar-width: none !important;
scrollbar-color: transparent transparent !important; scrollbar-color: transparent transparent !important;
} }
* { * {
-ms-overflow-style: -ms-autohiding-scrollbar !important; -ms-overflow-style: -ms-autohiding-scrollbar !important;
} }
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
main { main {
position: relative !important; position: relative !important;
z-index: 10 !important; z-index: 10 !important;
min-height: 100vh !important; min-height: 100vh !important;
width: 100% !important; width: 100% !important;
/* padding: 20px !important; */ /* padding: 20px !important; */
} }
nav.fixed { nav.fixed {
z-index: 50 !important; z-index: 50 !important;
background: transparent !important; background: transparent !important;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.background-canvas { .background-canvas {
opacity: 0.8 !important; opacity: 0.8 !important;
} }
body { body {
-webkit-overflow-scrolling: touch !important; -webkit-overflow-scrolling: touch !important;
overscroll-behavior-y: contain !important; overscroll-behavior-y: contain !important;
} }
* { * {
-webkit-tap-highlight-color: transparent !important; -webkit-tap-highlight-color: transparent !important;
} }
} }
.content-container { .content-container {
position: relative !important; position: relative !important;
z-index: 10 !important; z-index: 10 !important;
height: 100% !important; height: 100% !important;
overflow-y: auto !important; overflow-y: auto !important;
overflow-x: hidden !important; overflow-x: hidden !important;
-webkit-overflow-scrolling: touch !important; -webkit-overflow-scrolling: touch !important;
} }
.scroll-container::-webkit-scrollbar { .scroll-container::-webkit-scrollbar {
opacity: 0; opacity: 0;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
} }
.scroll-container:hover::-webkit-scrollbar { .scroll-container:hover::-webkit-scrollbar {
opacity: 1; opacity: 1;
} }
@supports (-webkit-touch-callout: none) { @supports (-webkit-touch-callout: none) {
body { body {
height: -webkit-fill-available !important; height: -webkit-fill-available !important;
} }
.background-canvas { .background-canvas {
height: -webkit-fill-available !important; height: -webkit-fill-available !important;
} }
} }
.fade-on-scroll { .fade-on-scroll {
opacity: 1; opacity: 1;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
} }
.fade-on-scroll.hidden { .fade-on-scroll.hidden {
opacity: 0; opacity: 0;
} }

View File

@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from "react";
import './BackgroundCanvas.css'; import "./BackgroundCanvas.css";
const BackgroundCanvas = ({ theme = 'light' }) => { const BackgroundCanvas = ({ theme = "light" }) => {
const canvasRef = useRef(null); const canvasRef = useRef(null);
const animationRef = useRef(null); const animationRef = useRef(null);
const jointsRef = useRef([]); const jointsRef = useRef([]);
@ -23,10 +23,14 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
constructor(position, vector) { constructor(position, vector) {
this.position = position; this.position = position;
this.vector = vector; this.vector = vector;
this.speed = 0.5; this.speed = Math.random() * 1.5 + 0.5;
this.w = 2;
this.h = 2;
this.bone_length = 150; this.bone_length = 150;
const shapes = ["circle", "triangle", "square", "pentagon"];
this.shape = shapes[Math.floor(Math.random() * shapes.length)];
this.size = Math.random() * 4 + 3;
this.angle = 0;
this.angularSpeed = (Math.random() - 0.5) * 0.1;
this.pulse = Math.random() * Math.PI * 2;
} }
} }
@ -41,7 +45,7 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
{ {
x: Math.random() * 2 - 1, x: Math.random() * 2 - 1,
y: Math.random() * 2 - 1, y: Math.random() * 2 - 1,
} },
); );
}); });
return arr; return arr;
@ -52,6 +56,9 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
joint.position.x += joint.vector.x * joint.speed; joint.position.x += joint.vector.x * joint.speed;
joint.position.y += joint.vector.y * joint.speed; joint.position.y += joint.vector.y * joint.speed;
joint.angle += joint.angularSpeed;
joint.pulse += 0.05;
if (joint.position.x < 0) joint.position.x = world.width; if (joint.position.x < 0) joint.position.x = world.width;
if (joint.position.x > world.width) joint.position.x = 0; if (joint.position.x > world.width) joint.position.x = 0;
if (joint.position.y < 0) joint.position.y = world.height; if (joint.position.y < 0) joint.position.y = world.height;
@ -63,14 +70,14 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
const len = joints.length; const len = joints.length;
let lineColor, pointColor, backgroundColor; let lineColor, pointColor, backgroundColor;
if (theme === 'dark') { if (theme === "dark") {
backgroundColor = '#446a85'; backgroundColor = "#313131";
lineColor = '#FFFFFF'; lineColor = "#F5EEE6";
pointColor = '#F5EEE6'; pointColor = "#e06923";
} else { } else {
backgroundColor = '#d3dde3'; backgroundColor = "#f1f1f1ff";
lineColor = '#131313'; lineColor = "rgba(49, 49, 49, 0.3)"; // More subtle lines
pointColor = '#041c40'; pointColor = "#041c40";
} }
ctx.fillStyle = backgroundColor; ctx.fillStyle = backgroundColor;
@ -80,7 +87,7 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
for (let j = i + 1; j < len; j += 3) { for (let j = i + 1; j < len; j += 3) {
const length = Math.hypot( const length = Math.hypot(
joints[j].position.x - joints[i].position.x, joints[j].position.x - joints[i].position.x,
joints[j].position.y - joints[i].position.y joints[j].position.y - joints[i].position.y,
); );
if (length <= joints[i].bone_length) { if (length <= joints[i].bone_length) {
@ -88,25 +95,57 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
ctx.strokeStyle = lineColor; ctx.strokeStyle = lineColor;
ctx.lineWidth = camera.zoom * (30 / length); ctx.lineWidth = camera.zoom * (30 / length);
ctx.moveTo( ctx.moveTo(
view.width / 2 + (joints[i].position.x - camera.position.x) * camera.zoom, view.width / 2 +
view.height / 2 + (joints[i].position.y - camera.position.y) * camera.zoom (joints[i].position.x - camera.position.x) * camera.zoom,
view.height / 2 +
(joints[i].position.y - camera.position.y) * camera.zoom,
); );
ctx.lineTo( ctx.lineTo(
view.width / 2 + (joints[j].position.x - camera.position.x) * camera.zoom, view.width / 2 +
view.height / 2 + (joints[j].position.y - camera.position.y) * camera.zoom (joints[j].position.x - camera.position.x) * camera.zoom,
view.height / 2 +
(joints[j].position.y - camera.position.y) * camera.zoom,
); );
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
} }
} }
ctx.fillStyle = pointColor; const currentSize =
ctx.fillRect( (joints[i].size + Math.sin(joints[i].pulse) * 1.5) * camera.zoom;
view.width / 2 + ((joints[i].position.x - camera.position.x) - joints[i].w / 2) * camera.zoom,
view.height / 2 + ((joints[i].position.y - camera.position.y) - joints[i].w / 2) * camera.zoom, ctx.save();
joints[i].w * camera.zoom, ctx.translate(
joints[i].h * camera.zoom view.width / 2 +
(joints[i].position.x - camera.position.x) * camera.zoom,
view.height / 2 +
(joints[i].position.y - camera.position.y) * camera.zoom,
); );
ctx.rotate(joints[i].angle);
ctx.fillStyle = pointColor;
ctx.beginPath();
if (joints[i].shape === "circle") {
ctx.arc(0, 0, currentSize, 0, Math.PI * 2);
} else if (joints[i].shape === "triangle") {
ctx.moveTo(0, -currentSize);
ctx.lineTo(currentSize, currentSize);
ctx.lineTo(-currentSize, currentSize);
} else if (joints[i].shape === "square") {
ctx.rect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
} else if (joints[i].shape === "pentagon") {
for (let k = 0; k < 5; k++) {
ctx.lineTo(
currentSize * Math.cos((k * 2 * Math.PI) / 5 - Math.PI / 2),
currentSize * Math.sin((k * 2 * Math.PI) / 5 - Math.PI / 2),
);
}
}
ctx.fill();
ctx.closePath();
ctx.restore();
} }
}; };
@ -127,49 +166,53 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
camera.acceleration.z *= 0.9; camera.acceleration.z *= 0.9;
}; };
const initCanvas = () => { const initCanvas = () => {
const canvas = canvasRef.current; const canvas = canvasRef.current;
if (!canvas) return; if (!canvas) return;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext("2d");
const view = { const view = {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight, height: window.innerHeight,
};
canvas.width = view.width;
canvas.height = view.height;
worldRef.current = {
width: view.width * 2,
height: view.height * 2,
};
cameraRef.current = new Camera(
{
x: worldRef.current.width / 2,
y: worldRef.current.height / 2,
},
1,
);
const isMobile = window.innerWidth < 768;
const numberOfJoints = isMobile ? 150 : 600;
jointsRef.current = generateJoints(
numberOfJoints,
worldRef.current.width,
worldRef.current.height,
);
if (isMobile) {
jointsRef.current.forEach((joint) => {
joint.bone_length = 120;
joint.speed = 0.4;
});
}
}; };
canvas.width = view.width;
canvas.height = view.height;
worldRef.current = {
width: view.width * 2,
height: view.height * 2,
};
cameraRef.current = new Camera(
{
x: worldRef.current.width / 2,
y: worldRef.current.height / 2,
},
1
);
const isMobile = window.innerWidth < 768;
const numberOfJoints = isMobile ? 80 : 300;
jointsRef.current = generateJoints(numberOfJoints, worldRef.current.width, worldRef.current.height);
if (isMobile) {
jointsRef.current.forEach(joint => {
joint.bone_length = 120;
joint.speed = 0.4;
});
}
};
const animate = () => { const animate = () => {
const canvas = canvasRef.current; const canvas = canvasRef.current;
if (!canvas) return; if (!canvas) return;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext("2d");
const view = { const view = {
width: canvas.width, width: canvas.width,
height: canvas.height, height: canvas.height,
@ -178,7 +221,13 @@ const initCanvas = () => {
moveCamera(cameraRef.current, keysRef.current); moveCamera(cameraRef.current, keysRef.current);
moveJoints(jointsRef.current, worldRef.current); moveJoints(jointsRef.current, worldRef.current);
drawJoints(ctx, jointsRef.current, cameraRef.current, view, themeRef.current); drawJoints(
ctx,
jointsRef.current,
cameraRef.current,
view,
themeRef.current,
);
animationRef.current = requestAnimationFrame(animate); animationRef.current = requestAnimationFrame(animate);
}; };
@ -196,12 +245,12 @@ const initCanvas = () => {
keysRef.current[e.keyCode] = 0; keysRef.current[e.keyCode] = 0;
}; };
window.addEventListener('keydown', handleKeyDown); window.addEventListener("keydown", handleKeyDown);
window.addEventListener('keyup', handleKeyUp); window.addEventListener("keyup", handleKeyUp);
return () => { return () => {
window.removeEventListener('keydown', handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener('keyup', handleKeyUp); window.removeEventListener("keyup", handleKeyUp);
}; };
}, []); }, []);
@ -219,8 +268,8 @@ const initCanvas = () => {
}; };
}; };
window.addEventListener('resize', handleResize); window.addEventListener("resize", handleResize);
return () => window.removeEventListener('resize', handleResize); return () => window.removeEventListener("resize", handleResize);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -235,11 +284,7 @@ const initCanvas = () => {
}, []); }, []);
return ( return (
<canvas <canvas ref={canvasRef} className="background-canvas" aria-hidden="true" />
ref={canvasRef}
className="background-canvas"
aria-hidden="true"
/>
); );
}; };

View File

@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
.theme-toggle-btn { .theme-toggle-btn {
background: transparent; background: transparent;
border: none; border: none;
color: ${props => props.theme === 'dark' ? '#FFD700' : '#FF8C00'}; color: ${(props) => (props.theme === "dark" ? "#FFD700" : "#FF8C00")};
cursor: pointer; cursor: pointer;
padding: 0.5rem; padding: 0.5rem;
border-radius: 50%; border-radius: 50%;
@ -20,7 +20,10 @@ const StyledWrapper = styled.div`
font-size: 1.5rem; font-size: 1.5rem;
&:hover { &:hover {
background: ${props => props.theme === 'dark' ? 'rgba(255, 215, 0, 0.1)' : 'rgba(255, 140, 0, 0.1)'}; background: ${(props) =>
props.theme === "dark"
? "rgba(255, 215, 0, 0.1)"
: "rgba(255, 140, 0, 0.1)"};
transform: scale(1.1); transform: scale(1.1);
} }
@ -46,10 +49,11 @@ const ThemeToggle = ({ currentTheme, toggleTheme }) => {
); );
}; };
const LanguageSwitcher = ({ i18n }) => { const LanguageSwitcher = ({ i18n, currentTheme }) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const ref = useRef(null); const ref = useRef(null);
const current = i18n.language?.startsWith("ar") ? "ar" : "en"; const current = i18n.language?.startsWith("ar") ? "ar" : "en";
const isDark = currentTheme === "dark";
useEffect(() => { useEffect(() => {
const onDocClick = (e) => { const onDocClick = (e) => {
@ -66,7 +70,14 @@ const LanguageSwitcher = ({ i18n }) => {
}; };
return ( return (
<div ref={ref} style={{ position: "relative", display: "inline-flex", alignItems: "center" }}> <div
ref={ref}
style={{
position: "relative",
display: "inline-flex",
alignItems: "center",
}}
>
<button <button
type="button" type="button"
aria-haspopup="true" aria-haspopup="true"
@ -77,7 +88,7 @@ const LanguageSwitcher = ({ i18n }) => {
borderRadius: "50%", borderRadius: "50%",
border: "none", border: "none",
background: "transparent", background: "transparent",
color: "inherit", color: isDark ? "#e6e6e6" : "#313131",
cursor: "pointer", cursor: "pointer",
display: "inline-flex", display: "inline-flex",
alignItems: "center", alignItems: "center",
@ -98,12 +109,15 @@ const LanguageSwitcher = ({ i18n }) => {
position: "absolute", position: "absolute",
top: "calc(100% + 8px)", top: "calc(100% + 8px)",
right: 0, right: 0,
background: "linear-gradient(180deg,#111827,#0b1220)", background: isDark
? "linear-gradient(180deg, #446a85, #2d4b5f)"
: "#ffffff",
borderRadius: 8, borderRadius: 8,
boxShadow: "0 8px 30px rgba(2,6,23,0.5)", boxShadow: "0 8px 30px rgba(2,6,23,0.5)",
padding: "6px", padding: "6px",
zIndex: 60, zIndex: 60,
minWidth: 96, minWidth: 96,
border: isDark ? "none" : "1px solid #e5e7eb",
}} }}
> >
<button <button
@ -117,8 +131,13 @@ const LanguageSwitcher = ({ i18n }) => {
textAlign: "left", textAlign: "left",
padding: "8px 10px", padding: "8px 10px",
borderRadius: 6, borderRadius: 6,
background: current === "en" ? "rgba(255,183,77,0.12)" : "transparent", background:
color: "inherit", current === "en"
? isDark
? "rgba(255,183,77,0.12)"
: "#f3f4f6"
: "transparent",
color: isDark ? "inherit" : "#313131",
border: "none", border: "none",
cursor: "pointer", cursor: "pointer",
transition: "all 0.2s ease", transition: "all 0.2s ease",
@ -139,8 +158,13 @@ const LanguageSwitcher = ({ i18n }) => {
textAlign: "left", textAlign: "left",
padding: "8px 10px", padding: "8px 10px",
borderRadius: 6, borderRadius: 6,
background: current === "ar" ? "rgba(255,183,77,0.12)" : "transparent", background:
color: "inherit", current === "ar"
? isDark
? "rgba(255,183,77,0.12)"
: "#f3f4f6"
: "transparent",
color: isDark ? "inherit" : "#313131",
border: "none", border: "none",
cursor: "pointer", cursor: "pointer",
transition: "all 0.2s ease", transition: "all 0.2s ease",
@ -169,11 +193,16 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
const scrollPosition = window.scrollY + 140; const scrollPosition = window.scrollY + 140;
for (const section of sections) { for (const section of sections) {
const element = document.getElementById(section) || document.querySelector(`[name="${section}"]`); const element =
document.getElementById(section) ||
document.querySelector(`[name="${section}"]`);
if (element) { if (element) {
const offsetTop = element.offsetTop; const offsetTop = element.offsetTop;
const offsetHeight = element.offsetHeight; const offsetHeight = element.offsetHeight;
if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) { if (
scrollPosition >= offsetTop &&
scrollPosition < offsetTop + offsetHeight
) {
setActiveSection(section); setActiveSection(section);
break; break;
} }
@ -224,11 +253,13 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
} }
.glass-nav { .glass-nav {
background: linear-gradient(180deg, rgba(75,85,99,0.96), rgba(55,65,81,0.9)); background: rgba(227, 232, 236, 0.9);
color: #e6e6e6; color: #313131;
border-bottom: 1px solid rgba(255,255,255,0.05); border-bottom: 1px solid rgba(4, 28, 64, 0.1);
backdrop-filter: blur(8px); backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(12px);
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
} }
.nav-link { .nav-link {
@ -260,35 +291,35 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
.nav-link:hover .nav-pill { .nav-link:hover .nav-pill {
transform: translateY(-6px) scale(1.06); transform: translateY(-6px) scale(1.06);
box-shadow: box-shadow:
0 14px 40px rgba(2,6,23,0.28), 0 14px 40px rgba(224, 105, 35, 0.28),
0 6px 20px rgba(255,122,24,0.08); 0 6px 20px rgba(224, 105, 35, 0.08);
background: linear-gradient(90deg, #ffd27a, #ff8a00); background: #e06923;
color: #050505; color: #ffffff;
} }
.nav-link.active .nav-pill, .nav-link.active .nav-pill,
.mobile-drawer .nav-pill.active { .mobile-drawer .nav-pill.active {
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18) !important; background: #e06923 !important;
color: #0b0b0b !important; color: #ffffff !important;
transform: translateY(-3px) scale(1.03); transform: translateY(-3px) scale(1.03);
box-shadow: 0 8px 26px rgba(255, 122, 24, 0.16), 0 2px 6px rgba(0,0,0,0.12); box-shadow: 0 8px 26px rgba(224, 105, 35, 0.25), 0 2px 6px rgba(0,0,0,0.12);
} }
.nav-link:active .nav-pill, .nav-link:active .nav-pill,
.mobile-drawer a:active .nav-pill { .mobile-drawer a:active .nav-pill {
transition-duration: 0s !important; transition-duration: 0s !important;
transform: translateY(-3px) scale(1.03) !important; transform: translateY(-3px) scale(1.03) !important;
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18) !important; background: #e06923 !important;
color: #0b0b0b !important; color: #ffffff !important;
box-shadow: 0 8px 26px rgba(255, 122, 24, 0.16) !important; box-shadow: 0 8px 26px rgba(224, 105, 35, 0.25) !important;
} }
.nav-link:focus-visible .nav-pill, .nav-link:focus-visible .nav-pill,
.mobile-drawer a:focus-visible .nav-pill { .mobile-drawer a:focus-visible .nav-pill {
outline: none; outline: none;
box-shadow: 0 0 0 6px rgba(255,165,0,0.14), 0 8px 30px rgba(255,122,24,0.12); box-shadow: 0 0 0 6px rgba(224, 105, 35, 0.14), 0 8px 30px rgba(224, 105, 35, 0.12);
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18); background: #e06923;
color: #0b0b0b; color: #ffffff;
transform: translateY(-3px) scale(1.02); transform: translateY(-3px) scale(1.02);
} }
@ -345,7 +376,11 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
<div className="mx-auto max-w-screen-xl"> <div className="mx-auto max-w-screen-xl">
<div className="flex items-center justify-between h-14 px-2 md:px-4"> <div className="flex items-center justify-between h-14 px-2 md:px-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div aria-hidden className="logo-placeholder" style={{ width: 36, height: 36 }} /> <div
aria-hidden
className="logo-placeholder"
style={{ width: 36, height: 36 }}
/>
<div className="hidden md:flex items-center space-x-6 rtl:space-x-reverse ml-6"> <div className="hidden md:flex items-center space-x-6 rtl:space-x-reverse ml-6">
<ul className="flex items-center gap-6"> <ul className="flex items-center gap-6">
{navItems.map((item) => { {navItems.map((item) => {
@ -361,13 +396,25 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
onSetActive={() => setActiveSection(item.key)} onSetActive={() => setActiveSection(item.key)}
onClick={() => { onClick={() => {
setActiveSection(item.key); setActiveSection(item.key);
if (item.key === "home") scroll.scrollToTop({ duration: 600 }); if (item.key === "home")
scroll.scrollToTop({ duration: 600 });
if (menuOpen) setMenuOpen(false); if (menuOpen) setMenuOpen(false);
}} }}
className={`nav-link cursor-pointer text-sm md:text-lg ${isActive ? "active" : "text-slate-200 dark:text-slate-200/90"}`} className={`nav-link cursor-pointer text-sm md:text-lg ${isActive ? "active" : ""}`}
style={{
color: isActive
? ""
: currentTheme === "dark"
? "rgba(229,231,235,0.95)"
: "#313131",
}}
aria-current={isActive ? "page" : undefined} aria-current={isActive ? "page" : undefined}
> >
<span className={`nav-pill ${isActive ? "active" : ""}`}>{item.label}</span> <span
className={`nav-pill ${isActive ? "active" : ""}`}
>
{item.label}
</span>
</Link> </Link>
</li> </li>
); );
@ -378,13 +425,22 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
<div className="flex items-center gap-3 md:gap-5"> <div className="flex items-center gap-3 md:gap-5">
<div className="hidden md:flex items-center gap-3"> <div className="hidden md:flex items-center gap-3">
<LanguageSwitcher i18n={i18n} /> <LanguageSwitcher i18n={i18n} currentTheme={currentTheme} />
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} /> <ThemeToggle
currentTheme={currentTheme}
toggleTheme={toggleTheme}
/>
</div> </div>
<div className="md:hidden w-full"> <div className="md:hidden w-full">
{!isRtl ? ( {!isRtl ? (
<div style={{ display: "flex", alignItems: "center", width: "100%" }}> <div
style={{
display: "flex",
alignItems: "center",
width: "100%",
}}
>
<div style={{ display: "flex", alignItems: "center" }}> <div style={{ display: "flex", alignItems: "center" }}>
<button <button
onClick={() => setMenuOpen((s) => !s)} onClick={() => setMenuOpen((s) => !s)}
@ -394,23 +450,56 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
style={{ marginRight: 8 }} style={{ marginRight: 8 }}
> >
<span className="sr-only">Open main menu</span> <span className="sr-only">Open main menu</span>
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden> <svg
{menuOpen ? <path d="M6 18L18 6M6 6l12 12" /> : <><path d="M3 6h18" /><path d="M3 12h18" /><path d="M3 18h18" /></>} className="h-6 w-6"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
{menuOpen ? (
<path d="M6 18L18 6M6 6l12 12" />
) : (
<>
<path d="M3 6h18" />
<path d="M3 12h18" />
<path d="M3 18h18" />
</>
)}
</svg> </svg>
</button> </button>
</div> </div>
<div style={{ flex: 1 }} /> <div style={{ flex: 1 }} />
<div style={{ display: "flex", alignItems: "center", gap: 8 }}> <div
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} /> style={{ display: "flex", alignItems: "center", gap: 8 }}
>
<ThemeToggle
currentTheme={currentTheme}
toggleTheme={toggleTheme}
/>
<LanguageSwitcher i18n={i18n} /> <LanguageSwitcher i18n={i18n} />
</div> </div>
</div> </div>
) : ( ) : (
<div style={{ display: "flex", alignItems: "center", width: "100%" }}> <div
<div style={{ display: "flex", alignItems: "center", gap: 8 }}> style={{
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} /> display: "flex",
alignItems: "center",
width: "100%",
}}
>
<div
style={{ display: "flex", alignItems: "center", gap: 8 }}
>
<ThemeToggle
currentTheme={currentTheme}
toggleTheme={toggleTheme}
/>
<LanguageSwitcher i18n={i18n} /> <LanguageSwitcher i18n={i18n} />
</div> </div>
@ -425,8 +514,25 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
> >
<span className="sr-only">Open main menu</span> <span className="sr-only">Open main menu</span>
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden> <svg
{menuOpen ? <path d="M6 18L18 6M6 6l12 12" /> : <><path d="M3 6h18" /><path d="M3 12h18" /><path d="M3 18h18" /></>} className="h-6 w-6"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
{menuOpen ? (
<path d="M6 18L18 6M6 6l12 12" />
) : (
<>
<path d="M3 6h18" />
<path d="M3 12h18" />
<path d="M3 18h18" />
</>
)}
</svg> </svg>
</button> </button>
</div> </div>
@ -455,13 +561,18 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
onSetActive={() => setActiveSection(item.key)} onSetActive={() => setActiveSection(item.key)}
onClick={() => { onClick={() => {
setActiveSection(item.key); setActiveSection(item.key);
if (item.key === "home") scroll.scrollToTop({ duration: 600 }); if (item.key === "home")
scroll.scrollToTop({ duration: 600 });
setMenuOpen(false); setMenuOpen(false);
}} }}
className={`block w-full text-right px-3 py-2 rounded-md font-semibold text-base ${isActive ? "text-amber-400" : "text-slate-200 hover:text-amber-400"}`} className={`block w-full text-right px-3 py-2 rounded-md font-semibold text-base ${isActive ? "text-amber-400" : "text-[#313131] dark:text-slate-200 hover:text-amber-400"}`}
aria-current={isActive ? "page" : undefined} aria-current={isActive ? "page" : undefined}
> >
<span className={`nav-pill ${isActive ? "active" : ""}`}>{item.label}</span> <span
className={`nav-pill ${isActive ? "active" : ""}`}
>
{item.label}
</span>
</Link> </Link>
</li> </li>
); );

View File

@ -14,11 +14,13 @@ export default function EngineeringHeroFlowbite() {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const defaultConfig = { const defaultConfig = {
main_title: "عندما تطلب الرؤية مستشارًا، ويحتاج المخطط منفذًا استراتيجيًا…\nنكون القرار بالقيادة من الرؤية حتى التسليم", main_title:
"عندما تطلب الرؤية مستشارًا، ويحتاج المخطط منفذًا استراتيجيًا…\nنكون القرار بالقيادة من الرؤية حتى التسليم",
subtitle: subtitle:
"حلول متكاملة تشمل التصميم، التنفيذ، التشغيل، والصيانة\nللمشاريع الصناعية والمدنية، وفق أحدث المعايير والتقنيات", "حلول متكاملة تشمل التصميم، التنفيذ، التشغيل، والصيانة\nللمشاريع الصناعية والمدنية، وفق أحدث المعايير والتقنيات",
primary_color: "#e67e22", primary_color: "#e67e22",
background_color: "#000000", // background_color: "transparent",
background_color: "#7c2a2aff",
text_color: "#ffffff", text_color: "#ffffff",
secondary_surface: "#95a5a6", secondary_surface: "#95a5a6",
secondary_action: "#34495e", secondary_action: "#34495e",
@ -91,10 +93,10 @@ export default function EngineeringHeroFlowbite() {
width <= 400 width <= 400
? baseSize * 0.78 ? baseSize * 0.78
: width <= 640 : width <= 640
? baseSize * 0.88 ? baseSize * 0.88
: width <= 1024 : width <= 1024
? baseSize * 0.96 ? baseSize * 0.96
: baseSize; : baseSize;
const headingMultiplier = width <= 640 ? 2.6 : 3.2; const headingMultiplier = width <= 640 ? 2.6 : 3.2;
const subtitleMultiplier = width <= 640 ? 0.95 : 1.1; const subtitleMultiplier = width <= 640 ? 0.95 : 1.1;
const mainText = (config.main_title || defaultConfig.main_title) const mainText = (config.main_title || defaultConfig.main_title)
@ -106,6 +108,7 @@ export default function EngineeringHeroFlowbite() {
const maxFont = 68; const maxFont = 68;
const minFont = Math.max(Math.round(responsiveBase * 1.6), 14); const minFont = Math.max(Math.round(responsiveBase * 1.6), 14);
const root = document.documentElement; const root = document.documentElement;
const isDark = root.classList.contains("dark");
root.style.setProperty("--base", `${responsiveBase}px`); root.style.setProperty("--base", `${responsiveBase}px`);
if (main) { if (main) {
@ -126,12 +129,14 @@ export default function EngineeringHeroFlowbite() {
const computedSize = Math.min( const computedSize = Math.min(
Math.max( Math.max(
Math.round(responsiveBase * headingMultiplier * lengthFactor), Math.round(responsiveBase * headingMultiplier * lengthFactor),
minFont minFont,
), ),
maxFont maxFont,
); );
main.style.fontSize = `${computedSize}px`; main.style.fontSize = `${computedSize}px`;
main.style.color = config.text_color || defaultConfig.text_color; main.style.color = isDark
? config.text_color || defaultConfig.text_color
: config.light_text_color || defaultConfig.light_text_color;
main.style.fontWeight = 800; main.style.fontWeight = 800;
main.style.textAlign = isArabic ? "right" : "left"; main.style.textAlign = isArabic ? "right" : "left";
main.style.whiteSpace = "normal"; main.style.whiteSpace = "normal";
@ -139,6 +144,7 @@ export default function EngineeringHeroFlowbite() {
main.style.overflowWrap = "anywhere"; main.style.overflowWrap = "anywhere";
main.style.direction = isArabic ? "rtl" : "ltr"; main.style.direction = isArabic ? "rtl" : "ltr";
main.style.display = "block"; main.style.display = "block";
main.style.textShadow = isDark ? "0 4px 10px rgba(0,0,0,0.5)" : "none";
} }
if (sub) { if (sub) {
@ -149,7 +155,9 @@ export default function EngineeringHeroFlowbite() {
sub.style.fontFamily = font; sub.style.fontFamily = font;
const subSize = Math.round(responsiveBase * subtitleMultiplier); const subSize = Math.round(responsiveBase * subtitleMultiplier);
sub.style.fontSize = `${subSize}px`; sub.style.fontSize = `${subSize}px`;
sub.style.color = config.text_color || defaultConfig.text_color; sub.style.color = isDark
? config.text_color || defaultConfig.text_color
: config.light_text_color || defaultConfig.light_text_color;
sub.style.textAlign = isArabic ? "right" : "left"; sub.style.textAlign = isArabic ? "right" : "left";
sub.style.maxWidth = "800px"; sub.style.maxWidth = "800px";
sub.style.whiteSpace = "normal"; sub.style.whiteSpace = "normal";
@ -161,19 +169,19 @@ export default function EngineeringHeroFlowbite() {
root.style.setProperty( root.style.setProperty(
"--ehb-primary", "--ehb-primary",
config.primary_color || defaultConfig.primary_color config.primary_color || defaultConfig.primary_color,
); );
root.style.setProperty( root.style.setProperty(
"--ehb-background", "--ehb-background",
config.background_color || defaultConfig.background_color config.background_color || defaultConfig.background_color,
); );
root.style.setProperty( root.style.setProperty(
"--ehb-surface", "--ehb-surface",
config.secondary_surface || defaultConfig.secondary_surface config.secondary_surface || defaultConfig.secondary_surface,
); );
root.style.setProperty( root.style.setProperty(
"--ehb-action", "--ehb-action",
config.secondary_action || defaultConfig.secondary_action config.secondary_action || defaultConfig.secondary_action,
); );
}; };
@ -230,7 +238,19 @@ export default function EngineeringHeroFlowbite() {
:root { --ehb-primary: #e67e22; --ehb-background: #000000; --ehb-surface: #95a5a6; --ehb-action: #34495e; --base: 16px } :root { --ehb-primary: #e67e22; --ehb-background: #000000; --ehb-surface: #95a5a6; --ehb-action: #34495e; --base: 16px }
.hero-section{position:relative;width:100%;height:100%;overflow:hidden} .hero-section{position:relative;width:100%;height:100%;overflow:hidden}
.hero-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(35deg, #57acd900 0%, rgb(151 162 179 / 0%) 50%, #47718b 100%);z-index:3} .hero-overlay{
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
z-index:3;
background: transparent;
transition: background 0.3s ease;
}
.dark .hero-overlay {
background: linear-gradient(35deg, transparent 0%, transparent 50%, rgba(71, 113, 139, 0.2) 100%);
}
.hero-layout{position:relative;z-index:10;height:100%;display:flex;align-items:center;justify-content:space-between;padding:clamp(12px,4vw,40px);gap:2rem;direction:ltr;flex-direction:row} .hero-layout{position:relative;z-index:10;height:100%;display:flex;align-items:center;justify-content:space-between;padding:clamp(12px,4vw,40px);gap:2rem;direction:ltr;flex-direction:row}
.hero-layout.layout-ltr{flex-direction:row-reverse} .hero-layout.layout-ltr{flex-direction:row-reverse}
.hero-left{flex:1;display:flex;align-items:center;justify-content:flex-start;padding:20px;position:relative;flex-direction:column} .hero-left{flex:1;display:flex;align-items:center;justify-content:flex-start;padding:20px;position:relative;flex-direction:column}

View File

@ -1,40 +1,47 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700;800&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700;800&display=swap");
@layer base { @layer base {
:root { :root {
overflow-y: scroll !important; overflow-y: scroll !important;
--primary: #041c40; --primary: #041c40;
--secondary: #e06923; --secondary: #e06923;
--tertiary: #313131; --tertiary: #313131;
--bg-color: #F5EEE6; --bg-color: #e3e8ec;
--text-color: #313131; --text-color: #313131;
--border-color: #d1c9be; --border-color: #d1c9be;
} }
.dark { .dark {
overflow-y: scroll !important; overflow-y: scroll !important;
--primary: #041c40; --primary: #041c40;
--secondary: #e06923; --secondary: #e06923;
--tertiary: #313131; --tertiary: #313131;
--bg-color: #313131; --bg-color: #313131;
--text-color: #F5EEE6; --text-color: #f5eee6;
--border-color: #4a4a4a; --border-color: #4a4a4a;
} }
body { body {
overflow-y: scroll !important; overflow-y: scroll !important;
margin: 0; margin: 0;
padding: 0; padding: 0;
min-height: 100vh; min-height: 100vh;
width: 100vw; width: 100vw;
overflow-x: hidden; overflow-x: hidden;
font-family: 'Cairo', system-ui, -apple-system, sans-serif; font-family:
color: var(--text-color); "Cairo",
transition: background-color 0.3s ease, color 0.3s ease; system-ui,
} -apple-system,
html { sans-serif;
overflow-y: scroll !important; color: var(--text-color);
scroll-behavior: smooth; background-color: var(--bg-color);
overflow-x: hidden; transition:
} background-color 0.3s ease,
color 0.3s ease;
}
html {
overflow-y: scroll !important;
scroll-behavior: smooth;
overflow-x: hidden;
}
} }

View File

@ -1,15 +1,15 @@
import React from 'react'; import React from "react";
import ReactDOM from 'react-dom/client'; import ReactDOM from "react-dom/client";
import App from './App'; import App from "./App";
import './index.css'; import "./index.css";
import './App.css'; import "./App.css";
document.documentElement.style.setProperty('--bg-color', '#F5EEE6'); document.documentElement.style.setProperty("--bg-color", "#F5EEE6");
document.documentElement.style.setProperty('--text-color', '#131313'); document.documentElement.style.setProperty("--text-color", "#131313");
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById("root"));
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode> </React.StrictMode>,
); );

View File

@ -1,25 +1,22 @@
module.exports = { module.exports = {
darkMode: 'class', darkMode: "class",
content: [ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
"./index.html", theme: {
"./src/**/*.{js,ts,jsx,tsx}", extend: {
], colors: {
theme: { primary: "#041c40",
extend: { secondary: "#e06923",
colors: { "dark-bg": "#313131",
primary: '#041c40', "light-bg": "#E3E8EC",
secondary: '#e06923', },
'dark-bg': '#313131', backdropBlur: {
'light-bg': '#F5EEE6', xs: "2px",
}, sm: "4px",
backdropBlur: { md: "8px",
'xs': '2px', lg: "12px",
'sm': '4px', xl: "20px",
'md': '8px', },
'lg': '12px',
'xl': '20px',
},
},
}, },
plugins: [], },
} plugins: [],
};