@@ -27,4 +56,4 @@ const Section = ({ children, id, className = '' }) => {
);
};
-export default Section;
\ No newline at end of file
+export default Section;
diff --git a/src/components/themetoggle/index.js b/src/components/themetoggle/index.js
index 53718c2..d2ca8b1 100644
--- a/src/components/themetoggle/index.js
+++ b/src/components/themetoggle/index.js
@@ -1,18 +1,29 @@
import React, { useEffect, useState } from "react";
import { RiSunLine, RiMoonClearLine } from "react-icons/ri";
+import "./style.css";
const Themetoggle = () => {
const [theme, settheme] = useState(localStorage.getItem("theme") || "dark");
+ const [isHovering, setIsHovering] = useState(false);
+
const themetoggle = () => {
settheme(theme === "dark" ? "light" : "dark");
};
+
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
+
return (
-
- {theme === "dark" ?
:
}
+
setIsHovering(true)}
+ onMouseLeave={() => setIsHovering(false)}
+ >
+
+ {theme === "dark" ? : }
+
);
};
diff --git a/src/components/themetoggle/style.css b/src/components/themetoggle/style.css
index 54da820..16d9836 100644
--- a/src/components/themetoggle/style.css
+++ b/src/components/themetoggle/style.css
@@ -20,3 +20,66 @@
.theme_toggler:hover svg {
color: var(--text-color-3);
}
+
+.theme__button {
+ color: var(--text-color);
+ padding: 10px;
+ background: transparent;
+ border: none;
+ position: relative;
+ overflow: visible;
+ cursor: pointer;
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ z-index: 1001;
+}
+
+.theme__button:hover {
+ color: var(--text-color-3);
+ transform: scale(1.05);
+}
+
+.theme-icon-wrapper {
+ position: relative;
+ width: 2em;
+ height: 2em;
+ transition: transform 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
+}
+
+.theme-icon-wrapper svg {
+ width: 2em;
+ height: 2em;
+ fill: var(--text-color-2);
+ color: var(--text-color-2);
+ transition: all 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
+ filter: drop-shadow(0 0 0 transparent);
+}
+
+.theme-icon-wrapper.hover svg {
+ transform: rotate(90deg) scale(1.1);
+ fill: var(--text-color-3);
+ color: var(--text-color-3);
+ filter: drop-shadow(0 0 5px var(--text-color-3));
+}
+
+/* Create a glowing effect behind the icon */
+.theme__button::before {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, var(--text-color-3) 0%, transparent 70%);
+ border-radius: 50%;
+ opacity: 0;
+ transform: translate(-50%, -50%);
+ transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ z-index: -1;
+ pointer-events: none;
+}
+
+.theme__button:hover::before {
+ width: 3em;
+ height: 3em;
+ opacity: 0.15;
+}
diff --git a/src/content_option.js b/src/content_option.js
index 1b82090..2d330a7 100644
--- a/src/content_option.js
+++ b/src/content_option.js
@@ -126,6 +126,11 @@ const services = [{
];
const dataportfolio = [{
+ img: "/images/project7.jpg",
+ description: "AI-Powered Photo Editor, A modern SPA using Next.js, TypeScript, and Google Gemini 2.0 for real-time AI image transformations with an intuitive interface",
+ link: "https://ai-image-editor-three.vercel.app/",
+ },
+ {
img: "/images/project6.jpg",
description: "AI-Powered Real Estate Investment Analysis. Make data-driven property investment decisions with comprehensive analytics, risk assessment, and market insights powered by artificial intelligence.",
link: "https://estate-ai-eight.vercel.app/landing",
diff --git a/src/header/index.js b/src/header/index.js
index 2620afe..881906b 100644
--- a/src/header/index.js
+++ b/src/header/index.js
@@ -1,15 +1,24 @@
-import React, { useState } from "react";
+import React, { useState, useRef, useEffect } from "react";
import "./style.css";
import { RiMenuLine, RiCloseLine } from "react-icons/ri";
+import { BsGrid3X3Gap } from "react-icons/bs";
import { logotext } from "../content_option";
import Themetoggle from "../components/themetoggle";
const Headermain = () => {
const [isActive, setActive] = useState("false");
+ const [isAnimating, setIsAnimating] = useState(false);
+ const [isHovering, setIsHovering] = useState(false);
const handleToggle = () => {
+ setIsAnimating(true);
setActive(!isActive);
document.body.classList.toggle("ovhidden");
+
+ // Reset animation state after animation completes
+ setTimeout(() => {
+ setIsAnimating(false);
+ }, 600);
};
const handleNavClick = (e, targetId) => {
@@ -44,8 +53,23 @@ const Headermain = () => {
-
diff --git a/src/header/style.css b/src/header/style.css
index 8ae84e4..ac75bc5 100644
--- a/src/header/style.css
+++ b/src/header/style.css
@@ -9,6 +9,11 @@
padding: 10px;
background: transparent;
border: none;
+ position: relative;
+ overflow: visible;
+ cursor: pointer;
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ z-index: 1001;
}
.menu__button:focus,
@@ -16,6 +21,7 @@
color: var(--text-color-3);
box-shadow: unset;
background: transparent;
+ transform: scale(1.05);
}
.menu__button svg {
@@ -23,12 +29,158 @@
height: 2em;
fill: var(--text-color-2);
color: var(--text-color-2);
- transition: color 0.3s ease;
+ transition: all 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
+ filter: drop-shadow(0 0 0 transparent);
}
-.menu__button:hover svg {
+/* Default hover for svg elements (for the close button) */
+.menu__button:hover > svg {
color: var(--text-color-3);
fill: var(--text-color-3);
+ transform: rotate(90deg) scale(1.1);
+ filter: drop-shadow(0 0 5px var(--text-color-3));
+}
+
+/* Icon container for transition effect */
+.icon-container {
+ position: relative;
+ width: 2em;
+ height: 2em;
+}
+
+.icon-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ transition: transform 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
+}
+
+.menu-icon,
+.grid-icon {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ transition: opacity 0.3s ease,
+ transform 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
+}
+
+.menu-icon {
+ opacity: 1;
+ transform: scale(1);
+}
+
+.grid-icon {
+ opacity: 0;
+ transform: scale(0.8);
+}
+
+/* Hover state for icon transition */
+.icon-wrapper.hover .menu-icon {
+ opacity: 0;
+ transform: scale(0.8) rotate(90deg);
+}
+
+.icon-wrapper.hover .grid-icon {
+ opacity: 1;
+ transform: scale(1) rotate(0deg);
+ filter: drop-shadow(0 0 5px var(--text-color-3));
+}
+
+/* Create a glowing effect behind the icon */
+.menu__button::before {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 0;
+ height: 0;
+ background: radial-gradient(circle, var(--text-color-3) 0%, transparent 70%);
+ border-radius: 50%;
+ opacity: 0;
+ transform: translate(-50%, -50%);
+ transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ z-index: -1;
+ pointer-events: none;
+}
+
+.menu__button:hover::before {
+ width: 3em;
+ height: 3em;
+ opacity: 0.15;
+}
+
+/* Animation when burger is clicked */
+.menu__button.animating svg,
+.menu__button.animating .icon-wrapper {
+ animation: pulse 0.6s cubic-bezier(0.68, -0.6, 0.32, 1.6) forwards;
+}
+
+@keyframes pulse {
+ 0% {
+ transform: scale(1);
+ filter: blur(0);
+ }
+ 50% {
+ transform: scale(1.2);
+ filter: blur(2px);
+ }
+ 100% {
+ transform: scale(1);
+ filter: blur(0);
+ }
+}
+
+/* Different effects for open and closed states */
+.menu__button svg[data-state="closed"] {
+ transform-origin: center;
+}
+
+.menu__button svg[data-state="open"] {
+ transform-origin: center;
+}
+
+/* Hover effects for closed state (hamburger) */
+.menu__button:hover svg[data-state="closed"] {
+ transform: rotate(90deg) scale(1.1);
+ filter: drop-shadow(0 0 5px var(--text-color-3));
+}
+
+/* Hover effects for open state (X) */
+.menu__button:hover svg[data-state="open"] {
+ transform: rotate(-90deg) scale(1.1);
+ filter: drop-shadow(0 0 5px var(--text-color-3));
+}
+
+/* After-click effect */
+.menu__button.animating::after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background: transparent;
+ border: 2px solid var(--text-color-3);
+ transform: translate(-50%, -50%) scale(0);
+ opacity: 1;
+ animation: ripple 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
+ pointer-events: none;
+}
+
+@keyframes ripple {
+ 0% {
+ transform: translate(-50%, -50%) scale(0);
+ opacity: 1;
+ border-width: 2px;
+ }
+ 100% {
+ transform: translate(-50%, -50%) scale(2);
+ opacity: 0;
+ border-width: 0;
+ }
}
.nav_ac {
@@ -262,3 +414,7 @@
margin-right: 10px;
text-decoration: none;
}
+
+.d-flex.align-items-center {
+ gap: 5px;
+}
diff --git a/src/pages/portfolio/index.js b/src/pages/portfolio/index.js
index 5102c72..41b0366 100644
--- a/src/pages/portfolio/index.js
+++ b/src/pages/portfolio/index.js
@@ -6,6 +6,9 @@ import { dataportfolio, meta } from "../../content_option";
import { FaExternalLinkAlt } from "react-icons/fa";
export const Portfolio = () => {
+ // Check if the device is mobile
+ const isMobile = window.innerWidth <= 768;
+
return (
@@ -22,17 +25,26 @@ export const Portfolio = () => {
{dataportfolio.map((data, i) => {
- const title = data.description.split(",")[0];
+ // Extract title and description without modifying for desktop
+ const fullTitle = data.description.split(",")[0];
const description = data.description.split(",").slice(1).join(",").trim();
+ // Create shortened version only for mobile display
+ const words = fullTitle.split(" ");
+ const shortTitle = words.length > 3
+ ? words.slice(0, 3).join(" ") + "..."
+ : fullTitle;
+
return (
-

+
diff --git a/src/pages/portfolio/style.css b/src/pages/portfolio/style.css
index d8ca605..8f8bf38 100644
--- a/src/pages/portfolio/style.css
+++ b/src/pages/portfolio/style.css
@@ -92,6 +92,7 @@
-moz-osx-font-smoothing: grayscale;
}
+/* Desktop styles for links */
.project_item .content a {
color: var(--text-color);
text-decoration: none;
@@ -109,6 +110,11 @@
border: 1px solid var(--card-border);
}
+/* Hide mobile text by default on desktop */
+.mobile-text {
+ display: none;
+}
+
.project_item .content a:hover {
background: var(--secondary-color);
color: var(--primary-color);
@@ -116,7 +122,7 @@
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
-/* Mobile Styles */
+/* Mobile Styles - Only apply below 768px */
@media (max-width: 768px) {
.project_items_ho {
gap: 1.5rem;
@@ -134,52 +140,42 @@
.project_item .content {
opacity: 1;
padding: 1.25rem;
- background: linear-gradient(
- to top,
- rgba(var(--primary-rgb), 0.95) 0%,
- rgba(var(--primary-rgb), 0.7) 50%,
- rgba(var(--primary-rgb), 0.3) 100%
- );
+ background: transparent;
justify-content: flex-end;
gap: 0.75rem;
- backdrop-filter: blur(10px);
- -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
border-top: 1px solid var(--card-border);
}
.project_item .content h3 {
- font-size: 1.1rem;
- margin-bottom: 0.5rem;
- padding-bottom: 0.25rem;
- width: 100%;
- max-width: 100%;
- overflow-wrap: break-word;
- word-wrap: break-word;
- -webkit-hyphens: auto;
- hyphens: auto;
+ display: none; /* Hide title on mobile */
}
.project_item .content p {
- font-size: 0.9rem;
- margin-bottom: 1rem;
- line-height: 1.4;
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
+ display: none; /* Hide description text on mobile */
+ }
+
+ /* Show mobile text and hide desktop text on mobile */
+ .mobile-text {
+ display: inline;
+ white-space: nowrap;
overflow: hidden;
- width: 100%;
- max-width: 100%;
- overflow-wrap: break-word;
- word-wrap: break-word;
- -webkit-hyphens: auto;
- hyphens: auto;
+ text-overflow: ellipsis;
+ max-width: calc(100% - 20px); /* Allow space for the icon */
+ }
+
+ .desktop-text {
+ display: none;
}
.project_item .content a {
padding: 0.75rem 1.25rem;
font-size: 0.9rem;
border-radius: 6px;
- background: var(--primary-color);
+ background: rgba(var(--primary-rgb), 0.7);
+ backdrop-filter: blur(5px);
+ -webkit-backdrop-filter: blur(5px);
width: 100%;
text-align: center;
justify-content: center;
@@ -189,6 +185,10 @@
appearance: none;
position: relative;
z-index: 1;
+ margin-top: 0; /* Remove margin since title is hidden */
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+ height: 45px; /* Fix height to prevent expansion */
+ overflow: hidden;
}
.project_item:active {