diff --git a/package-lock.json b/package-lock.json index dd94169..17b3bda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.0.2", + "styled-components": "^6.1.13", "vite-plugin-svgr": "^4.3.0" }, "devDependencies": { @@ -287,6 +288,24 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.24.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", @@ -1413,6 +1432,11 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -1751,6 +1775,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001690", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", @@ -1870,11 +1902,28 @@ "node": ">= 8" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-view-buffer": { "version": "1.0.1", @@ -3678,6 +3727,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3974,6 +4028,11 @@ "node": ">= 0.4" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4188,6 +4247,70 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/styled-components": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", + "integrity": "sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index f0e56b0..5caff60 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.0.2", + "styled-components": "^6.1.13", "vite-plugin-svgr": "^4.3.0" }, "devDependencies": { diff --git a/src/assets/icons/colorModeToggle/moon.svg b/src/assets/icons/colorModeToggle/moon.svg new file mode 100644 index 0000000..a84f32e --- /dev/null +++ b/src/assets/icons/colorModeToggle/moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/colorModeToggle/sun.svg b/src/assets/icons/colorModeToggle/sun.svg new file mode 100644 index 0000000..245e503 --- /dev/null +++ b/src/assets/icons/colorModeToggle/sun.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Switch.jsx b/src/components/Switch.jsx index 86d76f4..7629a88 100644 --- a/src/components/Switch.jsx +++ b/src/components/Switch.jsx @@ -1,17 +1,110 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import "../css/Switch.css"; -import { useState } from "react"; import "animate.css"; +import styled from "styled-components"; +import SunIcon from "../assets/icons/colorModeToggle/sun.svg?react"; +import MoonIcon from "../assets/icons/colorModeToggle/moon.svg?react"; + +const SwitchBtn = styled.button` + background-color: ${(props) => + props.colormode === "dark" ? "#000767" : "#009d28"}; + border: 1px solid + ${(props) => (props.colormode === "dark" ? "#f6f1d5" : "#fff145")}; + border-radius: 99px; + width: 50px; + height: 28px; + cursor: pointer; + transition: background-color 0.3s ease, border-color 0.3s ease; + box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.748); + position: relative; +`; + +const SwitchContainer = styled.div` + transition: background-color 1s ease; + grid-area: 8 / 4 / 10 / 5; + background-color: ${(props) => + props.colormode === "dark" ? "#131862" : "#69d0ff"}; + display: flex; + align-items: flex-end; + justify-content: center; + border: 1px solid + ${(props) => (props.colormode === "dark" ? "#1e2be0" : "#9bdfff")}; + position: relative; +`; + +const StyledSunIcon = styled(SunIcon)` + fill: #fff145; +`; + +const StyledMoonIcon = styled(MoonIcon)` + fill: #f6f1d5; +`; + +const PlanetsContainer = styled.div` + transition: transform 0.5s ease-in-out; + transform: ${(props) => `rotate(${props.rotation}deg)`}; +`; const Switch = ({ initialAnimation, onMouseEnter, onAnimationEnd }) => { const [animation, setAnimation] = useState(initialAnimation); + const [colormode, setcolormode] = useState(() => { + // Retrieve initial color mode from localStorage or default to "dark" + return localStorage.getItem("colormode") || "dark"; + }); + const [rotation, setRotation] = useState(0); + + useEffect(() => { + // Save the current color mode to localStorage whenever it changes + localStorage.setItem("colormode", colormode); + }, [colormode]); + + const toggleColorMode = () => { + setcolormode((prevMode) => (prevMode === "dark" ? "light" : "dark")); + setRotation((prevRotation) => prevRotation + 90); + }; + return ( -
onAnimationEnd(setAnimation)} > - switch -
+
+ + + + + + +
+ +
+
+ ); }; diff --git a/src/css/ProjectCard.css b/src/css/ProjectCard.css index d8ff639..7a6bd6d 100644 --- a/src/css/ProjectCard.css +++ b/src/css/ProjectCard.css @@ -10,8 +10,24 @@ justify-content: center; } +.imgs img:first-child { + border-radius: 8px 0 0 8px; +} + +.imgs img:last-child { + border-radius: 0 8px 8px 0; +} + +.imgs img { + height: 55%; + object-fit: contain; +} + .img img { width: 100%; + border-radius: 8px; + height: 55%; + object-fit: contain; } .img img, diff --git a/src/css/Switch.css b/src/css/Switch.css index 91f357a..4a13e3c 100644 --- a/src/css/Switch.css +++ b/src/css/Switch.css @@ -1,38 +1,70 @@ -.switch { - grid-area: 8 / 4 / 10 / 5; -} - -.switch-container { - display: inline-block; - position: relative; -} - -.switch-checkbox { - display: none; -} - -.switch-label { - display: block; - width: 50px; - height: 24px; - border-radius: 34px; - position: relative; - cursor: pointer; - transition: background-color 0.2s; -} - -.switch-label .switch-button { - display: block; - width: 20px; - height: 20px; - background: #fff; - border-radius: 50%; +.wrapper { + overflow: hidden; + height: 100%; + width: 100%; position: absolute; - top: 2px; - left: 2px; - transition: transform 0.2s; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); } -.switch-checkbox:checked + .switch-label .switch-button { - transform: translateX(26px); +.toggleIcon { + position: absolute; + width: 2rem; + height: auto; +} + +.colorToggleThumb { + height: 20px; + width: 20px; + background-color: #fff; + border-radius: 99px; + transform: translateX(0); + transition: left 0.3s ease; + position: absolute; + left: 3px; + top: 50%; + transform: translateY(-50%); +} + +.colorToggleThumb.dark { + left: calc(50px - 25px); +} + +.planets { + position: absolute; + /* border: 1px solid white; */ + width: 300px; + height: 300px; + top: 0; + left: -80%; +} + +/* Top corner */ +.moon1 { + top: 10px; + left: 50%; + transform: translateX(-50%); +} + +/* Right corner */ +.sun2 { + top: 50%; + right: 10px; + transform: translateY(-50%); + fill: aqua; +} + +/* Bottom corner */ +.moon2 { + bottom: 10px; + left: 50%; + transform: translateX(-50%); +} + +/* Left corner */ +.sun1 { + top: 50%; + left: 10px; + transform: translateY(-50%); }