chore: setup astro

This commit is contained in:
Alejandro Laguna
2025-08-02 14:40:33 +02:00
parent 3b0278b7fc
commit 8bb138dbe5
33 changed files with 5206 additions and 3491 deletions

34
.gitignore vendored
View File

@@ -1,24 +1,24 @@
# Logs # build output
logs dist/
*.log
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log*
node_modules # environment variables
dist .env
dist-ssr .env.production
*.local
# Editor directories and files # macOS-specific files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store .DS_Store
*.suo
*.ntvs* # jetbrains setting folder
*.njsproj .idea/
*.sln
*.sw?

View File

@@ -1,15 +1,46 @@
# `create-preact` # Astro Starter Kit: Basics
<h2 align="center"> ```sh
<img height="256" width="256" src="./src/assets/preact.svg"> npm create astro@latest -- --template basics
</h2> ```
<h3 align="center">Get started using Preact and Vite!</h3> > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## Getting Started ## 🚀 Project Structure
- `npm run dev` - Starts a dev server at http://localhost:5173/ Inside of your Astro project, you'll see the following folders and files:
- `npm run build` - Builds for production, emitting to `dist/` ```text
/
├── public/
│ └── favicon.svg
├── src
│   ├── assets
│   │   └── astro.svg
│   ├── components
│   │   └── Welcome.astro
│   ├── layouts
│   │   └── Layout.astro
│   └── pages
│   └── index.astro
└── package.json
```
- `npm run preview` - Starts a server at http://localhost:4173/ to test production build locally To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/).
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

5
astro.config.mjs Normal file
View File

@@ -0,0 +1,5 @@
// @ts-check
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({});

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon-32x32.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<title>Alejandro Laguna</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

