mirror of
https://github.com/liyunze-coding/RythonDev.git
synced 2026-09-22 15:53:57 +00:00
added theme toggle
This commit is contained in:
parent
7ce605fc30
commit
09c4c24d77
17 changed files with 259 additions and 36 deletions
|
|
@ -10,7 +10,7 @@ import Card from "../global/Card.astro";
|
||||||
<div class="w-[90%] md:w-[600px] lg:w-2/3">
|
<div class="w-[90%] md:w-[600px] lg:w-2/3">
|
||||||
<Card
|
<Card
|
||||||
shadowColor="rgb(4, 57, 57)"
|
shadowColor="rgb(4, 57, 57)"
|
||||||
className="animate-fadeIn bg-gradient-to-br from-[#101010] via-[#252525] to-[#101010]"
|
className="theme-dark-card animate-fadeIn bg-gradient-to-br from-[#101010] via-[#252525] to-[#101010]"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col items-center justify-center gap-2 py-10">
|
<div class="flex flex-col items-center justify-center gap-2 py-10">
|
||||||
<div class="mb-1 px-5">
|
<div class="mb-1 px-5">
|
||||||
|
|
@ -36,7 +36,7 @@ import Card from "../global/Card.astro";
|
||||||
<div class="text-muted-foreground text-base">
|
<div class="text-muted-foreground text-base">
|
||||||
Email me at:
|
Email me at:
|
||||||
</div>
|
</div>
|
||||||
<div class="text-white">
|
<div>
|
||||||
<a
|
<a
|
||||||
href="mailto:hello@rython.dev"
|
href="mailto:hello@rython.dev"
|
||||||
class="hover:text-accent">hello@rython.dev</a
|
class="hover:text-accent">hello@rython.dev</a
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,19 @@ export default function Experience() {
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent
|
<TabsContent
|
||||||
value="experience"
|
value="experience"
|
||||||
className="rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
className="theme-dark-card rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
||||||
>
|
>
|
||||||
{ExperienceTabContent()}
|
{ExperienceTabContent()}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent
|
<TabsContent
|
||||||
value="education"
|
value="education"
|
||||||
className="bg-secondary rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
className="theme-dark-card bg-secondary rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
||||||
>
|
>
|
||||||
{EducationTabContent()}
|
{EducationTabContent()}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent
|
<TabsContent
|
||||||
value="extracurricular"
|
value="extracurricular"
|
||||||
className="bg-secondary rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
className="theme-dark-card bg-secondary rounded-md bg-gradient-to-bl from-[#101010] via-[#272727] to-[#181818] px-5 py-5"
|
||||||
>
|
>
|
||||||
{ExtracurricularTabContent()}
|
{ExtracurricularTabContent()}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ const {
|
||||||
.card {
|
.card {
|
||||||
/* background: #1c1c1c; */
|
/* background: #1c1c1c; */
|
||||||
/* background: linear-gradient(135deg, rgb(38, 38, 38)#101010 20%, 100%); */
|
/* background: linear-gradient(135deg, rgb(38, 38, 38)#101010 20%, 100%); */
|
||||||
box-shadow: 0 10px 25px 10px var(--shadowColor);
|
box-shadow: 0 8px 20px 2px var(--shadowColor);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import { Image } from "astro:assets";
|
||||||
<div class="w-[90%] md:w-[600px] lg:w-1/2">
|
<div class="w-[90%] md:w-[600px] lg:w-1/2">
|
||||||
<Card
|
<Card
|
||||||
shadowColor="rgba(4, 57, 57, 0.5)"
|
shadowColor="rgba(4, 57, 57, 0.5)"
|
||||||
className="animate-fadeIn bg-gradient-to-br from-[#101010] to-[#202020]"
|
className="theme-dark-card animate-fadeIn bg-gradient-to-br from-[#101010] to-[#202020]"
|
||||||
>
|
>
|
||||||
<div id="intro" class="flex h-fit flex-col text-white">
|
<div id="intro" class="flex h-fit flex-col">
|
||||||
<div class="w-full px-5 pt-10 pb-2 lg:px-10">
|
<div class="w-full px-5 pt-10 pb-2 lg:px-10">
|
||||||
<div class="flex h-20 flex-row">
|
<div class="flex h-20 flex-row">
|
||||||
<div class="mr-1 flex w-20 items-center justify-center">
|
<div class="mr-1 flex w-20 items-center justify-center">
|
||||||
|
|
|
||||||
BIN
src/components/images/projects/coworking-guide-light.webp
Normal file
BIN
src/components/images/projects/coworking-guide-light.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
src/components/images/projects/portfolio-roast-bot-light.webp
Normal file
BIN
src/components/images/projects/portfolio-roast-bot-light.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
|
|
@ -4,6 +4,7 @@ import { Image } from "astro:assets";
|
||||||
interface Props {
|
interface Props {
|
||||||
position?: "left" | "right";
|
position?: "left" | "right";
|
||||||
image?: ImageMetadata;
|
image?: ImageMetadata;
|
||||||
|
image_light?: ImageMetadata;
|
||||||
alt?: string;
|
alt?: string;
|
||||||
href: string;
|
href: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -14,6 +15,7 @@ interface Props {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
image,
|
image,
|
||||||
|
image_light,
|
||||||
alt,
|
alt,
|
||||||
href,
|
href,
|
||||||
title,
|
title,
|
||||||
|
|
@ -40,18 +42,42 @@ const leftProject = position == "left";
|
||||||
<Image
|
<Image
|
||||||
src={image}
|
src={image}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
class="background-img absolute top-1/2 left-1/2 aspect-video max-w-full rounded-xl border-2 border-solid border-gray-800 opacity-40"
|
class="project-image-dark background-img absolute top-1/2 left-1/2 aspect-video max-w-full rounded-xl border-2 border-solid border-gray-800 opacity-40"
|
||||||
class:list={leftProject ? "left" : "right"}
|
class:list={[
|
||||||
|
leftProject ? "left" : "right",
|
||||||
|
{ "has-light-image": Boolean(image_light) },
|
||||||
|
]}
|
||||||
width={700}
|
width={700}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
src={image}
|
src={image}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
width={700}
|
width={700}
|
||||||
class="frontfacing-img absolute top-1/2 left-1/2 aspect-video max-w-full rounded-xl border-2 border-solid border-gray-800 opacity-100"
|
class="project-image-dark frontfacing-img absolute top-1/2 left-1/2 aspect-video max-w-full rounded-xl border-2 border-solid border-gray-800 opacity-100"
|
||||||
|
class:list={[
|
||||||
|
leftProject ? "left" : "right",
|
||||||
|
{ "has-light-image": Boolean(image_light) },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{image_light && (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
src={image_light}
|
||||||
|
alt={alt}
|
||||||
|
class="project-image-light background-img absolute top-1/2 left-1/2 hidden aspect-video max-w-full rounded-xl border-2 border-solid border-gray-300 opacity-40"
|
||||||
|
class:list={leftProject ? "left" : "right"}
|
||||||
|
width={700}
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
src={image_light}
|
||||||
|
alt={alt}
|
||||||
|
width={700}
|
||||||
|
class="project-image-light frontfacing-img absolute top-1/2 left-1/2 hidden aspect-video max-w-full rounded-xl border-2 border-solid border-gray-300 opacity-100"
|
||||||
class:list={leftProject ? "left" : "right"}
|
class:list={leftProject ? "left" : "right"}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,7 +87,7 @@ const leftProject = position == "left";
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="text-accent border-accent hover:bg-accent absolute top-1/2 left-1/2 hidden [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 bg-black px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:text-black lg:block"
|
class="visit-label text-accent border-accent hover:bg-accent absolute top-1/2 left-1/2 hidden [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 bg-black px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:text-black lg:block"
|
||||||
>{label}</a
|
>{label}</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -70,7 +96,7 @@ const leftProject = position == "left";
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="hover:text-accent rounded-4xl bg-blue-600 px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:bg-white"
|
class="visit-label-mobile hover:text-accent rounded-4xl bg-blue-600 px-5 py-3 font-bold opacity-100 transition-colors duration-300 hover:bg-white"
|
||||||
>{label}</a
|
>{label}</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -106,6 +132,44 @@ const leftProject = position == "left";
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(html[data-theme="light"]) .project-image-dark.has-light-image {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .project-image-light {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .project-image-dark {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .visit-div {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
background-color: rgb(255 255 255 / 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .visit-label {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #0f172a;
|
||||||
|
border-color: #72ffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .visit-label:hover {
|
||||||
|
background-color: #72ffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .visit-label-mobile {
|
||||||
|
background-color: #72ffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .visit-label-mobile:hover {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
.image-container img {
|
.image-container img {
|
||||||
transition: transform 0.5s ease-in-out;
|
transition: transform 0.5s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
---
|
---
|
||||||
import TaskWidget from "./TaskWidget.svelte";
|
import TaskWidget from "./TaskWidget.svelte";
|
||||||
import CoworkingGuide from "../images/projects/coworking-guide.webp";
|
import CoworkingGuide from "../images/projects/coworking-guide.webp";
|
||||||
|
import CoworkingGuideLight from "../images/projects/coworking-guide-light.webp";
|
||||||
import PortfolioRoastBot from "../images/projects/portfolio-roast-bot.webp";
|
import PortfolioRoastBot from "../images/projects/portfolio-roast-bot.webp";
|
||||||
|
import PortfolioRoastBotLight from "../images/projects/portfolio-roast-bot-light.webp";
|
||||||
import DsecNotebook from "../images/projects/dsec-notebook.webp";
|
import DsecNotebook from "../images/projects/dsec-notebook.webp";
|
||||||
|
import DsecNotebookLight from "../images/projects/dsec-notebook-light.webp";
|
||||||
import Project from "./Project.astro";
|
import Project from "./Project.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -14,6 +17,7 @@ import Project from "./Project.astro";
|
||||||
<Project
|
<Project
|
||||||
position="left"
|
position="left"
|
||||||
image={CoworkingGuide}
|
image={CoworkingGuide}
|
||||||
|
image_light={CoworkingGuideLight}
|
||||||
alt="coworking guide"
|
alt="coworking guide"
|
||||||
href="https://coworking-guide.vercel.app/"
|
href="https://coworking-guide.vercel.app/"
|
||||||
title="Coworking Guide"
|
title="Coworking Guide"
|
||||||
|
|
@ -25,6 +29,7 @@ import Project from "./Project.astro";
|
||||||
<Project
|
<Project
|
||||||
position="right"
|
position="right"
|
||||||
image={DsecNotebook}
|
image={DsecNotebook}
|
||||||
|
image_light={DsecNotebookLight}
|
||||||
alt="DSEC Notebook"
|
alt="DSEC Notebook"
|
||||||
href="https://notebook.dsec.club/"
|
href="https://notebook.dsec.club/"
|
||||||
title="DSEC Notebook"
|
title="DSEC Notebook"
|
||||||
|
|
@ -36,6 +41,7 @@ import Project from "./Project.astro";
|
||||||
<Project
|
<Project
|
||||||
position="left"
|
position="left"
|
||||||
image={PortfolioRoastBot}
|
image={PortfolioRoastBot}
|
||||||
|
image_light={PortfolioRoastBotLight}
|
||||||
alt="Portfolio Roast Bot"
|
alt="Portfolio Roast Bot"
|
||||||
href="https://codeberg.org/RythonDev/portfolio-roast-bot"
|
href="https://codeberg.org/RythonDev/portfolio-roast-bot"
|
||||||
label="View Repository"
|
label="View Repository"
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@
|
||||||
<!-- task bot -->
|
<!-- task bot -->
|
||||||
<div class="task-widget absolute flex h-full min-h-[50vh] w-full flex-col">
|
<div class="task-widget absolute flex h-full min-h-[50vh] w-full flex-col">
|
||||||
<div
|
<div
|
||||||
class="flex w-full flex-row items-center justify-between
|
class="task-widget-header flex w-full flex-row items-center justify-between
|
||||||
rounded-md
|
rounded-md
|
||||||
border-3 border-solid
|
border-3 border-solid
|
||||||
border-gray-600 bg-black
|
border-gray-600 bg-black
|
||||||
|
|
@ -161,7 +161,7 @@
|
||||||
<div class="scroll-element primary absolute w-full px-1">
|
<div class="scroll-element primary absolute w-full px-1">
|
||||||
{#each streamers as { streamer, tasks }}
|
{#each streamers as { streamer, tasks }}
|
||||||
<div
|
<div
|
||||||
class="my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
|
class="task-widget-item my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
|
||||||
text-sm overflow-ellipsis whitespace-nowrap text-white lg:text-base"
|
text-sm overflow-ellipsis whitespace-nowrap text-white lg:text-base"
|
||||||
>
|
>
|
||||||
{streamer} :
|
{streamer} :
|
||||||
|
|
@ -169,8 +169,8 @@
|
||||||
{#each tasks as task}
|
{#each tasks as task}
|
||||||
<li
|
<li
|
||||||
class={task.completed
|
class={task.completed
|
||||||
? "text-gray-400 line-through"
|
? "task-completed text-gray-400 line-through"
|
||||||
: "text-white"}
|
: "task-incomplete text-white"}
|
||||||
>
|
>
|
||||||
{task.name}
|
{task.name}
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -182,14 +182,16 @@
|
||||||
<div class="scroll-element secondary absolute w-full px-1">
|
<div class="scroll-element secondary absolute w-full px-1">
|
||||||
{#each streamers as { streamer, tasks }}
|
{#each streamers as { streamer, tasks }}
|
||||||
<div
|
<div
|
||||||
class="my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
|
class="task-widget-item my-2 w-full overflow-hidden rounded-md bg-[#404040] px-5 py-1
|
||||||
text-sm overflow-ellipsis whitespace-nowrap text-white lg:text-base"
|
text-sm overflow-ellipsis whitespace-nowrap text-white lg:text-base"
|
||||||
>
|
>
|
||||||
{streamer} :
|
{streamer} :
|
||||||
<ol class="list-inside list-decimal">
|
<ol class="list-inside list-decimal">
|
||||||
{#each tasks as task}
|
{#each tasks as task}
|
||||||
<li
|
<li
|
||||||
class={task.completed ? "line-through" : ""}
|
class={task.completed
|
||||||
|
? "task-completed line-through"
|
||||||
|
: "task-incomplete"}
|
||||||
>
|
>
|
||||||
{task.name}
|
{task.name}
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -203,6 +205,25 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(html[data-theme="light"]) .task-widget-header {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .task-widget-item {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .task-incomplete {
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .task-completed {
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
.background-widget.left {
|
.background-widget.left {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
<img
|
<img
|
||||||
src={image_src}
|
src={image_src}
|
||||||
alt={image_alt}
|
alt={image_alt}
|
||||||
class="background-img absolute
|
class="project-media background-img absolute
|
||||||
top-1/2
|
top-1/2
|
||||||
left-1/2 max-w-full rounded-xl border-2
|
left-1/2 max-w-full rounded-xl border-2
|
||||||
border-solid border-gray-800
|
border-solid border-gray-800
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
<!-- svelte-ignore a11y_media_has_caption -->
|
<!-- svelte-ignore a11y_media_has_caption -->
|
||||||
<video
|
<video
|
||||||
width="700"
|
width="700"
|
||||||
class="playable frontfacing-vid absolute
|
class="project-media playable frontfacing-vid absolute
|
||||||
top-1/2 left-1/2 max-w-full rounded-xl
|
top-1/2 left-1/2 max-w-full rounded-xl
|
||||||
border-2
|
border-2
|
||||||
border-solid border-gray-800
|
border-solid border-gray-800
|
||||||
|
|
@ -122,6 +122,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
:global(html[data-theme="light"]) .project-media {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
.background-img.left {
|
.background-img.left {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import Card from "../global/Card.astro";
|
||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
shadowColor="rgb(4, 57, 57)"
|
shadowColor="rgb(4, 57, 57)"
|
||||||
className="bg-gradient-to-br from-[#101010] to-[#202020]"
|
className="theme-dark-card bg-gradient-to-br from-[#101010] to-[#202020]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col items-start justify-start gap-2 px-5 py-5 text-left"
|
class="flex flex-col items-start justify-start gap-2 px-5 py-5 text-left"
|
||||||
|
|
@ -51,7 +51,7 @@ import Card from "../global/Card.astro";
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
shadowColor="rgb(4, 57, 57)"
|
shadowColor="rgb(4, 57, 57)"
|
||||||
className="bg-gradient-to-br from-[#202020] to-[#101010] lg:col-span-2"
|
className="theme-dark-card bg-gradient-to-br from-[#202020] to-[#101010] lg:col-span-2"
|
||||||
>
|
>
|
||||||
<div class="w-full px-5 py-5 lg:w-4/5">
|
<div class="w-full px-5 py-5 lg:w-4/5">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,31 @@ export const HoverNavigation = ({
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||||
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
||||||
const [hamburgerOpen, setHamburgerOpen] = useState(false);
|
const [hamburgerOpen, setHamburgerOpen] = useState(false);
|
||||||
|
const [theme, setTheme] = useState<"light" | "dark">("dark");
|
||||||
const hamburgerRef = useRef<HTMLDivElement>(null);
|
const hamburgerRef = useRef<HTMLDivElement>(null);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme(
|
||||||
|
document.documentElement.dataset.theme === "light"
|
||||||
|
? "light"
|
||||||
|
: "dark",
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
const nextTheme = theme === "dark" ? "light" : "dark";
|
||||||
|
document.documentElement.dataset.theme = nextTheme;
|
||||||
|
localStorage.setItem("theme", nextTheme);
|
||||||
|
document
|
||||||
|
.querySelector('meta[name="theme-color"]')
|
||||||
|
?.setAttribute(
|
||||||
|
"content",
|
||||||
|
nextTheme === "dark" ? "#101010" : "#fafafa",
|
||||||
|
);
|
||||||
|
setTheme(nextTheme);
|
||||||
|
};
|
||||||
|
|
||||||
// Track which section is in view
|
// Track which section is in view
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sectionIds = items.map((item) => {
|
const sectionIds = items.map((item) => {
|
||||||
|
|
@ -106,7 +128,7 @@ export const HoverNavigation = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="bg-secondary/50 fixed top-5 left-1/2 z-30 box-border hidden -translate-x-1/2 items-center justify-center overflow-auto rounded-[40px] border-2 border-solid border-gray-700 px-5 py-3 backdrop-blur-md md:flex md:w-11/12 lg:w-2/3 xl:w-auto">
|
<div className="theme-surface bg-secondary/50 fixed top-5 left-1/2 z-30 box-border hidden -translate-x-1/2 items-center justify-center overflow-auto rounded-[40px] border-2 border-solid border-gray-700 px-5 py-3 backdrop-blur-md md:flex md:w-11/12 lg:w-2/3 xl:w-auto">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<nav
|
<nav
|
||||||
|
|
@ -161,13 +183,15 @@ export const HoverNavigation = ({
|
||||||
))}
|
))}
|
||||||
</motion.ul>
|
</motion.ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
<ThemeToggle theme={theme} onToggle={toggleTheme} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-secondary/50 fixed top-5 left-1/2 z-50 flex w-4/5 -translate-x-1/2 justify-end rounded-full border-2 border-solid border-gray-700 pr-5 backdrop-blur-xl lg:hidden">
|
<div className="theme-surface bg-secondary/50 fixed top-5 left-1/2 z-50 flex w-4/5 -translate-x-1/2 items-center justify-end rounded-full border-2 border-solid border-gray-700 pr-5 backdrop-blur-xl lg:hidden">
|
||||||
|
<ThemeToggle theme={theme} onToggle={toggleTheme} />
|
||||||
<div
|
<div
|
||||||
ref={hamburgerRef}
|
ref={hamburgerRef}
|
||||||
className="flex rounded-full px-1 py-1 md:hidden [&>div]:rounded-full [&>div_div]:bg-white!"
|
className="flex rounded-full px-1 py-1 md:hidden [&>div]:rounded-full [&>div_div]:bg-current!"
|
||||||
>
|
>
|
||||||
<HamburgerCross
|
<HamburgerCross
|
||||||
toggle={setHamburgerOpen}
|
toggle={setHamburgerOpen}
|
||||||
|
|
@ -185,7 +209,7 @@ export const HoverNavigation = ({
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.95 }}
|
exit={{ opacity: 0, scale: 0.95 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
className="bg-secondary/50 fixed top-24 right-0 left-1/2 z-40 w-4/5 -translate-x-1/2 rounded-2xl py-2 text-white backdrop-blur-xl md:hidden"
|
className="theme-surface bg-secondary/50 fixed top-24 right-0 left-1/2 z-40 w-4/5 -translate-x-1/2 rounded-2xl py-2 backdrop-blur-xl md:hidden"
|
||||||
>
|
>
|
||||||
<nav aria-label="Mobile Navigation">
|
<nav aria-label="Mobile Navigation">
|
||||||
<ul className="flex flex-col gap-2">
|
<ul className="flex flex-col gap-2">
|
||||||
|
|
@ -220,6 +244,49 @@ export const HoverNavigation = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ThemeToggle = ({
|
||||||
|
theme,
|
||||||
|
onToggle,
|
||||||
|
}: {
|
||||||
|
theme: "light" | "dark";
|
||||||
|
onToggle: () => void;
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onToggle}
|
||||||
|
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} theme`}
|
||||||
|
title={`Switch to ${theme === "dark" ? "light" : "dark"} theme`}
|
||||||
|
className="ml-2 grid size-10 shrink-0 cursor-pointer place-items-center rounded-full transition-colors hover:bg-current/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
||||||
|
>
|
||||||
|
{theme === "dark" ? (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
className="size-5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="4" />
|
||||||
|
<path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
className="size-5"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
export const NavBarLink = ({
|
export const NavBarLink = ({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
|
|
@ -230,7 +297,7 @@ export const NavBarLink = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-20 h-fit w-full overflow-hidden rounded-2xl text-center mix-blend-difference transition-all ease-out",
|
"theme-nav-link relative z-20 h-fit w-full overflow-hidden rounded-2xl text-center mix-blend-difference transition-all ease-out",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export function Button({
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex h-full w-full items-center justify-center border border-slate-800 bg-slate-900/[0.8] text-sm text-white antialiased backdrop-blur-xl",
|
"theme-dark-card relative flex h-full w-full items-center justify-center border border-slate-800 bg-slate-900/[0.8] text-sm antialiased backdrop-blur-xl",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ function TabsList({
|
||||||
<TabsPrimitive.List
|
<TabsPrimitive.List
|
||||||
data-slot="tabs-list"
|
data-slot="tabs-list"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-primary inline-flex h-9 w-fit items-center justify-center rounded-lg bg-[#101010] p-[3px]",
|
"theme-dark-card text-primary inline-flex h-9 w-fit items-center justify-center rounded-lg bg-[#101010] p-[3px]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,20 @@ import { ClientRouter } from "astro:transitions";
|
||||||
content="Rython, student, developer, frontend, web developer, streamer, coworking"
|
content="Rython, student, developer, frontend, web developer, streamer, coworking"
|
||||||
/>
|
/>
|
||||||
<meta name="author" content="RythonDev" />
|
<meta name="author" content="RythonDev" />
|
||||||
<meta name="theme-color" content="#2B2D31" />
|
<meta name="theme-color" content="#101010" />
|
||||||
|
<script is:inline>
|
||||||
|
(() => {
|
||||||
|
const savedTheme = localStorage.getItem("theme");
|
||||||
|
const theme =
|
||||||
|
savedTheme === "light" || savedTheme === "dark"
|
||||||
|
? savedTheme
|
||||||
|
: window.matchMedia("(prefers-color-scheme: light)").matches
|
||||||
|
? "light"
|
||||||
|
: "dark";
|
||||||
|
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
{thumbnail && <meta name="twitter:image" content={thumbnail} />}
|
{thumbnail && <meta name="twitter:image" content={thumbnail} />}
|
||||||
|
|
||||||
|
|
@ -72,15 +85,23 @@ import { ClientRouter } from "astro:transitions";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: "Montserrat", sans-serif;
|
font-family: "Montserrat", sans-serif;
|
||||||
color: #fff;
|
color: var(--page-foreground);
|
||||||
background: #1d1c20;
|
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle, #303030 1px, transparent 1px), #101010;
|
radial-gradient(circle, var(--page-dot) 1px, transparent 1px),
|
||||||
|
var(--page-background);
|
||||||
background-size: 20px 20px;
|
background-size: 20px 20px;
|
||||||
|
transition:
|
||||||
|
color 0.25s ease,
|
||||||
|
background-color 0.25s ease;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import { Button } from "@/components/ui/moving-border";
|
||||||
<Button
|
<Button
|
||||||
client:visible
|
client:visible
|
||||||
borderRadius="1.75rem"
|
borderRadius="1.75rem"
|
||||||
className="bg-secondary hover:bg-accent cursor-pointer border-slate-800 px-5 text-white transition-all duration-300 hover:text-black"
|
className="bg-secondary hover:bg-accent text-primary cursor-pointer border-slate-800 px-5 transition-all duration-300 hover:text-black"
|
||||||
duration={2000}
|
duration={2000}
|
||||||
as={"a"}
|
as={"a"}
|
||||||
href="/projects"
|
href="/projects"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,46 @@
|
||||||
--animate-gradient: gradient 15s ease infinite;
|
--animate-gradient: gradient 15s ease infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--page-background: #101010;
|
||||||
|
--page-foreground: #fff;
|
||||||
|
--page-dot: #303030;
|
||||||
|
--surface: rgb(29 28 32 / 0.75);
|
||||||
|
--surface-border: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] {
|
||||||
|
--color-primary: #171717;
|
||||||
|
--color-secondary: #f5f5f5;
|
||||||
|
--color-muted-foreground: #525252;
|
||||||
|
--page-background: #fafafa;
|
||||||
|
--page-foreground: #171717;
|
||||||
|
--page-dot: #d4d4d4;
|
||||||
|
--surface: rgb(255 255 255 / 0.78);
|
||||||
|
--surface-border: #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .theme-surface {
|
||||||
|
background-color: var(--surface);
|
||||||
|
border-color: var(--surface-border);
|
||||||
|
color: var(--page-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .theme-nav-link {
|
||||||
|
color: #111827;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .card {
|
||||||
|
box-shadow: 0 6px 16px rgb(15 23 42 / 0.12) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .theme-dark-card {
|
||||||
|
background: linear-gradient(to bottom right, #ffffff, #f1f5f9, #e2e8f0) !important;
|
||||||
|
border-color: #cbd5e1 !important;
|
||||||
|
color: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes gradient {
|
@keyframes gradient {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0% 50%;
|
background-position: 0% 50%;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue