Construyendo Mi Portfolio Personal con Astro y Tailwind CSS
Introducción
Cuando me propuse construir mi portfolio personal y blog, tenía una visión clara: crear un sitio web moderno y eficiente que mostrara mi trabajo mientras fuera mantenible y escalable. Después de evaluar varios frameworks, elegí Astro por su simplicidad, rendimiento y flexibilidad.
En este artículo, te guiaré a través de todo el proceso de desarrollo, las tecnologías que utilicé y los desafíos que enfrenté en el camino.
Stack Tecnológico
Tecnologías Principales
- Astro 5.x: El framework principal, elegido por su arquitectura de islas y excelente rendimiento
- TypeScript: Para seguridad de tipos y mejor experiencia de desarrollo
- Tailwind CSS v4: Para estilizado con un enfoque utility-first
- MDX: Para escribir contenido del blog con la capacidad de usar componentes React
Librerías Adicionales
- Resend: Para manejar envíos del formulario de contacto
- @fontsource: Para auto-alojar las fuentes Inter y JetBrains Mono
- Astro Sitemap: Para generación automática del sitemap
Estructura del Proyecto
El proyecto sigue la estructura recomendada de Astro con algunas adiciones personalizadas para internacionalización:
portfolio-cristianqg/
├── src/
│ ├── components/
│ │ ├── sections/ # Secciones del homepage
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ ├── ThemeToggle.astro
│ │ └── LanguageSelector.astro
│ ├── content/
│ │ └── blog/ # Posts del blog en MDX
│ ├── i18n/
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── de.json
│ │ └── utils.ts
│ ├── layouts/
│ │ └── BaseLayout.astro
│ ├── pages/
│ │ ├── index.astro # Homepage en inglés
│ │ ├── es/
│ │ │ └── index.astro # Homepage en español
│ │ ├── de/
│ │ │ └── index.astro # Homepage en alemán
│ │ └── blog/
│ │ ├── index.astro
│ │ └── [...slug].astro
│ └── styles/
│ └── global.css
└── public/
Características Clave
1. Internacionalización (i18n)
Uno de los aspectos más desafiantes fue implementar una internacionalización adecuada. Quería soportar tres idiomas: inglés, español y alemán.
Solución
Creé un sistema i18n personalizado usando archivos JSON para traducciones y funciones de utilidad para manejar la detección de idioma y traducción:
// src/i18n/utils.ts
export function getLangFromUrl(url: URL) {
const [, lang] = url.pathname.split('/');
if (lang in translations) return lang;
return defaultLang;
}
export function useTranslations(lang: string) {
return function t(key: string) {
// Lógica de traducción
};
}
2. Sistema de Temas
Implementé un sistema de temas oscuro/claro que persiste las preferencias del usuario usando localStorage. El sistema por defecto es modo oscuro y respeta las preferencias del sistema del usuario.
const currentTheme = localStorage.getItem('theme') || 'dark';
3. Colecciones de Contenido
Para el blog, utilicé la API de Colecciones de Contenido de Astro con un esquema para asegurar la seguridad de tipos:
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.date(),
lang: z.enum(['en', 'es', 'de']),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
4. Sistema de Diseño
Creé un sistema de diseño cohesivo usando propiedades personalizadas de CSS para los colores:
- Fondo Oscuro:
#18181B - Texto Claro:
#FEF7ED - Primario/Acento:
#EA580C - Secundario:
#71717A
El diseño es minimalista, enfocándose en la legibilidad y experiencia del usuario. Todos los colores cumplen con los estándares de accesibilidad WCAG 2.1 para contraste.
5. Navegación Responsiva
El header incluye un menú de navegación responsivo que se colapsa en un menú hamburguesa en dispositivos móviles, asegurando una gran experiencia en todos los tamaños de pantalla.
Secciones del Portfolio
El homepage está dividido en varias secciones:
- Hero: Introducción con botones de llamada a la acción
- Sobre mí: Filosofía personal y enfoque al trabajo
- Proyectos: Muestra de proyectos clave con enlaces a repositorios
- Experiencia: Línea temporal profesional con tecnologías utilizadas
- Habilidades: Habilidades técnicas categorizadas con niveles de competencia
- Contacto: Formulario para contactar (integra con API de Resend)
Funcionalidad del Blog
El blog incluye:
- Búsqueda: Filtrado de búsqueda del lado del cliente por título, descripción y tags
- Tags: Sistema de categorización para organizar posts
- Tiempo de Lectura: Cálculo automático basado en el conteo de palabras
- Multiidioma: Los posts pueden escribirse en cualquiera de los tres idiomas soportados
- Resaltado de Sintaxis: Bloques de código con formato apropiado
- Modo Borrador: Capacidad de ocultar posts de producción
Optimizaciones de Rendimiento
- Generación de Sitios Estáticos: Todas las páginas se pre-renderizan en tiempo de build
- Optimización de Fuentes: Fuentes auto-alojadas para reducir peticiones externas
- Optimización de Imágenes: Optimización de imágenes integrada en Astro
- JavaScript Mínimo: Solo se cargan scripts esenciales del lado del cliente
Estrategia de Despliegue
El sitio está desplegado en mi propio servidor Hetzner usando Dokploy, que automáticamente despliega cambios desde el repositorio de GitHub.
Desafíos y Soluciones
Desafío 1: Gestión de Rutas Multiidioma
Problema: Mantener rutas consistentes entre idiomas mientras se mantiene el SEO.
Solución: Creé funciones de utilidad para generar paths localizados y usé la configuración de enrutamiento de Astro para manejar el idioma por defecto sin prefijo.
Desafío 2: Flash de Tema al Cargar la Página
Problema: Breve flash del tema incorrecto antes de que se ejecute JavaScript.
Solución: JavaScript crítico inline en el head para establecer el tema antes de renderizar el contenido.
Desafío 3: Aislamiento de Idioma en Posts del Blog
Problema: Asegurar que los usuarios solo vean posts en su idioma seleccionado.
Solución: Filtré posts del blog por idioma en la consulta de colección:
const allPosts = await getCollection('blog', ({ data }) => {
return data.lang === lang && !data.draft;
});
Lecciones Aprendidas
-
Empezar con Accesibilidad: Construir con accesibilidad en mente desde el principio es mucho más fácil que adaptarla después.
-
La Seguridad de Tipos Importa: TypeScript atrapó numerosos bugs potenciales durante el desarrollo.
-
Las Variables CSS son Poderosas: Usar propiedades personalizadas de CSS hizo el cambio de tema trivial y mantenible.
-
Las Colecciones de Contenido son Increíbles: La API de Colecciones de Contenido de Astro proporciona una excelente experiencia de desarrollo con seguridad de tipos y validación.
-
Rendimiento por Defecto: La arquitectura de Astro facilita construir sitios web rápidos sin mucho esfuerzo de optimización.
Mejoras Futuras
Aunque el portfolio es funcional y cumple mis necesidades actuales, hay varias mejoras que me gustaría añadir:
- Feed RSS: Para suscriptores del blog
- Sistema de Comentarios: Para habilitar discusión en posts del blog
- Panel de Analíticas: Visualización personalizada de analíticas
- Integración de Newsletter: Para notificar a suscriptores de nuevos posts
- Modo Oscuro para Bloques de Código: Mejores temas de resaltado de sintaxis
Conclusión
Construir este portfolio con Astro fue una excelente experiencia de aprendizaje. El enfoque de simplicidad y rendimiento del framework se alinea perfectamente con mi filosofía de desarrollo de crear código limpio y mantenible.
La combinación de Astro, Tailwind CSS y TypeScript proporcionó una base sólida para construir un portfolio moderno y profesional que es fácil de mantener y extender.
Si estás considerando construir tu propio portfolio, recomiendo encarecidamente probar Astro. Su curva de aprendizaje es suave, pero es lo suficientemente poderoso para requisitos complejos como internacionalización y gestión de contenido.
Puedes ver el código fuente de este proyecto en mi GitHub.
Recursos
¡Gracias por leer! No dudes en contactarme si tienes alguna pregunta sobre la implementación o quieres discutir sobre desarrollo web.