5696
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,14 @@
{ {
"homepage": "https://alejandrolaguna20.github.io/portfolio/", "name": "portfoliov2",
"private": true,
"type": "module", "type": "module",
"version": "0.0.1",
"scripts": { "scripts": {
"dev": "vite", "dev": "astro dev",
"build": "vite build", "build": "astro build",
"preview": "vite preview", "preview": "astro preview",
"predeploy": "npm run build", "astro": "astro"
"deploy": "gh-pages -d dist"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.3", "astro": "^5.12.8"
"preact": "^10.25.3",
"preact-iso": "^2.9.1",
"tailwindcss": "^4.1.3"
},
"devDependencies": {
"@preact/preset-vite": "^2.9.3",
"gh-pages": "^6.3.0",
"typescript": "^5.8.2",
"vite": "^6.0.4"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

9
public/favicon.svg Normal file
View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

1
src/assets/astro.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="115" height="48"><path fill="#17191E" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="url(#a)" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="#17191E" d="M.02 30.31s4.02-1.95 8.05-1.95l3.04-9.4c.11-.45.44-.76.82-.76.37 0 .7.31.82.76l3.04 9.4c4.77 0 8.05 1.95 8.05 1.95L17 11.71c-.2-.56-.53-.91-.98-.91H7.83c-.44 0-.76.35-.97.9L.02 30.31Zm42.37-5.97c0 1.64-2.05 2.62-4.88 2.62-1.85 0-2.5-.45-2.5-1.41 0-1 .8-1.49 2.65-1.49 1.67 0 3.09.03 4.73.23v.05Zm.03-2.04a21.37 21.37 0 0 0-4.37-.36c-5.32 0-7.82 1.25-7.82 4.18 0 3.04 1.71 4.2 5.68 4.2 3.35 0 5.63-.84 6.46-2.92h.14c-.03.5-.05 1-.05 1.4 0 1.07.18 1.16 1.06 1.16h4.15a16.9 16.9 0 0 1-.36-4c0-1.67.06-2.93.06-4.62 0-3.45-2.07-5.64-8.56-5.64-2.8 0-5.9.48-8.26 1.19.22.93.54 2.83.7 4.06 2.04-.96 4.95-1.37 7.2-1.37 3.11 0 3.97.71 3.97 2.15v.57Zm11.37 3c-.56.07-1.33.07-2.12.07-.83 0-1.6-.03-2.12-.1l-.02.58c0 2.85 1.87 4.52 8.45 4.52 6.2 0 8.2-1.64 8.2-4.55 0-2.74-1.33-4.09-7.2-4.39-4.58-.2-4.99-.7-4.99-1.28 0-.66.59-1 3.65-1 3.18 0 4.03.43 4.03 1.35v.2a46.13 46.13 0 0 1 4.24.03l.02-.55c0-3.36-2.8-4.46-8.2-4.46-6.08 0-8.13 1.49-8.13 4.39 0 2.6 1.64 4.23 7.48 4.48 4.3.14 4.77.62 4.77 1.28 0 .7-.7 1.03-3.71 1.03-3.47 0-4.35-.48-4.35-1.47v-.13Zm19.82-12.05a17.5 17.5 0 0 1-6.24 3.48c.03.84.03 2.4.03 3.24l1.5.02c-.02 1.63-.04 3.6-.04 4.9 0 3.04 1.6 5.32 6.58 5.32 2.1 0 3.5-.23 5.23-.6a43.77 43.77 0 0 1-.46-4.13c-1.03.34-2.34.53-3.78.53-2 0-2.82-.55-2.82-2.13 0-1.37 0-2.65.03-3.84 2.57.02 5.13.07 6.64.11-.02-1.18.03-2.9.1-4.04-2.2.04-4.65.07-6.68.07l.07-2.93h-.16Zm13.46 6.04a767.33 767.33 0 0 1 .07-3.18H82.6c.07 1.96.07 3.98.07 6.92 0 2.95-.03 4.99-.07 6.93h5.18c-.09-1.37-.11-3.68-.11-5.65 0-3.1 1.26-4 4.12-4 1.33 0 2.28.16 3.1.46.03-1.16.26-3.43.4-4.43-.86-.25-1.81-.41-2.96-.41-2.46-.03-4.26.98-5.1 3.38l-.17-.02Zm22.55 3.65c0 2.5-1.8 3.66-4.64 3.66-2.81 0-4.61-1.1-4.61-3.66s1.82-3.52 4.61-3.52c2.82 0 4.64 1.03 4.64 3.52Zm4.71-.11c0-4.96-3.87-7.18-9.35-7.18-5.5 0-9.23 2.22-9.23 7.18 0 4.94 3.49 7.59 9.21 7.59 5.77 0 9.37-2.65 9.37-7.6Z"/><defs><linearGradient id="a" x1="6.33" x2="19.43" y1="40.8" y2="34.6" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1024" fill="none"><path fill="url(#a)" fill-rule="evenodd" d="M-217.58 475.75c91.82-72.02 225.52-29.38 341.2-44.74C240 415.56 372.33 315.14 466.77 384.9c102.9 76.02 44.74 246.76 90.31 366.31 29.83 78.24 90.48 136.14 129.48 210.23 57.92 109.99 169.67 208.23 155.9 331.77-13.52 121.26-103.42 264.33-224.23 281.37-141.96 20.03-232.72-220.96-374.06-196.99-151.7 25.73-172.68 330.24-325.85 315.72-128.6-12.2-110.9-230.73-128.15-358.76-12.16-90.14 65.87-176.25 44.1-264.57-26.42-107.2-167.12-163.46-176.72-273.45-10.15-116.29 33.01-248.75 124.87-320.79Z" clip-rule="evenodd" style="opacity:.154"/><path fill="url(#b)" fill-rule="evenodd" d="M1103.43 115.43c146.42-19.45 275.33-155.84 413.5-103.59 188.09 71.13 409 212.64 407.06 413.88-1.94 201.25-259.28 278.6-414.96 405.96-130 106.35-240.24 294.39-405.6 265.3-163.7-28.8-161.93-274.12-284.34-386.66-134.95-124.06-436-101.46-445.82-284.6-9.68-180.38 247.41-246.3 413.54-316.9 101.01-42.93 207.83 21.06 316.62 6.61Z" clip-rule="evenodd" style="opacity:.154"/><defs><linearGradient id="b" x1="373" x2="1995.44" y1="1100" y2="118.03" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient><linearGradient id="a" x1="107.37" x2="1130.66" y1="1993.35" y2="1026.31" gradientUnits="userSpaceOnUse"><stop stop-color="#3245FF"/><stop offset="1" stop-color="#BC52EE"/></linearGradient></defs></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="27.68" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 296">
<path fill="#673AB8" d="m128 0l128 73.9v147.8l-128 73.9L0 221.7V73.9z"></path>
<path fill="#FFF" d="M34.865 220.478c17.016 21.78 71.095 5.185 122.15-34.704c51.055-39.888 80.24-88.345 63.224-110.126c-17.017-21.78-71.095-5.184-122.15 34.704c-51.055 39.89-80.24 88.346-63.224 110.126Zm7.27-5.68c-5.644-7.222-3.178-21.402 7.573-39.253c11.322-18.797 30.541-39.548 54.06-57.923c23.52-18.375 48.303-32.004 69.281-38.442c19.922-6.113 34.277-5.075 39.92 2.148c5.644 7.223 3.178 21.403-7.573 39.254c-11.322 18.797-30.541 39.547-54.06 57.923c-23.52 18.375-48.304 32.004-69.281 38.441c-19.922 6.114-34.277 5.076-39.92-2.147Z"></path>
<path fill="#FFF" d="M220.239 220.478c17.017-21.78-12.169-70.237-63.224-110.126C105.96 70.464 51.88 53.868 34.865 75.648c-17.017 21.78 12.169 70.238 63.224 110.126c51.055 39.889 105.133 56.485 122.15 34.704Zm-7.27-5.68c-5.643 7.224-19.998 8.262-39.92 2.148c-20.978-6.437-45.761-20.066-69.28-38.441c-23.52-18.376-42.74-39.126-54.06-57.923c-10.752-17.851-13.218-32.03-7.575-39.254c5.644-7.223 19.999-8.261 39.92-2.148c20.978 6.438 45.762 20.067 69.281 38.442c23.52 18.375 42.739 39.126 54.06 57.923c10.752 17.85 13.218 32.03 7.574 39.254Z"></path>
<path fill="#FFF" d="M127.552 167.667c10.827 0 19.603-8.777 19.603-19.604c0-10.826-8.776-19.603-19.603-19.603c-10.827 0-19.604 8.777-19.604 19.603c0 10.827 8.777 19.604 19.604 19.604Z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,48 +0,0 @@
import { useEffect, useState } from 'react';
import image from '../assets/sketch.png';
const taglines = [
"Amateur Writer",
"Bookworm",
"Star Gazer",
"Physics Enthusiast",
"Lifelong Learner",
"Tea Enthusiast",
"Cosmic Explorer",
"Knowledge Seeker",
];
export default function About({ theme }) {
const [tagline, setTagline] = useState('');
useEffect(() => {
const randomTagline = taglines[Math.floor(Math.random() * taglines.length)];
setTagline(randomTagline);
}, []);
return (
<section id="about" class="pt-8">
<div class="flex flex-col md:flex-row gap-8 items-center">
<div class="flex-1">
<h1 class={`text-4xl md:text-5xl mb-4 ${theme.accent} font-serif font-semibold`}>Alejandro Laguna</h1>
<h2 class="text-xl md:text-2xl mb-6 text-[#b8c4b5]/80 font-medium font-serif">Software Engineer & {tagline}</h2>
<p class="text-lg mb-6 text-[#b8c4b5]/90">
Crafting digital projects that inspire me, while exploring everything from books and tea to space and science. First-year computer science student.
</p>
<div class="flex gap-4">
<a href="#projects" class={`px-6 py-2 rounded-full border ${theme.border} ${theme.hover} transition-all hover:-translate-y-0.5`}>
View Work
</a>
<a href="mailto:alejandro.laguna@epitech.eu" class={`px-6 py-2 rounded-full ${theme.button} transition-all hover:-translate-y-0.5`}>
Contact
</a>
</div>
</div>
<div class="flex-1 flex justify-center">
<img src={image} alt="Alex Clark's Sketch" class="w-64 h-64 rounded-full object-cover" />
</div>
</div>
</section>
);
}

View File

@@ -1,37 +0,0 @@
import EducationCard from './EducationCard';
const education = [
{
institution: "European Institute of Technology (EPITECH)",
degree: "Degree + Master in Computer Science",
duration: "2024 - 2029",
highlights: [
"ONGOING",
"Multiple courses passed with Honors",
"Completed over 30 projects from different fields"
]
},
{
institution: "INS Cendrassos",
degree: "Microcomputer systems & networks",
duration: "2022 - 2024",
highlights: [
"First of the class",
"ERASMUS+ Scholarship",
"Student of the year"
]
}
];
export default function Education({ theme }) {
return (
<section id="education">
<h2 class={`text-3xl mb-8 font-serif font-semibold ${theme.accent}`}>Education</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{education.map((edu, i) => (
<EducationCard key={i} education={edu} theme={theme} />
))}
</div>
</section>
);
}

View File

@@ -1,18 +0,0 @@
export default function EducationCard({ education, theme }) {
return (
<div class={`p-4 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
<div class="flex justify-between items-start mb-4">
<div>
<h3 class="text-lg">{education.degree}</h3>
<p class="text-[#b8c4b5]/80">{education.institution}</p>
</div>
<span class="text-sm text-[#b8c4b5]/50">{education.duration}</span>
</div>
<ul class="list-disc pl-6 space-y-2 mb-4">
{education.highlights.map((highlight, j) => (
<li key={j} class="text-[#b8c4b5]/90">{highlight}</li>
))}
</ul>
</div>
);
}

View File

@@ -1,39 +0,0 @@
import ExperienceCard from './ExperienceCard';
const experience = [
{
company: "Avantiam Inc - Figueres, Spain",
role: "Full-Stack Developer",
duration: "Sep 2023 - Sep 2024",
technologies: ["Laravel", "Livewire", "AlpineJS", "TailwindCSS", "MariaDB/MySQL"],
highlights: [
"Developed and maintained a comprehensive business management service used by over 100 companies",
"Managed all aspects of the project lifecycle, including server administration, performance tuning, and user experience improvements.",
"Recommendation letter available upon request."
]
},
{
company: "Niblu Group - Maribor, Slovenia",
role: "Backend Developer Intern",
duration: "Summer 2023",
technologies: ["NextJS", "TailwindCSS", "TypeScript", "FastAPI", "AWS S3 Bucket"],
highlights: [
"Developed a secure authentication system and features for managing courses across different user roles.",
"Integrated AWS S3 for seamless storage and retrieval of course materials.",
"Gained valuable personal and professional growth through the ERASMUS experience, adapting to new challenges abroad alone."
]
}
];
export default function Experience({ theme }) {
return (
<section id="experience">
<h2 class={`text-3xl mb-8 ${theme.accent} font-semibold font-serif`}>Work Experience</h2>
<div class="space-y-8">
{experience.map((exp, i) => (
<ExperienceCard key={i} experience={exp} theme={theme} />
))}
</div>
</section>
);
}

View File

@@ -1,36 +0,0 @@
export default function ExperienceCard({ experience, theme }) {
return (
<div class={`p-6 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
<div class="flex justify-between items-start mb-4">
<div>
<h3 class="text-xl">{experience.company}</h3>
<p class="text-[#b8c4b5]/80">{experience.role}</p>
</div>
<span class="text-sm text-[#b8c4b5]/50">{experience.duration}</span>
</div>
<ul class="list-disc pl-6 space-y-2 mb-4">
{experience.highlights.map((highlight, j) => (
<li key={j} class="text-[#b8c4b5]/90">{highlight}</li>
))}
</ul>
{experience.technologies && (
<div class="flex flex-wrap gap-2 mb-4">
<h4 class="text-lg font-semibold text-[#b8c4b5]/90">Technologies Used:</h4>
{experience.technologies.map((tech, i) => (
<span key={i} class={`px-3 rounded-full ${theme.bg} text-[#b8c4b5]/80`}>{tech}</span>
))}
</div>
)}
{experience.link && (
<a
href={experience.link}
class={`inline-flex items-center text-sm ${theme.accent} ${theme.hover}`}
>
View More
<span class="ml-2 transition-transform group-hover:translate-x-1"></span>
</a>
)}
</div>
);
}

View File

@@ -1,15 +0,0 @@
export default function Footer({ theme }) {
return (
<footer class={`pt-8 border-t border-opacity-20 ${theme.border}`}>
<div class="flex flex-col md:flex-row justify-between items-center">
<p class="text-sm text-[#b8c4b5]/50 mb-4 md:mb-0">© 2025 Alejandro Laguna</p>
<div class="flex gap-6">
<a href="https://github.com/alejandrolaguna20" class={`text-sm ${theme.text} ${theme.hover}`}>GitHub</a>
<a href="https://www.linkedin.com/in/alejandro-laguna-939687278/" class={`text-sm ${theme.text} ${theme.hover}`}>LinkedIn</a>
<a href="/resume_eng.pdf" download="cv_english_alejandro_laguna.pdf" target="_blank" rel="noopener" class={`text-sm ${theme.text} ${theme.hover}`}>CV</a>
</div>
</div>
</footer>
);
}

View File

@@ -1,22 +0,0 @@
export function Header({ theme }) {
return (
<header class="fixed w-full top-0 z-50 backdrop-blur-sm">
<div class="max-w-5xl mx-auto px-4 py-3 flex justify-between items-center">
<h1 class={`text-xl font-bold opacity-50 ${theme.accent} tracking-wider font-serif`}>alejandrolaguna.dev</h1>
<nav class="hidden md:flex gap-6">
{['About', 'Projects', 'Experience'].map((item) => (
<a
href={`#${item.toLowerCase()}`}
class={`text-sm ${theme.text} ${theme.hover} transition-colors`}
>
{item}
</a>
))}
</nav>
</div>
</header>
);
}

View File

@@ -1,10 +0,0 @@
export default function Layout({ children, theme }) {
return (
<div class={`min-h-screen transition-colors duration-300 ${theme.bg} ${theme.text}`}>
<main class="max-w-5xl mx-auto px-4 pt-24 pb-16 space-y-16">
{children}
</main>
</div>
);
}

View File

@@ -1,26 +0,0 @@
export default function ProjectCard({ project, theme }) {
return (
<div class={`p-6 rounded-xl border ${theme.border} ${theme.card} transition-all hover:-translate-y-1 group`}>
<div class="flex justify-between items-start mb-4">
<h3 class="text-xl">{project.title}</h3>
<span class="text-sm text-[#b8c4b5]/50">{project.year}</span>
</div>
<p class="mb-4 text-[#b8c4b5]/80">{project.description}</p>
<div class="flex flex-wrap gap-2 mb-4">
{project.tech.map((tech, j) => (
<span key={j} class={`px-3 py-1 rounded-full ${theme.bg} text-[#b8c4b5]/80`}>{tech}</span>
))}
</div>
{project.link && (
<a
href={project.link}
class={`inline-flex items-center text-sm ${theme.accent} ${theme.hover}`}
>
View Details
<span class="ml-2 transition-transform group-hover:translate-x-1"></span>
</a>
)}
</div>
);
}

View File

@@ -1,53 +0,0 @@
import ProjectCard from './ProjectCard';
const projects = [
{
title: "Morph",
description: "Lightweight URL shortening API built in Go with minimal dependencies. First ever project with this language, and I tried to focus on the stdlib and build any needed tools",
tech: ["Go"],
year: "2025",
link: "https://github.com/alejandrolaguna20/morph"
},
{
title: "Runes",
description: "A small, simple, smooth and beautiful flashcard TUI program featuring keyboard navigation and custom study sessions. Built with Go and the Bubbletea framework.",
tech: ["Go", "Bubbletea"],
year: "2025",
link: "https://github.com/alejandrolaguna20/nook.nvim"
},
{
title: "Nook.nvim",
description: "A tiny Neovim plugin created to manage tasks in a way that perfectly fits my personal workflow ",
tech: ["Lua"],
year: "2025",
link: "https://github.com/alejandrolaguna20/nook.nvim"
},
{
title: "Portfolio",
description: "The website you are currently browsing, my little corner on the web.",
tech: ["Typescript", "PreactJS", "TailwindCSS", "Vite"],
year: "2025",
link: "https://github.com/alejandrolaguna20/portfolio"
},
{
title: "Epitech Projects",
description: "A variety of projects I am building in my first year @ Epitech that range from algorithm implementation, graphical and mathematical simulations, and developing from scratch different Linux commands and programs, including a shell.",
tech: ["C", "Bash", "Python"],
year: "2025",
link: "https://www.epitech.eu/programme-grande-ecole-informatique/"
}
];
export default function Projects({ theme }) {
return (
<section id="projects">
<h2 class={`text-3xl font-semibold font-serif mb-8 ${theme.accent}`}>Projects</h2>
<div class="grid gap-6">
{projects.map((project, i) => (
<ProjectCard key={i} project={project} theme={theme} />
))}
</div>
</section>
);
}

View File

@@ -0,0 +1,210 @@
---
import astroLogo from '../assets/astro.svg';
import background from '../assets/background.svg';
---
<div id="container">
<img id="background" src={background.src} alt="" fetchpriority="high" />
<main>
<section id="hero">
<a href="https://astro.build"
><img src={astroLogo.src} width="115" height="48" alt="Astro Homepage" /></a
>
<h1>
To get started, open the <code><pre>src/pages</pre></code> directory in your project.
</h1>
<section id="links">
<a class="button" href="https://docs.astro.build">Read our docs</a>
<a href="https://astro.build/chat"
>Join our Discord <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"
><path
fill="currentColor"
d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z"
></path></svg
>
</a>
</section>
</section>
</main>
<a href="https://astro.build/blog/astro-5/" id="news" class="box">
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"
><path
d="M24.667 12c1.333 1.414 2 3.192 2 5.334 0 4.62-4.934 5.7-7.334 12C18.444 28.567 18 27.456 18 26c0-4.642 6.667-7.053 6.667-14Zm-5.334-5.333c1.6 1.65 2.4 3.43 2.4 5.333 0 6.602-8.06 7.59-6.4 17.334C13.111 27.787 12 25.564 12 22.666c0-4.434 7.333-8 7.333-16Zm-6-5.333C15.111 3.555 16 5.556 16 7.333c0 8.333-11.333 10.962-5.333 22-3.488-.774-6-4-6-8 0-8.667 8.666-10 8.666-20Z"
fill="#111827"></path></svg
>
<h2>What's New in Astro 5.0?</h2>
<p>
From content layers to server islands, click to learn more about the new features and
improvements in Astro 5.0
</p>
</a>
</div>
<style>
#background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
filter: blur(100px);
}
#container {
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
height: 100%;
}
main {
height: 100%;
display: flex;
justify-content: center;
}
#hero {
display: flex;
align-items: start;
flex-direction: column;
justify-content: center;
padding: 16px;
}
h1 {
font-size: 22px;
margin-top: 0.25em;
}
#links {
display: flex;
gap: 16px;
}
#links a {
display: flex;
align-items: center;
padding: 10px 12px;
color: #111827;
text-decoration: none;
transition: color 0.2s;
}
#links a:hover {
color: rgb(78, 80, 86);
}
#links a svg {
height: 1em;
margin-left: 8px;
}
#links a.button {
color: white;
background: linear-gradient(83.21deg, #3245ff 0%, #bc52ee 100%);
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.12),
inset 0 -2px 0 rgba(0, 0, 0, 0.24);
border-radius: 10px;
}
#links a.button:hover {
color: rgb(230, 230, 230);
box-shadow: none;
}
pre {
font-family:
ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono',
monospace;
font-weight: normal;
background: linear-gradient(14deg, #d83333 0%, #f041ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0;
}
h2 {
margin: 0 0 1em;
font-weight: normal;
color: #111827;
font-size: 20px;
}
p {
color: #4b5563;
font-size: 16px;
line-height: 24px;
letter-spacing: -0.006em;
margin: 0;
}
code {
display: inline-block;
background:
linear-gradient(66.77deg, #f3cddd 0%, #f5cee7 100%) padding-box,
linear-gradient(155deg, #d83333 0%, #f041ff 18%, #f5cee7 45%) border-box;
border-radius: 8px;
border: 1px solid transparent;
padding: 6px 8px;
}
.box {
padding: 16px;
background: rgba(255, 255, 255, 1);
border-radius: 16px;
border: 1px solid white;
}
#news {
position: absolute;
bottom: 16px;
right: 16px;
max-width: 300px;
text-decoration: none;
transition: background 0.2s;
backdrop-filter: blur(50px);
}
#news:hover {
background: rgba(255, 255, 255, 0.55);
}
@media screen and (max-height: 368px) {
#news {
display: none;
}
}
@media screen and (max-width: 768px) {
#container {
display: flex;
flex-direction: column;
}
#hero {
display: block;
padding-top: 10%;
}
#links {
flex-wrap: wrap;
}
#links a.button {
padding: 14px 18px;
}
#news {
right: 16px;
left: 16px;
bottom: 2.5rem;
max-width: 100%;
}
h1 {
line-height: 1.5;
}
}
</style>

