RythonDev/src/components/projects/Project.astro
2026-09-03 18:43:17 +08:00

237 lines
6 KiB
Text

---
import { Image } from "astro:assets";
interface Props {
position?: "left" | "right";
image?: ImageMetadata;
image_light?: ImageMetadata;
alt?: string;
href: string;
title: string;
description: string;
label?: string;
tags?: Array<string>;
}
const {
image,
image_light,
alt,
href,
title,
description,
label = "View Repository",
tags = [],
position = "left",
} = Astro.props;
const leftProject = position == "left";
---
<div
class="mb-10 flex h-fit w-full flex-col justify-center px-10 lg:px-20"
class:list={leftProject ? "lg:flex-row" : "lg:flex-row-reverse"}
>
<div
class="image-container relative mb-5 flex aspect-video w-full [perspective:200px] lg:mr-5 lg:mb-0 lg:ml-auto lg:w-1/2"
>
<slot />
{
image && alt && (
<>
<Image
src={image}
alt={alt}
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",
{ "has-light-image": Boolean(image_light) },
]}
width={700}
/>
<Image
src={image}
alt={alt}
width={700}
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"}
/>
</>
)}
</>
)
}
<div
class="visit-div absolute top-1/2 left-1/2 aspect-video w-[700px] max-w-full [transform:translateX(-50%)_translateY(-50%)] rounded-xl border-2 border-solid border-gray-600 bg-[rgba(0,0,0,0.5)] opacity-0"
>
<a
href={href}
target="_blank"
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
>
</div>
</div>
<div class="mb-10 flex items-center justify-center lg:hidden">
<a
href={href}
target="_blank"
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
>
</div>
<div
class="flex h-fit w-full flex-col items-center justify-center lg:h-[340px] lg:w-1/2 lg:px-10"
class:list={leftProject ? "lg:items-start" : "lg:items-end"}
>
<h2
class="mb-5 text-center text-3xl font-bold"
class:list={leftProject ? "lg:text-left" : "lg:text-right"}
>
{title}
</h2>
<p
class="max-w-lg text-center text-xl text-balance lg:max-w-none"
class:list={leftProject ? "lg:text-left" : "lg:text-right"}
>
{description}
</p>
<div
class="mt-5 flex w-full flex-row flex-wrap justify-center"
class:list={leftProject ? "lg:justify-start" : "lg:justify-end"}
>
{
tags.map((tag) => (
<div class="bg-accent my-1 mr-1 w-fit rounded-full px-3 py-1 text-black transition-colors duration-300">
{tag}
</div>
))
}
</div>
</div>
</div>
<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 {
transition: transform 0.5s ease-in-out;
}
.background-img.left {
transform: translate(-50%, -50%);
}
.background-img.right {
transform: translate(-50%, -50%);
}
.frontfacing-img.left {
transform: translate(-50%, -50%);
}
.frontfacing-img.right {
transform: translate(-50%, -50%);
}
@media (min-width: 426px) {
.background-img.left {
transform: translateX(calc(-50% + 25px))
translateY(calc(-50% + 5px)) translateZ(-50px) rotateY(5deg);
}
.background-img.right {
transform: translateX(calc(-50%)) translateY(calc(-50% + 5px))
translateZ(-50px) rotateY(-3deg);
}
.frontfacing-img.left {
transform: translateX(calc(-50% + 35px)) translateY(calc(-50%))
translateZ(-50px) rotateY(5deg);
}
.frontfacing-img.right {
transform: translateX(calc(-50% - 10px)) translateY(-50%)
translateZ(-50px) rotateY(-3deg);
}
}
@media (min-width: 1025px) {
.image-container:hover > img {
transform: rotateY(0deg) rotateX(0deg) translateZ(0px)
translateX(-50%) translateY(-50%);
}
.image-container:hover > .visit-div {
opacity: 100%;
transition: opacity 0.3s ease-in-out;
transition-delay: 0.3s;
}
.background-img.left {
transform: translateX(-50%) translateY(calc(-50% + 5px))
translateZ(-50px) rotateY(5deg);
}
.frontfacing-img.left {
transform: translateX(calc(-50% + 10px)) translateY(calc(-50%))
translateZ(-50px) rotateY(5deg);
}
}
</style>