Skip to main content

Portfolio Showcase Feature

The portfolio showcase is the core feature of the website, displaying Juan Roccia’s work through an elegant, responsive project gallery powered by Astro’s content collections.

Overview

The portfolio showcase consists of two main pages:
  1. Home Page (/index.astro) - Featured projects (4 most recent)
  2. Work Page (/work.astro) - Complete project gallery (all projects)
  3. Individual Project Pages (/work/[slug].astro) - Detailed project information
All portfolio projects are sourced from Astro content collections in /src/content/work/, ensuring type-safe content management with automatic validation.
The home page showcases the four most recent projects, sorted by publication date:
---
import { getCollection } from 'astro:content';
import PortfolioPreview from '../components/PortfolioPreview.astro';
import Grid from '../components/Grid.astro';

// Content Fetching: List four most recent work projects
const projects = (await getCollection('work'))
  .sort((a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf())
  .slice(0, 4);
---

<section class="section with-background with-cta">
  <header class="section-header stack gap-2 lg:gap-4">
    <h3>Trabajos Destacados</h3>
    <p>Estos son algunos de mis trabajos más recientes.</p>
  </header>

  <div class="gallery">
    <Grid variant="offset">
      {
        projects.map((project) => (
          <li>
            <PortfolioPreview project={project} />
          </li>
        ))
      }
    </Grid>
  </div>

  <div class="cta">
    <CallToAction href="/work/">
      View All
      <Icon icon="arrow-right" size="1.2em" />
    </CallToAction>
  </div>
</section>

Key Features:

Automatic Sorting

Projects are automatically sorted by publishDate in descending order (newest first)

Limited Display

Only the 4 most recent projects appear on the home page via .slice(0, 4)

CTA Integration

“View All” button directs users to the complete portfolio on /work

Offset Grid

Uses variant="offset" for a visually striking staggered layout
The dedicated work page displays all portfolio projects without limitation:
---
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import PortfolioPreview from '../components/PortfolioPreview.astro';
import Hero from '../components/Hero.astro';
import Grid from '../components/Grid.astro';
import ContactCTA from '../components/ContactCTA.astro';

const projects = (await getCollection('work')).sort(
  (a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
);
---

<BaseLayout
  title="My Work | Juan Roccia"
  description="Learn about Juan Roccia's most recent projects"
>
  <div class="stack gap-20">
    <main class="wrapper stack gap-8">
      <Hero
        title="Mi Trabajo"
        tagline="Podes ver los proyectos más recientes a continuación para hacerte una idea de mi talento y experiencia."
        align="start"
      />
      <Grid variant="offset">
        {
          projects.map((project) => (
            <li>
              <PortfolioPreview project={project} />
            </li>
          ))
        }
      </Grid>
    </main>
    <ContactCTA />
  </div>
</BaseLayout>

Project Preview Component

The PortfolioPreview component renders individual project cards with hover effects and view transitions:
---
import type { CollectionEntry } from 'astro:content';

interface Props {
  project: CollectionEntry<'work'>;
}

const { data, slug } = Astro.props.project;
---

<a class="card" href={`/work/${slug}`}>
  <span class="title" transition:name={`title-${slug}`}>
    {data.title}
  </span>
  <img 
    src={data.img} 
    transition:name={`img-${slug}`} 
    alt={data.img_alt || ''} 
    loading="lazy" 
    decoding="async" 
  />
</a>

Component Features:

1

Type Safety

Uses TypeScript CollectionEntry<'work'> type for full type checking and autocomplete
2

View Transitions

Implements Astro’s view transitions API with transition:name for smooth page navigation
3

Lazy Loading

Images use loading="lazy" and decoding="async" for optimal performance
4

Responsive Cards

Cards adjust height and border radius based on viewport size

Grid Layout System

The portfolio uses a custom Grid component with two variants:
  • Single column layout
  • Standard grid with 1rem gap
  • No vertical offset
@media (min-width: 50em) {
  .grid {
    grid-template-columns: 1fr 1fr;
    gap: 4rem;
  }

  .grid.offset {
    --row-offset: 7.5rem;
    padding-bottom: var(--row-offset);
  }

  /* Shift first item in each row vertically to create staggered effect */
  .grid.offset > :global(:nth-child(odd)) {
    transform: translateY(var(--row-offset));
  }

  /* If last row contains only one item, display it in the second column */
  .grid.offset > :global(:last-child:nth-child(odd)) {
    grid-column: 2 / 3;
    transform: none;
  }
}

Small Variant (Funding Options)

Used for displaying funding options on the home page:
  • Mobile: 2-column grid with 1.5rem gap
  • Desktop: Flexbox layout with wrapping, centered items
  • Last odd item spans both columns on mobile

Project Metadata & Tags

Each project includes rich metadata for categorization and filtering:
---
title: AudioGPT
publishDate: 2024-03-10 00:00:00
img: /assets/stock-2.jpg
img_alt: AudioGPT application interface
description: |
  Aplicación de inteligencia artificial que convierte texto a voz con diversas opciones de personalización y control.
tags:
  - AI
  - Audio Generation
  - Web App
  - Speech Synthesis
---

## AudioGPT: Generación de voz avanzada con IA

Este proyecto combina tecnologías de procesamiento de lenguaje natural y síntesis de voz...

Metadata Fields:

FieldTypePurpose
titlestringProject name displayed on cards
publishDatedateUsed for sorting projects chronologically
imgstringPath to project preview image
img_altstring (optional)Alt text for accessibility
descriptionstringBrief project summary
tagsstring[]Categorization and technology tags
The publishDate field is coerced to a Date object automatically by Zod schema validation, ensuring consistent date handling across the application.

Dynamic Routing

Individual project pages are generated automatically using Astro’s dynamic routing:
/src/pages/work/[...slug].astro
This creates a unique page for each project at /work/[project-name] with:
  • Full project description and details
  • Technology stack and implementation notes
  • High-quality project images
  • Related tags and metadata

Visual Design Elements

Card Styling

Gradient Backgrounds

Cards use var(--gradient-subtle) for a sophisticated visual effect

Hover Effects

Box shadow transitions on hover provide interactive feedback

Image Overlays

Project titles overlay images with semi-transparent backgrounds

Responsive Sizing

Cards scale from 11rem on mobile to 22rem on desktop

Section Backgrounds

The portfolio section features a sophisticated background system:
.with-background::before {
  --hero-bg: var(--bg-image-subtle-2);
  content: '';
  position: absolute;
  pointer-events: none;
  left: 50%;
  width: 100vw;
  aspect-ratio: calc(2.25 / var(--bg-scale));
  top: 0;
  transform: translateY(-75%) translateX(-50%);
  background:
    url('/assets/backgrounds/noise.png') top center/220px repeat,
    var(--hero-bg) center center / var(--bg-gradient-size) no-repeat,
    var(--gray-999);
  background-blend-mode: overlay, normal, normal, normal;
  mix-blend-mode: var(--bg-blend-mode);
  z-index: -1;
}

Performance Optimizations

1

Static Generation

All project pages are pre-rendered at build time for instant loading
2

Lazy Loading

Images load only when entering the viewport, reducing initial page weight
3

Async Decoding

Browser decodes images asynchronously without blocking the main thread
4

Optimized Sorting

Projects sorted once at build time, not on each page load
The portfolio showcase demonstrates Astro’s strengths: type-safe content management, zero-JS by default, and excellent performance through static site generation.

Build docs developers (and LLMs) love