View File

@@ -1,44 +0,0 @@
import { render } from 'preact';
import { LocationProvider, Router, Route } from 'preact-iso';
import { Home } from './pages/Home/index.jsx';
import { NotFound } from './pages/_404.jsx';
import './style.css';
import { Header } from './components/Header.js';
export function App() {
const theme = {
bg: 'bg-[#1c2529]',
text: 'text-[#f0e4c8]',
accent: 'text-[#c7dd9a]',
accent2: 'text-[#f0b090]',
border: 'border-[#344146]',
card: 'bg-[#263136]',
hover: 'hover:text-[#fff4d8]',
button: 'bg-[#344146] hover:bg-[#49555a] text-[#f0e4c8]',
bg_hl: 'bg-[#344146]',
gradient: 'from-[#1c2529] via-[#263136] to-[#1c2529]'
}
return (
<LocationProvider>
<Header
theme={theme}
/>
<main>
<Router>
<Route
path="/"
component={() => (
<Home
theme={theme}
/>
)}
/>
<Route default component={() => (<Home theme={theme} />)} />
</Router>
</main>
</LocationProvider>
);
}
render(<App />, document.getElementById('app'));

22
src/layouts/Layout.astro Normal file
View File

@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>Astro Basics</title>
</head>
<body>
<slot />
</body>
</html>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,28 +0,0 @@
import { useState, useEffect } from 'preact/hooks';
import Layout from '../../components/Layout';
import About from '../../components/About';
import Projects from '../../components/Projects';
import Experience from '../../components/Experience';
import Footer from '../../components/Footer';
import Education from '../../components/Education';
export function Home({ theme }) {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => setScrollPosition(window.scrollY);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<Layout theme={theme}>
<About theme={theme} />
<Education theme={theme} />
<Experience theme={theme} />
<Projects theme={theme} />
<Footer theme={theme} />
</Layout>
);
}

