update ui
This commit is contained in:
1 parent
ef3098bb48
commit
f061457d7f
7 files changed
+193
-55
No files matched your search
@@ -27,6 +27,7 @@ export default function RootLayout({
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased px-4`}
|
||||
>
|
||||
<div className="aurora" />
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+19
-11
@@ -10,34 +10,42 @@ const IndexPage = async () => {
|
||||
const home = await getHome();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 justify-center items-center h-screen">
|
||||
<Image className="rounded-full max-w-[200px]" src={ProfileImage} alt="Liam Pietralla" />
|
||||
<h1 className="text-5xl font-bold">Liam Pietralla</h1>
|
||||
<div className="flex flex-col md:flex-row gap-0 md:gap-[7px]">
|
||||
<div className="flex flex-col gap-5 justify-center items-center h-screen">
|
||||
<div className="glow-ring animate-fade-in" style={{ animationDelay: "0ms" }}>
|
||||
<Image className="rounded-full max-w-[200px]" src={ProfileImage} alt="Liam Pietralla" />
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold animate-fade-in-up" style={{ animationDelay: "120ms" }}>
|
||||
Liam Pietralla
|
||||
</h1>
|
||||
<div className="flex flex-col md:flex-row gap-0 md:gap-[7px] animate-fade-in-up" style={{ animationDelay: "240ms" }}>
|
||||
<h2 className="text-xl text-center">Enthusiastic Software Developer</h2>
|
||||
<h2 className="text-xl text-center mt-[-5px] md:mt-0">& DevOps Engineer</h2>
|
||||
</div>
|
||||
<Rule className="w-[300px]" />
|
||||
<div className="flex flex-row gap-4">
|
||||
<Rule className="w-[300px] animate-fade-in" style={{ animationDelay: "320ms" }} />
|
||||
<div className="flex flex-row gap-4 animate-fade-in-up" style={{ animationDelay: "400ms" }}>
|
||||
{home.mainLinks.map(link => (
|
||||
<IndexLink key={link.id} {...link} />
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row gap-4">
|
||||
<div className="flex flex-row gap-4 animate-fade-in-up relative z-20" style={{ animationDelay: "480ms" }}>
|
||||
{home.popoverLinks.map(link => (
|
||||
<IndexLink key={link.id} {...link} isPopover={true} />
|
||||
))}
|
||||
</div>
|
||||
<Rule className="w-[300px]" />
|
||||
<a href="mailto:me@liampietralla.com" className="group leading-relaxed">
|
||||
<Rule className="w-[300px] animate-fade-in" style={{ animationDelay: "560ms" }} />
|
||||
<a
|
||||
href="mailto:me@liampietralla.com"
|
||||
className="group leading-relaxed animate-fade-in-up"
|
||||
style={{ animationDelay: "640ms" }}
|
||||
>
|
||||
<span className="flex flex-row gap-2">
|
||||
<Mail />
|
||||
me@liampietralla.com
|
||||
</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-accent"></span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default IndexPage;
|
||||
export default IndexPage;
|
||||
@@ -1,9 +1,7 @@
|
||||
import ProjectCard from "@/components/project-card";
|
||||
import Rule from "@/components/horizontal-rule";
|
||||
import { getProjects } from "@/services/projects-service";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Fragment } from "react";
|
||||
|
||||
import ProfileImage from "../../../../public/images/liam_pietralla.jpg";
|
||||
|
||||
@@ -11,25 +9,26 @@ const ProjectsPage = async () => {
|
||||
const projects = await getProjects();
|
||||
return (
|
||||
<div className="flex flex-col gap-4 justify-center items-center my-15">
|
||||
<div className="flex flex-row items-center gap-2 my-2">
|
||||
<div className="flex flex-row items-center gap-2 my-2 animate-fade-in-up">
|
||||
<Image src={ProfileImage} alt="Liam Pietralla" className="rounded-full max-w-[50px]" />
|
||||
<Link href="/" className="group leading-relaxed font-semi-bold">
|
||||
Liam Pietralla
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-accent"></span>
|
||||
</Link>
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold">Projects</h1>
|
||||
<h2>A collection of interesting projects that I am working on currently or have worked on in the past.</h2>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h1 className="text-5xl font-bold animate-fade-in-up" style={{ animationDelay: "120ms" }}>Projects</h1>
|
||||
<h2 className="animate-fade-in-up text-center max-w-2xl" style={{ animationDelay: "240ms" }}>
|
||||
A collection of interesting projects that I am working on currently or have worked on in the past.
|
||||
</h2>
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
{projects.docs.map((project, index) => (
|
||||
<Fragment key={index}>
|
||||
<div key={index} className="animate-fade-in-up" style={{ animationDelay: `${320 + index * 120}ms` }}>
|
||||
<ProjectCard {...project} />
|
||||
{index < projects.docs.length - 1 && <Rule />}
|
||||
</Fragment>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectsPage;
|
||||
export default ProjectsPage;
|
||||
+119
-2
@@ -3,11 +3,13 @@
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
--accent: #7c9cff;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-accent: var(--accent);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
@@ -15,5 +17,120 @@
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.aurora {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.aurora::before,
|
||||
.aurora::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 60vw;
|
||||
height: 60vw;
|
||||
border-radius: 9999px;
|
||||
filter: blur(120px);
|
||||
opacity: 0.22;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.aurora::before {
|
||||
top: -15%;
|
||||
left: -10%;
|
||||
background: radial-gradient(circle at center, var(--accent), transparent 70%);
|
||||
animation: drift-1 18s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.aurora::after {
|
||||
bottom: -20%;
|
||||
right: -10%;
|
||||
background: radial-gradient(circle at center, #b07cff, transparent 70%);
|
||||
animation: drift-2 22s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes drift-1 {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
100% { transform: translate(15vw, 10vh) scale(1.15); }
|
||||
}
|
||||
|
||||
@keyframes drift-2 {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
100% { transform: translate(-12vw, -8vh) scale(1.2); }
|
||||
}
|
||||
|
||||
@keyframes fade-in-up {
|
||||
from { opacity: 0; transform: translateY(16px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
opacity: 0;
|
||||
animation: fade-in-up 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
opacity: 0;
|
||||
animation: fade-in 1.2s ease forwards;
|
||||
}
|
||||
|
||||
.glow-ring {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.glow-ring::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -6px;
|
||||
border-radius: 9999px;
|
||||
background: conic-gradient(from 0deg, var(--accent), #b07cff, var(--accent));
|
||||
filter: blur(8px);
|
||||
opacity: 0.55;
|
||||
z-index: -1;
|
||||
animation: spin-slow 8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin-slow {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
transition: background 0.3s ease, border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.glass-hover:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(124, 156, 255, 0.4);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 30px -10px rgba(124, 156, 255, 0.35);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
.aurora::before,
|
||||
.aurora::after,
|
||||
.glow-ring::before {
|
||||
animation: none !important;
|
||||
}
|
||||
.animate-fade-in-up,
|
||||
.animate-fade-in {
|
||||
opacity: 1;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
@@ -15,42 +15,53 @@ const HomePageLink = ({ title, icon, url, isPopover }: HomePageLinkProps) => {
|
||||
const IconComponent = lucidIconMap[icon as IconName];
|
||||
|
||||
if (isPopover) {
|
||||
const LinkContent = (
|
||||
<span className="flex items-center space-x-2 glass glass-hover rounded-lg px-3 py-2 transition-all duration-300">
|
||||
{icon && <IconComponent className="size-4" />}
|
||||
</span>
|
||||
);
|
||||
|
||||
const Popover = (
|
||||
<div className="absolute left-1/2 -translate-x-1/2 top-full mt-2 z-10 hidden p-2 px-4 text-sm text-black bg-white rounded-md shadow-lg group-hover:block text-center whitespace-nowrap">
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (isRelative) {
|
||||
return (
|
||||
<div className="relative group">
|
||||
<Link href={url} className="flex items-center space-x-2">
|
||||
{icon && <IconComponent />}
|
||||
</Link>
|
||||
<div className="absolute left-1/2 -translate-x-1/2 z-10 hidden p-2 px-4 text-sm text-black bg-white rounded-md group-hover:block text-center">
|
||||
{title}
|
||||
</div>
|
||||
<Link href={url}>{LinkContent}</Link>
|
||||
{Popover}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className="relative group">
|
||||
<a href={url} className="flex items-center space-x-2">
|
||||
{icon && <IconComponent />}
|
||||
</a>
|
||||
<div className="absolute left-1/2 -translate-x-1/2 z-10 hidden p-2 px-4 text-sm text-black bg-white rounded-md group-hover:block text-center">
|
||||
{title}
|
||||
</div>
|
||||
<a href={url}>{LinkContent}</a>
|
||||
{Popover}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const Content = (
|
||||
<>
|
||||
<span className="flex flex-row gap-2 group-hover:text-accent transition-colors duration-300">
|
||||
{icon && <IconComponent className="size-4" />} {title}
|
||||
</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-accent"></span>
|
||||
</>
|
||||
);
|
||||
|
||||
if (isRelative) {
|
||||
return (
|
||||
<Link href={url} className="group leading-relaxed">
|
||||
<span className="flex flex-row gap-2">{icon && <IconComponent />} {title}</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
{Content}
|
||||
</Link>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<a href={url} className="group leading-relaxed">
|
||||
<span className="flex flex-row gap-2">{icon && <IconComponent />} {title}</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
{Content}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -58,4 +69,4 @@ const HomePageLink = ({ title, icon, url, isPopover }: HomePageLinkProps) => {
|
||||
}
|
||||
|
||||
|
||||
export default HomePageLink;
|
||||
export default HomePageLink;
|
||||
@@ -1,13 +1,15 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
interface RuleProps {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const HorizontalRule = ({ className }: RuleProps) => {
|
||||
const HorizontalRule = ({ className, style }: RuleProps) => {
|
||||
return (
|
||||
<div className={cn("border-t-1 border-accent", className)} />
|
||||
<div style={style} className={cn("border-t-1 border-accent", className)} />
|
||||
)
|
||||
}
|
||||
|
||||
export default HorizontalRule;
|
||||
export default HorizontalRule;
|
||||
@@ -5,16 +5,16 @@ import { Project } from "@/payload-types";
|
||||
|
||||
const ProjectCard = (project: Project) => {
|
||||
return (
|
||||
<div className="border border-white rounded-md p-4 flex flex-col gap-2 max-w-[700px] mx-auto">
|
||||
<div className="glass glass-hover rounded-lg p-5 flex flex-col gap-3 max-w-[700px] mx-auto">
|
||||
<h2 className="text-2xl font-bold">{project.title}</h2>
|
||||
<div className="flex flex-col gap-2">
|
||||
{project.featuredImage && typeof project.featuredImage === "object" && (
|
||||
<Image src={project.featuredImage.url!} alt={project.title} width={project.featuredImage.width!} height={project.featuredImage.height!} className="rounded-md border border-gray-600" />
|
||||
<Image src={project.featuredImage.url!} alt={project.title} width={project.featuredImage.width!} height={project.featuredImage.height!} className="rounded-md border border-white/10" />
|
||||
)}
|
||||
<p className="whitespace-pre-wrap">{project.description}</p>
|
||||
<div className="flex flex-row gap-2 my-2">
|
||||
<div className="flex flex-row flex-wrap gap-2 my-2">
|
||||
{(project?.tags || []).map((tag) => (
|
||||
<span key={tag.id} className="bg-white text-black rounded-md px-1 py-0.5 text-sm hover:bg-gray-200">
|
||||
<span key={tag.id} className="bg-white/10 text-foreground rounded-md px-2 py-0.5 text-sm border border-white/10 transition-colors hover:bg-white/20">
|
||||
{tag.tag}
|
||||
</span>
|
||||
))}
|
||||
@@ -24,20 +24,20 @@ const ProjectCard = (project: Project) => {
|
||||
<div className="flex flex-row gap-4">
|
||||
{project.viewLink && (
|
||||
<a href={project.viewLink} target="_blank" className="group">
|
||||
<span className="flex flex-row gap-2">
|
||||
<ExternalLink />
|
||||
<span className="flex flex-row gap-2 group-hover:text-accent transition-colors duration-300">
|
||||
<ExternalLink className="size-4" />
|
||||
View Project
|
||||
</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-accent"></span>
|
||||
</a>
|
||||
)}
|
||||
{project.repositoryLink && (
|
||||
<a href={project.repositoryLink} target="_blank" className="group">
|
||||
<span className="flex flex-row gap-2">
|
||||
<Code />
|
||||
<span className="flex flex-row gap-2 group-hover:text-accent transition-colors duration-300">
|
||||
<Code className="size-4" />
|
||||
View Source
|
||||
</span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-white"></span>
|
||||
<span className="block max-w-0 group-hover:max-w-full transition-all duration-500 h-0.5 bg-accent"></span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
@@ -45,4 +45,4 @@ const ProjectCard = (project: Project) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectCard;
|
||||
export default ProjectCard;
|
||||
Reference in new issue
Block a user