Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Hazielgmz/astro-Portfolio/llms.txt
Use this file to discover all available pages before exploring further.
Core Features
Astro Portfolio is packed with modern web development features that make it performant, maintainable, and visually appealing.
Server-Side Rendering (SSR)
The portfolio is configured with Astro’s SSR mode and the Vercel adapter for optimal performance and dynamic content loading.
Configuration
SSR is enabled in astro.config.mjs:
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
output: 'server', // Enables SSR in Astro
adapter: vercel({}),
vite: {
plugins: [tailwindcss()]
}
});
Benefits
Dynamic Data
Content is fetched from Supabase on each request, ensuring always up-to-date information
SEO Optimization
Server-rendered HTML is fully crawlable by search engines
Fast Initial Load
Users see content immediately without waiting for client-side JavaScript
Secure API Keys
Environment variables stay on the server, never exposed to clients
Responsive Design
The portfolio is fully responsive and adapts beautifully to all screen sizes, from mobile phones to large desktop displays.
Mobile-First Layout
All components use Tailwind’s responsive utilities:
<article class="flex flex-col items-center gap-8 md:flex-row">
<div class="order-2 md:order-1">
<!-- Content -->
</div>
<img class="order-1 md:order-2 w-64" />
</article>
Breakpoint System
- Mobile: Default styles (< 768px)
- Tablet:
md: prefix (≥ 768px)
- Desktop:
lg: prefix (≥ 1024px)
Dynamic Content Management
All portfolio content is managed through Supabase, making updates easy without touching code.
Supabase Integration
The Supabase client is initialized once and reused across components:
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.SUPABASE_URL;
const supabaseKey = import.meta.env.SUPABASE_KEY;
export const supabase = createClient(supabaseUrl, supabaseKey);
Content Types
About Me
Projects
Career
Tools
Dynamic personal information loaded from the about_me table:---
import { supabase } from "../db/supabase"
const { data: aboutMe, error } = await supabase
.from("about_me")
.select("*")
.single()
---
<h1>{aboutMe.name}</h1>
<img src={aboutMe.profile_image} alt="Profile" />
Projects with filtering and relationships to tools:const { data: projects } = await supabase
.from("projects")
.select("*")
.eq('visible', true)
.order('created_at', { ascending: false });
Each project includes:
- Title and description
- Preview image
- GitHub repository link
- Live demo link
- Associated technologies
Professional experience displayed chronologically:const { data: careers } = await supabase
.from("careers")
.select("*")
.order('period', { ascending: false })
.eq('visible', true);
Tools categorized by type (Frontend, Backend, Database, Framework):async function fetchToolsByType(type: string) {
const { data } = await supabase
.from("tools")
.select("*")
.eq("type", type)
.eq("visible", true)
.order("name");
return data || [];
}
Interactive Components
The portfolio uses Alpine.js for lightweight, reactive interactivity without heavy JavaScript frameworks.
Alpine.js Integration
Alpine is loaded from CDN in the main layout:
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
Click on any tool to see related projects and certifications:
<div x-data="{
selectedTool: null,
modalOpen: false,
openModal(tool) {
this.selectedTool = tool;
this.modalOpen = true;
},
closeModal() {
this.modalOpen = false;
setTimeout(() => this.selectedTool = null, 300);
}
}">
<!-- Tools display -->
<div
x-on:click="openModal(tool)"
class="cursor-pointer transition hover:scale-110"
>
<!-- Tool card -->
</div>
<!-- Modal with transitions -->
<div
x-show="modalOpen"
x-transition:enter="transition ease-out duration-300"
>
<!-- Modal content -->
</div>
</div>
Features of the Modal
- Smooth transitions - Fade and scale animations
- Click outside to close - Using Alpine’s
@click.away
- Related content - Shows certificates and projects using the selected tool
- Visual indicator - Yellow pulse dot on tools with related content
The portfolio includes several scroll-based animations for a dynamic user experience.
The header transforms as you scroll using CSS animation-timeline:
#header-nav {
animation: blur linear both 0.5s;
animation-timeline: scroll();
animation-range: 0 500px;
}
@keyframes blur {
from {
background-color: transparent;
backdrop-filter: blur(0px);
}
to {
backdrop-filter: blur(20px);
background-color: rgba(229, 229, 229, 0.8);
border-radius: 9999px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
}
Intersection Observer Navigation
Active navigation items are highlighted based on scroll position:
const callback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
navItems.forEach((item) => {
if (item.getAttribute("aria-label") == entry.target.id) {
item.classList.add("text-blue-500")
} else {
item.classList.remove("text-blue-500")
}
})
}
})
}
const observer = new IntersectionObserver(callback, {
threshold: 0.3
});
Project Showcase
The projects section displays your work with rich media and detailed information.
Project Cards
Each project features:
<article class="flex flex-col md:flex-row group">
<div class="w-full md:w-1/2">
<img
src={project.image}
class="transition duration-500 md:group-hover:scale-105"
/>
</div>
<div class="w-full md:w-1/2">
<h3>{project.title}</h3>
<ul class="flex flex-wrap gap-2">
{projectTools.map((tool) => (
<li class="rounded-full px-2 py-1">
<img src={tool.icon} class="w-4 h-4" />
{tool.name}
</li>
))}
</ul>
<p>{project.description}</p>
<footer class="flex gap-4">
<LinkButton href={project.codeLink}>
<GitHub class="size-6" /> Code
</LinkButton>
<LinkButton href={project.PreviewLink}>
<Link class="size-4" /> Preview
</LinkButton>
</footer>
</div>
</article>
Tools are color-coded by category:
function getToolClass(toolType: string) {
switch(toolType) {
case 'frontend':
return 'bg-blue-600 text-white';
case 'backend':
return 'bg-green-600 text-white';
case 'database':
return 'bg-yellow-600 text-black';
case 'framework':
return 'bg-purple-600 text-white';
default:
return 'bg-gray-600 text-white';
}
}
Career Timeline
Display your professional journey in a clean, chronological format.
<ol class="relative mt-16">
{careers.map((career) => (
<li>
<ExperienceItem {...career} />
</li>
))}
</ol>
Career items are automatically sorted by period in descending order, showing your most recent experience first.
The tools section organizes your technical skills by category with interactive elements.
Categorized Display
Tools are grouped into four categories:
- Frontend - React, Vue, HTML, CSS, etc.
- Backend - Node.js, Python, Java, etc.
- Database - PostgreSQL, MongoDB, Redis, etc.
- Frameworks - Next.js, Astro, Express, etc.
Each category uses horizontal scrolling for better mobile experience:
<div class="flex overflow-x-auto snap-x scroll-smooth gap-8">
{tools.map(tool => (
<div class="flex-none snap-start min-w-[65px]">
<img src={tool.icon} class="w-12 h-12" />
<h3>{tool.name}</h3>
</div>
))}
</div>
.custom-scrollbar::-webkit-scrollbar {
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: rgba(156, 163, 175, 0.5);
border-radius: 8px;
}
Dark Mode Support
The entire portfolio supports dark mode using Tailwind’s dark mode utilities.
Color Scheme
<div class="bg-white dark:bg-gray-800">
<p class="text-gray-900 dark:text-white">
Content
</p>
</div>
Animated Background
The gradient background adapts to dark mode:
<div class="
bg-gradient-to-br
from-indigo-100 via-slate-100 to-blue-50
dark:from-indigo-900 dark:via-slate-900 dark:to-blue-900
"></div>
Typography & Fonts
The portfolio uses the Onest Variable font for a modern, clean appearance:
import "@fontsource-variable/onest"
html {
font-family: "Onest Variable", sans-serif;
scroll-behavior: smooth;
}
Lazy Loading
Images use native lazy loading: loading="lazy"
Optimized Queries
Only visible content is fetched: .eq('visible', true)
Minimal JavaScript
Alpine.js is only 15kb gzipped
CSS Purging
Tailwind removes unused CSS in production
Accessibility Features
- Semantic HTML - Proper use of
<header>, <main>, <article>, <section>
- ARIA labels - Screen reader support for navigation and icons
- Keyboard navigation - All interactive elements are keyboard accessible
- Reduced motion - Respects
prefers-reduced-motion preference:
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
Next Steps
Customize Your Portfolio
Learn how to personalize colors, fonts, and layouts
Supabase Setup
Complete guide to setting up your database schema
Deploy to Production
Deploy your portfolio to Vercel or other platforms
Add Custom Components
Extend the portfolio with your own components