View File

@@ -1,8 +0,0 @@
export function NotFound() {
return (
<section>
<h1>404: Not Found</h1>
<p>It's gone :(</p>
</section>
);
}

11
src/pages/index.astro Normal file
View File

@@ -0,0 +1,11 @@
---
import Welcome from '../components/Welcome.astro';
import Layout from '../layouts/Layout.astro';
// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build
// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh.
---
<Layout>
<Welcome />
</Layout>

View File

@@ -1,63 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Aleo:ital,wght@0,100..900;1,100..900&family=Sarala:wght@400;700&display=swap');
@import "tailwindcss";
@theme {
--font-serif: "Aleo", "monospace";
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.transition-all {
transition: all 0.3s ease;
}
.hover\:-translate-y-2:hover {
transform: translateY(-8px);
}
.hover\:-translate-y-1:hover {
transform: translateY(-4px);
}
.backdrop-blur-sm {
backdrop-filter: blur(8px);
}
/* Add to style.css */
.group:hover .group-hover\:translate-x-1 {
transform: translateX(0.25rem);
}
.transition-transform {
transition: transform 0.3s ease;
}
.list-disc li::marker {
color: #b8c4b550;
}
.group:hover .group-hover\:translate-x-1 {
transform: translateX(0.25rem);
}
.transition-transform {
transition: transform 0.3s ease;
}
.list-disc li::marker {
color: #b8c4b550;
}
html {
scroll-behavior: smooth;
}

View File

@@ -1,20 +1,5 @@
{ {
"compilerOptions": { "extends": "astro/tsconfigs/strict",
"target": "ES2020", "include": [".astro/types.d.ts", "**/*"],
"module": "ESNext", "exclude": ["dist"]
"moduleResolution": "bundler",
"noEmit": true,
"allowJs": true,
"checkJs": true,
/* Preact Config */
"jsx": "react-jsx",
"jsxImportSource": "preact",
"skipLibCheck": true,
"paths": {
"react": ["./node_modules/preact/compat/"],
"react-dom": ["./node_modules/preact/compat/"]
}
},
"include": ["node_modules/vite/client.d.ts", "**/*"]
} }

View File

@@ -1,11 +0,0 @@
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
import tailwindcss from '@tailwindcss/vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
tailwindcss(),
preact(),
],
});