diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-10-12 22:49:13 +0200 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-10-12 22:49:13 +0200 |
| commit | cbdb2ee1547fcfbef8c874da60b17eee80b5fa18 (patch) | |
| tree | f1adc98515c2ac7681dcd0902b9ebcc2997d6b73 | |
| parent | dcc806eff41da24ee6b5984d011f62bc9f4f00ee (diff) | |
Get rid of trajectory and portfolio
| -rw-r--r-- | public/fonts/robotoflex.woff2 | bin | 0 -> 747084 bytes | |||
| -rw-r--r-- | src/content.config.ts | 16 | ||||
| -rw-r--r-- | src/data/portfolio/mapa-ferrol.mdx | 45 | ||||
| -rw-r--r-- | src/data/portfolio/mientreno.mdx | 17 | ||||
| -rw-r--r-- | src/data/portfolio/order-extractor.mdx | 27 | ||||
| -rw-r--r-- | src/data/portfolio/qr-ponteareas.mdx | 13 | ||||
| -rw-r--r-- | src/data/portfolio/qr-touro.mdx | 13 | ||||
| -rw-r--r-- | src/data/portfolio/vigo-360.mdx | 17 | ||||
| -rw-r--r-- | src/data/portfolio/wp-consulting.mdx | 9 | ||||
| -rw-r--r-- | src/layouts/HomePageLayout.astro | 46 | ||||
| -rw-r--r-- | src/layouts/Layout.astro | 15 | ||||
| -rw-r--r-- | src/layouts/PortfolioListLayout.astro | 137 | ||||
| -rw-r--r-- | src/layouts/PortfolioSingleLayout.astro | 370 | ||||
| -rw-r--r-- | src/pages/contact.astro | 32 | ||||
| -rw-r--r-- | src/pages/portfolio/[id].astro | 17 | ||||
| -rw-r--r-- | src/pages/portfolio/index.astro | 5 | ||||
| -rw-r--r-- | src/pages/trajectory.astro | 5 | ||||
| -rw-r--r-- | src/partials/Header.astro | 5 | ||||
| -rw-r--r-- | styles/_variables.scss | 4 | ||||
| -rw-r--r-- | styles/shared.scss | 14 |
20 files changed, 37 insertions, 770 deletions
diff --git a/public/fonts/robotoflex.woff2 b/public/fonts/robotoflex.woff2 Binary files differnew file mode 100644 index 0000000..b4acfba --- /dev/null +++ b/public/fonts/robotoflex.woff2 diff --git a/src/content.config.ts b/src/content.config.ts index acaaf8a..b5fca4e 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -11,22 +11,6 @@ const blog = defineCollection({ }), }); -const portfolio = defineCollection({ - loader: glob({ pattern: "**/*.{md,mdx}", base: "src/data/portfolio" }), - schema: z.object({ - title: z.string(), - description: z.string(), - technologies: z.array(z.string()), - githubLink: z.string().url().optional(), - onlineLink: z.string().url().optional(), - demoLink: z.string().url().optional(), - images: z.array( - z.object({ src: z.string(), alt: z.string() }) - ).default([]) - }), -}); - export const collections = { blog, - portfolio, }; diff --git a/src/data/portfolio/mapa-ferrol.mdx b/src/data/portfolio/mapa-ferrol.mdx deleted file mode 100644 index 6152ced..0000000 --- a/src/data/portfolio/mapa-ferrol.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "Mapa de patrimonio da Costa Ártabra" -description: "Desarrollo de una aplicación completa para el patrimonio cultural y militar de la Costa Ártabra." -technologies: ["dotnet", "react", "mysql", "ubuntu"] -onlineLink: "https://mapacostaartabramilitar.com" -images: -- src: "/images/portfolio/mapa-ferrol/home-narrow" - alt: "Vista principal del mapa con los elementos del patrimonio" -- src: "/images/portfolio/mapa-ferrol/home-info-narrow" - alt: "Sheet con información de un elemento patrimonial" -- src: "/images/portfolio/mapa-ferrol/route-id-narrow" - alt: "Vista de una ruta predefinida" -- src: "/images/portfolio/mapa-ferrol/planner-narrow" - alt: "Planificador de rutas de senderismo" ---- - -Desarrollo de una **aplicación web integral** para la exploración del patrimonio cultural y militar de la Costa Ártabra (Ferrol, Ares, Valdoviño). La plataforma incluye baterías costeras, faros, rutas de senderismo y puntos de interés natural, ofreciendo una experiencia adaptada principalmente a dispositivos móviles. - -Tecnologías clave: - -* Backend con ASP.NET Core y base de datos MySQL. -* Frontend en React (TypeScript) y mapas interactivos con MapLibre. -* Teselas vectoriales de OpenStreetMap con [Protomaps](https://protomaps.com/) para un mapa de alta calidad y buen rendimiento. -* Calculador de rutas autoalojado con [OSRM](https://project-osrm.org/) para optimización de rutas. - -Diseñada para autonomía y privacidad: - -* Infraestructura autoalojada (Ubuntu) sin dependencia de APIs comerciales. -* Optimización de rutas con motor de cálculo interno (OSRM). -* Panel de administración para gestión centralizada del patrimonio. - -## Características clave - -1. **Tecnologías autónomas**: Implementación sin dependencia de servicios externos (como Google Maps), garantizando privacidad del usuario y reducción de costos. - -2. **Gestión de recursos**: Solución personalizada para iconografía y capas de mapa, asegurando consistencia visual y eficiencia. - -3. **Planificación de rutas inteligente**: Sistema de optimización para senderismo basado en puntos patrimoniales, con énfasis en rendimiento y estabilidad. - -4. **Panel de administración**: Herramienta interna para gestión de contenido por personal no técnico. - - ---- - -Desarrollado en 2025 en colaboración con [Kendra](https://kendra.es/). diff --git a/src/data/portfolio/mientreno.mdx b/src/data/portfolio/mientreno.mdx deleted file mode 100644 index 79a40c6..0000000 --- a/src/data/portfolio/mientreno.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: "MiEntreno" -description: "Creación de un sitio web dinámico con PHP adaptado a móviles y accesible mediante QR en localizaciones físicas" -technologies: ["dotnet", "azure"] ---- - -Aplicación web para la gestión de entrenamientos deportivos, con una interfaz sencilla y fácil de usar. Desarrollado con ASP.NET Core, Razor Pages y SQL Server, y alojada en Azure App Service. - -La aplicación permite a los entrenadores personales crear y gestionar rutinas de entrenamiento para sus clientes, que pueden proporcionar _feedback_ sobre estos. Además, los clientes pueden ver sus rutinas de entrenamiento y registrar sus progresos en la aplicación. - -La principal complejidad que me encontré en este proyecto fue la implementación de los dos roles de usuario (entrenador y cliente) y la gestión de los permisos de acceso a las distintas partes de la aplicación, con filtrado para evitar mostrar datos potencialmente sensibles. - -Además, el tiempo de desarrollo era muy limitado, al tratarse del proyecto final de ciclo, en un plazo de 3 meses desde la concepción de la idea, redacción del proyecto y desarrollo de la aplicación. Podría considerarse un _MVP_ (Producto Mínimo Viable) de la idea original, con muchas funcionalidades que quedaron fuera del alcance de este proyecto. - -También, fue un "campo de pruebas" para mí, ya que fue el primer proyecto serio que desarrollé con ASP.NET Core, y donde también quise introducir el uso de emailing transaccional, colas de mensajes y otros conceptos más avanzados que no había utilizado antes en una aplicación real. - -El código fuente se puede encontrar en [GitHub](https://github.com/arielcostas/mientreno) bajo la licencia GNU Affero GPL v3.0. diff --git a/src/data/portfolio/order-extractor.mdx b/src/data/portfolio/order-extractor.mdx deleted file mode 100644 index dfdbd2e..0000000 --- a/src/data/portfolio/order-extractor.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: "Extractor de pedidos (2023-actualidad)" -description: "Extractor de pedidos de compra online para generar informes de ventas y rentabilidad" -technologies: ["java", "windows"] ---- - -Implementación de una aplicación de escritorio que extrae los datos sobre los pedidos on-line -de diversas plataformas (como WooCommerce, Amazon y Ebay) para almacenar de forma local y -centralizada. Además, cruza estos datos con la base de datos de productos en almacén y sus -precios de coste, para generar informes de Excel sobre los ingresos y costes por cada pedido, -así como calcular la rentabilidad de estos. - -Los principales problemas enfrentados en este proyecto fueron la diversidad de formatos de los -datos de entrada y la necesidad de mantener la aplicación actualizada con los cambios en las -plataformas de venta online; además de no contar con SDKs oficiales para estas plataformas, -teniendo que implementar llamadas HTTP a las API públicas, a veces con autenticación compleja -o firma de peticiones (como AWS Signature V4). - -Por otra parte, está la distribución, instalación y actualización de la aplicación en los -equipos del cliente, teniendo que generar un instalador MSI firmado a partir del Java compilado -y _shaded_ con sus dependencias mediante maven, y `jpackage` para generar el ejecutable nativo -de Windows. - -Trabajo inicial realizado en 2023 para un cliente de España, con mantenimiento hasta la actualidad. -En 2025 se comenzó un proyecto de migración a una aplicación cloud con más funcionalidades y que -soluciona dificultades como la sincronización de datos entre equipos y la generación de nuevos tipos -de informes. diff --git a/src/data/portfolio/qr-ponteareas.mdx b/src/data/portfolio/qr-ponteareas.mdx deleted file mode 100644 index 61449f3..0000000 --- a/src/data/portfolio/qr-ponteareas.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Museo a ceo aberto de Ponteareas" -description: "Creación de un sitio web dinámico con PHP adaptado a móviles y accesible mediante QR en localizaciones físicas" -technologies: ["php", "web"] -demoLink: "https://museocorpus.ponteareas.gal/?place=01-bugallal" ---- - -Creación de un sitio web dinámico con PHP adaptado a móviles y accesible mediante -QR en localizaciones físicas. Cada página muestra datos de la ubicación correspondiente -en tres idiomas (castellano, gallego e inglés), una galería de imágenes en 360º con -la biblioteca PanoLens y vídeos incrustados de YouTube. - -Desarrollado en 2021 en colaboración con [Kendra](https://kendra.es/). diff --git a/src/data/portfolio/qr-touro.mdx b/src/data/portfolio/qr-touro.mdx deleted file mode 100644 index 63a770e..0000000 --- a/src/data/portfolio/qr-touro.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "Punto de información turística en móviles" -description: "Creación de un sitio web dinámico con PHP adaptado a móviles y accesible mediante QR en localizaciones físicas" -technologies: ["typescript", "web"] -demoLink: "https://www.concellodetouro.com/qr-carteis/petroglifo.html" ---- - -Desarrollo de un generador estático en TypeScript para información turística del Concello de -Touro, A Coruña, con un sitio web dinámico adaptado a móviles. El sitio se accede mediante códigos QR -en localizaciones físicas, permitiendo a los usuarios obtener información sobre el patrimonio cultural -y natural de la zona. - -Desasrrollado en 2020, en colaboración con [Kendra](https://kendra.es/). diff --git a/src/data/portfolio/vigo-360.mdx b/src/data/portfolio/vigo-360.mdx deleted file mode 100644 index 2bebdf3..0000000 --- a/src/data/portfolio/vigo-360.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: "Vigo 360" -description: "Desarrollo y publicación de un blog sobre Vigo" -technologies: ["go", "mysql", "ubuntu"] -onlineLink: "https://vigo360.es" -githubLink: "https://github.com/arielcostas/vigo360" ---- - -Blog sobre Vigo y su entorno, orientado principalmente a hablar de movilidad y toponimia. Desarrollado en Go, con base de datos MySQL y desplegado sobre infraestructura propia. - -Opté por desarrollarlo de cero, en lugar de invertir mis esfuerzos en aprender a desarrollar sobre WordPress, por la necesidad de mantener una tonelada de plugins, un tema propio y código que no controlaba. Además, me permitió aprender Go y desplegar una aplicación en producción. - -Además de esto, ocasionalmente publico contenido en este blog sobre transporte público, movilidad y toponimia en Vigo y su entorno; sin fines de lucro y con un nivel de rigor que nos ha llevado a ser citados en medios de comunicación locales como un medio especializado en estos temas. - -El blog cuenta con un sistema publicaciones, comentarios, etiquetas y búsqueda (con Algolia), así como optimización para buscadores y redes sociales con etiquetas de OpenGraph, Twitter Cards y datos semánticos de Schema.org mediante JSON-LD. - -Durante 2025 se planea hacer una reescritura de la aplicación usando .NET para poder implementar nuevas funcionalidades, como procesamiento de fondo de ciertas tareas (generar miniaturas, indexar contenido, etc.) y mejorar la manteniabilidad del código, o búsqueda mediante vectores sin depender de terceros. diff --git a/src/data/portfolio/wp-consulting.mdx b/src/data/portfolio/wp-consulting.mdx deleted file mode 100644 index 0b6a35f..0000000 --- a/src/data/portfolio/wp-consulting.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Consultoría WordPress" -description: "Trabajos de mantenimiento, optimización y migración de sitios web WordPress y tiendas online WooCommerce" -technologies: ["php"] ---- - -Trabajos de mantenimiento, optimización y migración de sitios web WordPress y tiendas online WooCommerce. Incluyendo la instalación y configuración de plugins, temas y optimización de rendimiento y SEO. - -Trabajo bajo la premisa de reducir la dependencia de plugins y temas de terceros, y de mejorar la seguridad y rendimiento de los sitios web; además de usar herramientas optimizadas y sin limitaciones artificiales bajo muros de pago, en la medida de lo posible. diff --git a/src/layouts/HomePageLayout.astro b/src/layouts/HomePageLayout.astro index fb3cf34..ca009a5 100644 --- a/src/layouts/HomePageLayout.astro +++ b/src/layouts/HomePageLayout.astro @@ -24,56 +24,12 @@ const schema = { /> <h1>Inicio</h1> + <p> Te doy la bienvenida a mi web. Me llamo Ariel, y aquí encontrarás información sobre mí y mis proyectos. </p> - <h2>¿Quién soy?</h2> - - <p> - Soy un desarrollador de software con varios años de experiencia en el - sector, especializado en el desarrollo de aplicaciones web y la - administración de sistemas Cloud. Me apasiona la tecnología y disfruto - creando soluciones prácticas y eficientes para resolver problemas reales, - simplificando la vida y el trabajo de las personas. - </p> - - <p> - En mi tiempo libre, fuera de mi trabajo, me interesan temas como la - filosofía, la economía y las finanzas, así como el derecho y la política. Me - gusta aprender sobre cómo funcionan las cosas y cómo se relacionan entre sí, - y reflexionar sobre el impacto que tienen en nuestra sociedad y en nuestras - vidas cotidianas. - </p> - - <h2>¿Qué hago?</h2> - - <p> - Actualmente trabajo como desarrollador de software y administrador Cloud en - Estelaria Solutions, una empresa de tecnología en Vigo (España). Me encargo - de desarrollar aplicaciones web en PHP y desplegarlas en la nube de forma - segura y eficiente. - </p> - - <a href="/trajectory/">Conoce más sobre mi trayectoria profesional</a> - - <p> - También realizo ocasinalmente proyectos personales y freelance para clientes - que necesitan soluciones a medida o apoyo técnico en sus proyectos. Me gusta - trabajar con tecnologías modernas y aprender nuevas habilidades para mejorar - mi trabajo y ofrecer un mejor servicio a mis clientes. - </p> - - <p> - Si necesitas ayuda con un proyecto o quieres colaborar conmigo, no dudes en - ponerte en contacto conmigo a través de mi correo electrónico o mis redes - sociales. - </p> - - <a href="/portfolio/">Echa un vistazo a mi portfolio</a> - - <h2>Mis reflexiones</h2> <p> En mi blog comparto mis reflexiones, aprendizajes y experiencias sobre los temas que me interesan, además de hablar ocasionalmente sobre tecnología y diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 6e389e2..3aa2dd0 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,6 +1,4 @@ --- -import "@fontsource-variable/roboto"; -import "@fontsource-variable/roboto-mono"; import Header from "../partials/Header.astro"; import Footer from "../partials/Footer.astro"; @@ -137,8 +135,19 @@ const { title, description, empty } = Astro.props; background-color: color.adjust($accent, $alpha: -0.85); } + ul:has(li) { + padding-left: 1.25rem; + margin-top: 0; + margin-bottom: 1.5rem; + + display: flex; + flex-direction: column; + gap: 0.5rem; + } + li > time { - font-family: $monoFontStack; + font-size: 0.95rem; + user-select: none; } main { diff --git a/src/layouts/PortfolioListLayout.astro b/src/layouts/PortfolioListLayout.astro deleted file mode 100644 index b509bec..0000000 --- a/src/layouts/PortfolioListLayout.astro +++ /dev/null @@ -1,137 +0,0 @@ ---- -import t from "../i18n/es.json"; - -import Layout from "./Layout.astro"; -import PortfolioProject from "@/components/PortfolioProject.astro"; - -const schema = { - "@context": "https://schema.org", - "@type": "WebPage", - url: "https://www.costas.dev/portfolio", - headline: t.portfolioPage.headline, -}; ---- - -<Layout title={t.portfolioPage.title} description={t.portfolioPage.description}> - <script - is:inline - type="application/ld+json" - slot="head-jsonld" - set:html={JSON.stringify(schema)} - /> - - <h1>{t.portfolioPage.headline}</h1> - - <p set:html={t.portfolioPage.intro} /> - - <h2>{t.portfolioPage.freelanceTitle}</h2> - - <p>{t.portfolioPage.freelanceDesc}</p> - - <section> - <PortfolioProject - title="Mapa patrimonial de la Costa Ártabra" - summary="Desarrollo de una aplicación completa para el patrimonio cultural y militar de la Costa Ártabra, con un sistema CMS para el personal de la administración y una aplicación web progresiva (PWA) para visualizar el mapa y planificar rutas." - tags={["dotnet", "react", "mysql", "ubuntu"]} - detailsLink="/portfolio/mapa-ferrol/" - onlineLink="https://mapacostaartabramilitar.com" - /> - - <PortfolioProject - title="Extractor de pedidos venta online" - summary="Aplicación de escritorio que extrae los datos sobre los pedidos on-line de diversas plataformas (como WooCommerce, Amazon y Ebay)." - tags={["java", "windows"]} - detailsLink="/portfolio/order-extractor/" - /> - - <PortfolioProject - title="Museo a ceo aberto de Ponteareas" - summary="Desarrollo de visualización de contenido turístico para el Concello de Ponteareas. Incluye contenido en 360º e incrustación de vídeos de YouTube. Realizado en 2021." - tags={["php", "web"]} - detailsLink="/portfolio/qr-ponteareas/" - /> - - <PortfolioProject - title="QR Touro turístico" - summary="Desarrollo de un generador estático en TypeScript para información turística del Concello de Touro. Realizado en 2020." - tags={["typescript", "web"]} - detailsLink="/portfolio/qr-touro/" - /> - - <PortfolioProject - title="Consultoría WordPress" - summary="Trabajos de mantenimiento, optimización y migración de sitios web WordPress y tiendas online WooCommerce." - tags={["php", "wordpress"]} - detailsLink="/portfolio/wp-consulting/" - /> - </section> - - <h2>{t.portfolioPage.ownProjectsTitle}</h2> - - <p>{t.portfolioPage.ownProjectsDesc}</p> - - <section> - <PortfolioProject - title="Web personal" - summary="Desarrollado con Astro, un generador de sitios web estáticos que permite escribir contenido en Markdown y publicar en la web con un rendimiento excelente. Desplegado via GitHub Actions en mi servidor." - tags={["astro", "github", "ubuntu"]} - githubLink="https://github.com/arielcostas/costasdev" - /> - - <PortfolioProject - title="MiEntreno (proyecto fin de ciclo)" - summary="Aplicación web para la gestión de entrenamientos deportivos, con una interfaz sencilla y fácil de usar. Desarrollado con ASP.NET Core, Razor Pages y SQL Server." - tags={["dotnet", "azure"]} - githubLink="https://github.com/arielcostas/mientreno" - detailsLink="/portfolio/mientreno/" - /> - - <PortfolioProject - title="Vigo 360" - summary="Blog sobre Vigo y su entorno, orientado principalmente a hablar de movilidad y toponimia. Desarrollado en Go, con base de datos MySQL y desplegado sobre VPS administrado por mí mismo." - tags={["go", "mysql", "ubuntu"]} - githubLink="https://github.com/arielcostas/vigo360" - detailsLink="/portfolio/vigo-360/" - onlineLink="https://vigo360.es" - /> - </section> - - <div class="trajectory-summary"> - <h2>{t.portfolioPage.trajectoryTitle}</h2> - <p>{t.portfolioPage.trajectorySummary}</p> - <a href="/trajectory" class="cta-link">{t.portfolioPage.viewTrajectory} →</a> - </div> -</Layout> - -<style> - section { - display: grid; - grid-template-columns: repeat(auto-fill,minmax(350px,1fr)); - gap: 1.5rem; - } - - .trajectory-summary { - margin-top: 3rem; - background-color: #FFFFFF; - border-radius: 8px; - padding: 1.5rem; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.08); - border-left: 4px solid hsl(209, 94%, 42%); - } - - .trajectory-summary h2 { - margin-top: 0; - } - - .cta-link { - display: inline-block; - margin-top: 0.5rem; - font-weight: 600; - text-decoration: none; - color: hsl(215, 90%, 30%); - } - - .cta-link:hover { - text-decoration: underline; - } -</style> diff --git a/src/layouts/PortfolioSingleLayout.astro b/src/layouts/PortfolioSingleLayout.astro deleted file mode 100644 index 1922e16..0000000 --- a/src/layouts/PortfolioSingleLayout.astro +++ /dev/null @@ -1,370 +0,0 @@ ---- -import Layout from "@/layouts/Layout.astro"; -import { render } from "astro:content"; -import TechnologyBadge from "@/components/TechnologyBadge.astro"; -import type { InferEntrySchema } from "astro:content"; -import { Icon } from "astro-icon/components"; - -interface Props { - entry: any; -} - -const { entry } = Astro.props; -const data = entry.data as InferEntrySchema<"portfolio">; -const { Content } = await render(entry); ---- - -<Layout title={data.title} description={data.description}> - <a id="link-back" href="/portfolio/"> - <svg - xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - stroke-width="2" - stroke-linecap="round" - stroke-linejoin="round" - class="w-6 h-6 inline-block mr-2" - > - <polyline points="15 18 9 12 15 6"></polyline> - </svg> - Volver al portfolio - </a> - - <h1>{data.title}</h1> - - <small>Hecho con {data.technologies.map((technology: string) => ( - <TechnologyBadge size="small" code={technology} /> - ))}</small> - - <Content /> - - <div class="project-links"> - {data.githubLink && <a href={data.githubLink} target="_blank" rel="noopener noreferrer" class="project-link"> - <Icon name="tabler:brand-github" class="link-icon"/> - <span>Código en GitHub</span> - </a>} - - {data.onlineLink && <a href={data.onlineLink} target="_blank" rel="noopener noreferrer" class="project-link"> - <Icon name="tabler:external-link" class="link-icon"/> - <span>Ver en línea</span> - </a>} - - {data.demoLink && <a href={data.demoLink} target="_blank" rel="noopener noreferrer" class="project-link"> - <Icon name="tabler:device-laptop" class="link-icon"/> - <span>Ver demo</span> - </a>} - </div> - - {data.images.length > 0 && ( - <h2>Galería</h2> - <section id="project-images"> - {data.images.map((image) => ( - <a href={`${image.src}.png`} target="_blank" rel="noopener noreferrer"> - <picture> - <source - srcset={`${image.src}.webp`} - type="image/webp" - /> - <img src={`${image.src}.png`} alt={image.alt} loading="lazy" /> - </picture> - </a> - ))} - </section> - )} - - <dialog id="largeimage-dialog"> - <div id="largeimage-dialogcontainer"> - <button class="largeimage__nav" aria-label="Previous image" id="largeimage__nav-left"> - <Icon name="tabler:chevron-left" /> - </button> - <div id="largeimage-imagecontainer"> - <img id="largeimage-image" alt="Some alt" /> - </div> - <div id="largeimage-caption">Some caption</div> - <button class="largeimage__nav" aria-label="Next image" id="largeimage__nav-right"> - <Icon name="tabler:chevron-right" /> - </button> - <button id="largeimage-close"><Icon name="tabler:x" /></button> - </div> - </dialog> - -</Layout> - -<script> - document.addEventListener("DOMContentLoaded", () => { - const dialog = document.getElementById("largeimage-dialog") as HTMLDialogElement; - const closeButton = document.getElementById("largeimage-close") as HTMLButtonElement; - const imageElement = document.getElementById("largeimage-image") as HTMLImageElement; - const captionElement = document.getElementById("largeimage-caption") as HTMLDivElement; - const navLeft = document.getElementById("largeimage__nav-left") as HTMLButtonElement; - const navRight = document.getElementById("largeimage__nav-right") as HTMLButtonElement; - const imageLinks = Array.from(document.querySelectorAll("#project-images a")) as HTMLAnchorElement[]; - - const images = imageLinks.map(link => { - const img = link.querySelector("img"); - return { - src: img?.src, - alt: img?.alt - }; - }); - let currentIndex = 0; - - function showImage(index: number) { - if (index < 0 || index >= images.length) return; - currentIndex = index; - const { src, alt } = images[currentIndex]; - if (!imageElement || !captionElement) { - console.error("Image or caption element not found"); - return; - } - imageElement.src = src!; - imageElement.alt = alt!; - captionElement.textContent = alt || "Imagen del proyecto"; - } - - if (!dialog || !closeButton || !imageElement || !captionElement) { - console.error("Dialog or elements not found"); - return; - } - - imageLinks.forEach((link, idx) => { - link.addEventListener("click", (e) => { - e.preventDefault(); - showImage(idx); - dialog.showModal(); - }); - }); - - closeButton.addEventListener("click", () => { - dialog.close(); - }); - - if (navLeft) { - navLeft.addEventListener("click", (e) => { - e.preventDefault(); - showImage((currentIndex - 1 + images.length) % images.length); - }); - } - if (navRight) { - navRight.addEventListener("click", (e) => { - e.preventDefault(); - showImage((currentIndex + 1) % images.length); - }); - } - - dialog.addEventListener("keydown", (e) => { - if (e.key === "ArrowLeft") { - showImage((currentIndex - 1 + images.length) % images.length); - } else if (e.key === "ArrowRight") { - showImage((currentIndex + 1) % images.length); - } else if (e.key === "Escape") { - dialog.close(); - } - }); - - dialog.addEventListener("shown", () => { - dialog.focus(); - }); - - dialog.addEventListener("open", () => { - dialog.focus(); - }); - - dialog.addEventListener("click", (e) => { - if (e.target === dialog) { - dialog.close(); - } - }); - }); -</script> - -<style> - a#link-back { - display: inline-flex; - align-items: center; - gap: 0.5rem; - text-decoration: none; - text-transform: uppercase; - transition: color 0.2s ease-in-out; - } - - a#link-back svg { - height: 1em; - } - - #project-images { - display: flex; - flex-direction: row; - gap: 1.5rem; - margin: 1.5rem 0 2rem; - overflow-x: auto; - padding-bottom: 1rem; - -webkit-overflow-scrolling: touch; - scrollbar-width: thin; - } - - #project-images a { - display: block; - flex: 0 0 auto; - box-shadow: none; - text-decoration: none; - padding: 0; - } - - #project-images picture { - display: block; - overflow: hidden; - border-radius: 0.25rem; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - height: max(20vh, 400px); - } - - #project-images img { - height: 100%; - object-fit: cover; - } - - #project-images a { - position: relative; - } - - .project-links { - display: flex; - gap: 1rem; - flex-wrap: wrap; - margin: 1.5rem 0 2.5rem; - } - - .project-link { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1.25rem; - border-radius: 2rem; - background-color: #f3f4f6; - color: #333; - font-weight: 600; - text-decoration: none; - box-shadow: none; - transition: all 0.2s ease-in-out; - - &:hover { - background-color: #e5e7eb; - transform: translateY(-2px); - box-shadow: none; - } - - &:focus { - outline: 2px solid $secondary; - background-color: #e5e7eb; - box-shadow: none; - } - - .link-icon { - font-size: 1.2rem; - } - } - - #largeimage-dialog { - padding: 0; - border: none; - background: transparent; - max-width: 100vw; - max-height: 100vh; - margin: auto; - } - - #largeimage-dialog::backdrop { - background: #000c; - } - - #largeimage-dialogcontainer { - position: relative; - display: flex; - flex-direction: column; - gap: 1rem; - height: 100%; - padding: 1.5rem; - } - - #largeimage-imagecontainer { - max-width: 100%; - max-height: calc(100vh - 2rem); - overflow: hidden; - position: relative; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - - #largeimage-image { - object-fit: cover; - max-width: 100%; - max-height: calc(100vh - 9rem); - - border-radius: 0.5rem; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - } - - #largeimage-caption { - position: static; - background: #FFFC; - color: #000; - font-size: 1rem; - text-align: center; - border-radius: 0.5rem; - padding: 0.75rem 1rem; - max-width: 100%; - } - - #largeimage-close { - position: absolute; - top: 1rem; - right: 1rem; - width: 2rem; - height: 2rem; - border: none; - background: #fffc; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - color: #000; - transition: background-color .2s; - } - - .largeimage__nav { - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 2.5rem; - height: 2.5rem; - border: none; - background: #fffc; - border-radius: 50%; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 2rem; - z-index: 2; - color: #000; - transition: background-color .2s; - } - - #largeimage__nav-left { - left: 1rem; - } - #largeimage__nav-right { - right: 1rem; - } - - .largeimage__nav:hover, - #largeimage-close:hover { - background-color: #FFFFFF; - } -</style> diff --git a/src/pages/contact.astro b/src/pages/contact.astro index 9cb58a6..bd5984e 100644 --- a/src/pages/contact.astro +++ b/src/pages/contact.astro @@ -25,51 +25,31 @@ const schema = { <p> La forma más sencilla de contactar conmigo es a través de mi dirección de correo electrónico: - <a href="#" id="email-addr">ACTIVA JAVASCRIPT PARA VER ESTO</a>. También - puedes usar <a href="https://wa.me/message/W7T7L4EZAELQI1">WhatsApp</a> con el - número de teléfono - <a href="#" id="phone-number">ACTIVA JAVASCRIPT PARA VER ESTO</a>. + <a href="#" id="email-addr">ACTIVA JAVASCRIPT PARA VER ESTO</a>. </p> - <p>También puedes encontrarme en algunas redes sociales:</p> - - <dl> - <dt>GitHub</dt> - <dd><a href="https://github.com/arielcostas">@arielcostas</a></dd> - <dt>LinkedIn</dt> - <dd> - <a href="https://www.linkedin.com/in/ariel-costas/">/in/ariel-costas</a> - </dd> - <dt>BlueSky</dt> - <dd><a href="https://bsky.app/profile/costas.dev">@costas.dev</a></dd> - </dl> + <p> + También puedes encontrarme en GitHub: + <a href="https://github.com/arielcostas">@arielcostas</a> + </p> </Layout> <script> const encryptedEmail = "LygNLiMmFRo/GlQZaFIWBA=="; - const encryptedPhoneNumber = "ZWlQfX1QT0Z+XgVd"; const key = "NZdKOfvuLn5jF6sryF0Q"; const emailAddrLink = document.getElementById( "email-addr", ) as HTMLAnchorElement; - const phoneNumberLink = document.getElementById( - "phone-number", - ) as HTMLAnchorElement; - (() => { - if (emailAddrLink == null || phoneNumberLink == null) { + if (emailAddrLink == null) { return; } const emailAddress = xorData(encryptedEmail, key); - const phoneNumber = xorData(encryptedPhoneNumber, key); emailAddrLink.href = `mailto:${emailAddress}`; emailAddrLink.textContent = emailAddress; - - phoneNumberLink.href = `tel:${phoneNumber}`; - phoneNumberLink.textContent = phoneNumber; })(); function xorData(data: string, key: string): string { diff --git a/src/pages/portfolio/[id].astro b/src/pages/portfolio/[id].astro deleted file mode 100644 index 7424b91..0000000 --- a/src/pages/portfolio/[id].astro +++ /dev/null @@ -1,17 +0,0 @@ ---- -import type { GetStaticPaths } from "astro"; -import { getCollection } from "astro:content"; -import PortfolioSingleLayout from "@/layouts/PortfolioSingleLayout.astro"; - -export const getStaticPaths: GetStaticPaths = async () => { - const entries = await getCollection("portfolio"); - return entries.map((entry: any) => ({ - params: { id: entry.id }, - props: { entry }, - })); -}; - -const { entry } = Astro.props; ---- - -<PortfolioSingleLayout entry={entry} /> diff --git a/src/pages/portfolio/index.astro b/src/pages/portfolio/index.astro deleted file mode 100644 index c12ab23..0000000 --- a/src/pages/portfolio/index.astro +++ /dev/null @@ -1,5 +0,0 @@ ---- -import PortfolioListLayout from "@/layouts/PortfolioListLayout.astro"; ---- - -<PortfolioListLayout /> diff --git a/src/pages/trajectory.astro b/src/pages/trajectory.astro deleted file mode 100644 index b1366e6..0000000 --- a/src/pages/trajectory.astro +++ /dev/null @@ -1,5 +0,0 @@ ---- -import TrajectoryPageLayout from "../layouts/TrajectoryPageLayout.astro"; ---- - -<TrajectoryPageLayout /> diff --git a/src/partials/Header.astro b/src/partials/Header.astro index e8db9d5..5fc8010 100644 --- a/src/partials/Header.astro +++ b/src/partials/Header.astro @@ -17,8 +17,6 @@ import t from "../i18n/es.json"; <nav class="nav-links" id="nav-links" aria-label="Main navigation"> <a href="/">{t.header.home}</a> - <a href="/trajectory/">{t.header.trajectory}</a> - <a href="/portfolio/">{t.header.portfolio}</a> <a href="/blog/">{t.header.blog}</a> <a href="/contact/">{t.header.contact}</a> </nav> @@ -83,7 +81,8 @@ import t from "../i18n/es.json"; .brand-link { font-size: 1.8rem; - font-weight: 800; + font-weight: 700; + font-variation-settings: "wdth" 87.5,"wght" 700,"GRAD" 150; text-decoration: none; color: $accent; diff --git a/styles/_variables.scss b/styles/_variables.scss index 2defb13..0678cd4 100644 --- a/styles/_variables.scss +++ b/styles/_variables.scss @@ -1,5 +1,5 @@ -$titleFontStack: "Roboto Variable", system-ui, "Liberation Sans", "Arial", "Helvetica", sans-serif; -$mainFontStack: "Roboto Variable", system-ui, "Liberation Sans", "Arial", "Helvetica", sans-serif; +$titleFontStack: "Roboto Flex", system-ui, "Liberation Sans", "Arial", "Helvetica", sans-serif; +$mainFontStack: "Roboto Flex", system-ui, "Liberation Sans", "Arial", "Helvetica", sans-serif; $monoFontStack: "Roboto Mono Variable", "Fira Code", "Consolas", monospace; $accent: hsl(209, 94%, 42%); diff --git a/styles/shared.scss b/styles/shared.scss index 053350f..ef9379c 100644 --- a/styles/shared.scss +++ b/styles/shared.scss @@ -1,6 +1,14 @@ @use "./variables" as *; @use "./alert"; +@font-face { + font-display: swap; + font-family: 'Roboto Flex'; + font-style: normal; + font-weight: 100 900; + src: url('/fonts/robotoflex.woff2') format('woff2'); +} + body { font-family: $mainFontStack; line-height: 1.65; @@ -17,12 +25,14 @@ h1 { @extend %heading; font-size: 3.25rem; line-height: 1.3; + } h2 { @extend %heading; font-size: 2.5rem; line-height: 1.55; + font-variation-settings: "wdth" 87.5,"wght" 700,"GRAD" 150 } h3 { @@ -36,3 +46,7 @@ h4 { font-size: 1.25rem; line-height: 1.55; } + +p { + font-variation-settings: "GRAD" -15,"wdth" 112.5; +} |
