AstroPortfolio

wavival.dev — Astro Portfolio Template

By wavival

Valentina Ramírez's personal portfolio website v4, built with Astro and TypeScript, using a custom design system, supporting Spanish and English, light and dark themes, and accessibility.

Explore the design

Compare the page layouts and mobile experience. Open any screenshot for a closer look.

3 previews
Desktop homepage1 / 3
Desktop captureTop of page

Actual screenshot captured by the server browser.

All pages & views 3

Screenshots show visual appearance; interactive behavior may differ.

Recorded 2026-10-04View source

What stands out

A quick read of the recorded project description.

01

Astro-based static site with a Vercel Function for quote submissions

02

Bilingual routing structure with Spanish default root path and English /en/ mirror

03

Custom design system: single-column editorial layout, Raleway and Poppins fonts, 1px dividers, default dark theme

04

Layout.astro uniformly manages canonical links, hreflang, Open Graph, Twitter Card, and JSON-LD

05

Accessibility measures include skip links, keyboard-safe mobile menu, 44px icon controls, and a single h1

06

Quality check chain consisting of Playwright, Lighthouse CI, linkinator, ESLint, and Prettier

Template Details

This is the 4th version of Valentina Ramírez's personal portfolio, built with Astro, primarily static pages, and includes a Vercel Function for quote submissions. The site's default language is Spanish (root path), with an English mirror under /en/, covering pages such as home, projects, services, quote, about, contact, tools, and privacy. Project content is centrally defined in src/data/projects.ts and adapted for localized rendering via projectView.ts.

Regarding the design system, it uses a single-column editorial layout, Raleway 800 display font and Poppins body font, 1px dividers instead of shadow cards, a default dark theme with .dark class and localStorage persistence. Style tokens are in src/styles/tokens.css, and component classes are in src/styles/utilities.css.

SEO and accessibility are uniformly provided by Layout.astro with basic metadata, including canonical links, es/en/x-default hreflang, Open Graph and Twitter Card, JSON-LD structured data, sitemap and robots metadata, and provides public/llms.txt and llms-full.txt for AI discovery. Accessibility measures include skip links, localized status labels, keyboard-safe mobile menu, native details expansion, 44px icon control targets, and a single visible h1 per route.

The project includes quality tools such as Playwright browser tests, Lighthouse CI, link checking, ESLint and Prettier, and is configured with Husky pre-commit hooks and CI workflows. Quote submission relies on Brevo transactional email service, requires setting BREVO_API_KEY in Vercel, and must remain server-side only. Umami analytics is optional and only enabled when both PUBLIC_UMAMI_* variables are configured.

AstroPortfolio

Is it right for your project?

Use Cases

  • Individual developers or designers building a bilingual portfolio website
  • Sites that require static generation with a small amount of server-side functionality (such as a contact form)
  • Front-end projects looking to reference a custom design system and light/dark theme implementation
  • Scenarios requiring SEO metadata, structured data, and AI discovery files for the site

Suitable For

  • Front-end developers familiar with Astro and TypeScript
  • Individual users needing a Spanish-English bilingual portfolio template
  • Site builders focused on accessibility and basic SEO configuration

Getting started & customization

Use these recorded setup notes alongside the repository instructions for your chosen version.

  1. 1

    Requires Node 22.x and npm; after cloning, run nvm use, npm ci, copy .env.example to .env, and run npm run dev

  2. 2

    Umami analytics variables are optional and only enabled when both PUBLIC_UMAMI_SRC and PUBLIC_UMAMI_ID are set

  3. 3

    Quote submission requires configuring BREVO_API_KEY in Vercel, and it must remain server-side only, without the PUBLIC_ prefix

  4. 4

    In Brevo, [email protected] must be verified as a sender

  5. 5

    When modifying pages or slugs, the slug mapping in src/i18n/utils.ts must be updated accordingly

Read repository instructions

License & source material

Usage conditions & limitations

Free to use · keep license notice

This entry is the free version under the MIT license in the public repository; the free scope is the code of this version and does not imply that external services or third-party assets are free.

Must comply with when using

  • Free use, modification, commercial use, and distribution of this version's code are permitted.
  • When distributing copies or substantial portions of the code, retain the original author's copyright notice and the full MIT license notice.
  • The code is provided as-is; warranties and limitations of liability are governed by the original license and applicable law.

Additional costs & items to verify

  • Demo images, fonts, icons, and other third-party assets must be checked against their respective licenses and cannot be judged solely by the code's MIT license.
  • External services such as domain, hosting, database, payment, or AI may incur additional charges.
  • The quote submission feature relies on Brevo transactional email service, requires configuring BREVO_API_KEY in Vercel, and this key must be used server-side only, without the PUBLIC_ prefix.
  • Before using the quote submission feature, [email protected] must be verified as a sender in Brevo, otherwise emails cannot be sent properly.
  • Umami analytics and Core Web Vitals reporting are optional features, only enabled when both PUBLIC_UMAMI_SRC and PUBLIC_UMAMI_ID variables are configured.
  • The Vercel Microfrontends contract reserves the /nullbreach and /nullbreach/:path* paths for the independent NullBreach project; this portfolio owns only the remaining paths, and path ownership must be noted during deployment.
  • In the branch model, merging from stg to main is restricted to the repository owner; other collaborators cannot directly complete this production release merge.
  • Version releases require manually running the release.yml workflow to tag and publish, not an automatically triggered process.
  • The commit message hook will additionally run the script if an executable file exists at ~/.claude/git-hooks/commit-msg on the developer's local machine, which is local environment-dependent behavior.
View Original Text Basis

Permission is hereby granted, free of charge

Original License Text

`BREVO_API_KEY` is required in Vercel for quote delivery and must remain server-only.

Original README

`[email protected]` must be a verified sender in Brevo.

Original README

The Umami values are optional. Analytics and Core Web Vitals reporting are emitted only when both are set.

Original README

The Vercel Microfrontends contract reserves `/nullbreach` and `/nullbreach/:path*` for the independent NullBreach project. This portfolio owns all remaining paths.

Original README

`stg` to `main` is merged only by the owner.

Original README

`release.yml` is run by hand to tag and publish a version

Original README

`commit-msg` also runs `~/.claude/git-hooks/commit-msg` when that executable exists on the developer machine.

Original README
https://github.com/wavival/wavival.dev/blob/dev/LICENSEhttps://github.com/wavival/wavival.dev/blob/dev/README.md

Compiled by DeepSeek based on repository license and README ·2026-10-04. Results only cover the materials read; dependencies, images, fonts, and subsequent term changes are subject to their respective original texts.

Project checks & description sources

Template Information

Source platforms
GitHub
Documentation and maintenance reference score
More documented · 80 / 100
Technologies
Astro
Website type
Portfolio
How this score is calculated

Our heuristic uses license records (up to 30), a demo link (12), a preview (10), stars (15), recorded maintenance (15), description completeness (8), feature records (5) and classification data (5). It is not a code, security or performance test, or a Google score. A link does not establish tested functionality.

What to verify for this project

Repository update recorded
2026-10-04
Screenshot last checked
2026-10-04

A demo link is listed. Check it with your own content and viewport sizes; its presence does not confirm that forms, authentication or payments work.

The catalog identifies Astro as the primary stack. Check the actual dependency manifest and lockfile for versions and hosting requirements. These notes use recorded metadata, not a code audit or a deployment test.

Use a selection guide →

How this description was prepared

AI-assisted description based on catalog records and repository material. Repository authors are not automatically the reviewers of this description.

Review declaration: [email protected] · 2026-10-04

Inspect the recorded source material

SHA-256: 53399952d75dd09bf336a8b804323eed8abf0d5c3d3b5afa388e89e957e38be3

2026-10-04T17:13:53.756Z

Source file / repository
<h1 align="left">
  <img src="assets/logo-w.png" width="48" valign="middle" alt="logo-w">
  Valentina Ramírez · Portfolio v4
</h1>

![Portfolio banner](assets/banner.png)

[![Portfolio](https://img.shields.io/badge/Portfolio-wavival.dev-1565c0?style=for-the-badge&logo=vercel&logoColor=white)](https://www.wavival.dev)
[![Blog](https://img.shields.io/badge/Blog-blog.luminaw.co-1565c0?style=for-the-badge&logo=hashnode&logoColor=white)](https://blog.luminaw.co/)
[![Lúmina W](https://img.shields.io/badge/Lúmina%20W-luminaw.co-1565c0?style=for-the-badge&logo=google-chrome&logoColor=white)](https://luminaw.co/)

> Version 4 of the personal portfolio for Valentina Ramírez, Full Stack developer focused on backend and AI, and founder of [Lúmina W](https://luminaw.co/). It is a bilingual Astro site, deployed on Vercel, with a serverless quote-delivery function.

> Last updated: 2026-10-04

## Contents

- [Local development](#local-development)
  - [Requirements](#requirements)
  - [Environment variables](#environment-variables)
  - [Commands](#commands)
- [Stack](#stack)
- [Architecture](#architecture)
  - [Project structure](#project-structure)
- [Routes and internationalization](#routes-and-internationalization)
  - [Locale routes](#locale-routes)
  - [Microfrontends](#microfrontends)
- [Design system](#design-system)
  - [Principles](#principles)
- [SEO, discovery and accessibility](#seo-discovery-and-accessibility)
  - [SEO and discovery](#seo-and-discovery)
  - [Accessibility](#accessibility)
- [Testing and quality](#testing-and-quality)
  - [Local validation](#local-validation)
  - [Local hooks](#local-hooks)
  - [CI gates](#ci-gates)
- [Delivery flow](#delivery-flow)
  - [Branch model](#branch-model)
- [Deployment](#deployment)
  - [Vercel](#vercel)
- [Repository documentation](#repository-documentation)
- [License](#license)
- [Contact](#contact)

## Local development

### Requirements

- Node `22.x`, pinned in [`.nvmrc`](./.nvmrc)
- npm

```bash
git clone [email protected]:wavival/wavival.dev.git
cd wavival.dev
nvm use
npm ci
cp .env.example .env
npm run dev
```

The development server runs at `http://localhost:4321`.

### Environment variables

The Umami values are optional. Analytics and Core Web Vitals reporting are emitted only when both are set. `BREVO_API_KEY` is required in Vercel for quote delivery and must remain server-only.

| Variable           | Read by                  | Required                               | Purpose                                                      |
| ------------------ | ------------------------ | -------------------------------------- | ------------------------------------------------------------ |
| `PUBLIC_UMAMI_SRC` | Build (`Layout.astro`)   | Optional, both Umami variables or none | Umami script URL.                                            |
| `PUBLIC_UMAMI_ID`  | Build (`Layout.astro`)   | Optional, both Umami variables or none | Umami website ID.                                            |
| `BREVO_API_KEY`    | Runtime (`api/quote.ts`) | Required in Vercel for quotes          | Brevo transactional email key. Server-only, never `PUBLIC_`. |

Set the three variables in the Vercel project settings, not in the repository. The `PUBLIC_` ones are read at build time, so a change needs a new deployment. `[email protected]` must be a verified sender in Brevo. See [`.env.example`](./.env.example) for the local template.

### Commands

| Command                | Purpose                                                         |
| ---------------------- | --------------------------------------------------------------- |
| `npm run dev`          | Starts Astro with HMR on port 4321.                             |
| `npm run build`        | Builds the static site into `dist/`.                            |
| `npm run preview`      | Serves the production build locally.                            |
| `npm run check`        | Runs Astro diagnostics and type checks.                         |
| `npm run lint`         | Runs ESLint.                                                    |
| `npm run lint:fix`     | Runs ESLint and applies the safe fixes.                         |
| `npm run format`       | Formats the repository with Prettier.                           |
| `npm run format:check` | Checks Prettier formatting.                                     |
| `npm test`             | Runs Playwright against a production preview on port 4329.      |
| `npm run test:ui`      | Opens the Playwright UI runner.                                 |
| `npm run test:install` | Installs the Chromium build Playwright needs.                   |
| `npm run commitlint`   | Checks a commit message against the Conventional Commits rules. |
| `npm run lhci`         | Runs Lighthouse CI against `dist/`. Build first.                |
| `npm run links`        | Checks built links in `dist/`. Build first.                     |
| `npm run csp:check`    | Validates CSP hashes after a build.                             |
| `npm run css:check`    | Ensures component CSS classes survive the production build.     |

## Stack

| Layer                | Choice                                                                 |
| -------------------- | ---------------------------------------------------------------------- |
| Framework            | Astro 7, static pages plus one Vercel Function for quote delivery.     |
| Styling              | Tailwind CSS 3, PostCSS, CSS custom properties.                        |
| Components           | Astro, organized with atomic design.                                   |
| Client scripts       | Vanilla TypeScript.                                                    |
| Internationalization | Spanish at root and English under `/en/`.                              |
| SEO                  | `@astrojs/sitemap`, Open Graph, Twitter Card, JSON-LD and `llms.txt`.  |
| Analytics            | Umami and `web-vitals`, enabled only with both public Umami variables. |
| Testing              | Playwright, Lighthouse CI, linkinator, ESLint and Prettier.            |
| Hosting              | Vercel with Microfrontends.                                            |

## Architecture

The site is statically rendered with Astro 7. Tailwind CSS 3 supplies utility classes while CSS custom properties carry the design tokens. Client behavior is limited to vanilla TypeScript for navigation, theme state, quote submission, and optional web-vitals reporting. The Vercel Function at `api/quote.ts` sends quote requests through Brevo to `[email protected]`.

### Project structure

```text
src/
├── components/          atoms, molecules and organisms
├── data/                projects, stack, GitHub build-time data and view models
├── i18n/                copy, slug map, localized routes and accessibility helpers
├── layouts/             Layout.astro, the shared document shell and metadata owner
├── pages/               Spanish routes and the English mirror under en/ (quote: cotizar.astro and en/quote/)
├── scripts/             navigation, theme and optional RUM behavior
├── utils/               pure helpers (bento grid spans)
└── styles/              global CSS, design tokens and component classes

api/                     quote.ts, the only Vercel Function
public/                  brand, fonts, UI icons, images, CVs, crawler and AI-discovery files
scripts/                 build-time validation scripts
tests/                   Playwright browser and unit-style specs
docs/                    SEO, i18n, accessibility, performance, security, engineering, release, brand, commercial and roadmap guides
.github/workflows/       CI, PR checks, auto-merge into dev, branch cleanup and release
.husky/                  pre-commit (lint-staged) and commit-msg (commitlint) hooks
assets/                  README-only visual assets

Root configuration: astro.config.mjs, tailwind.config.mjs, postcss.config.cjs, eslint.config.mjs,
tsconfig.json, playwright.config.ts, lighthouserc.json, commitlint.config.cjs,
.prettierrc, .prettierignore, .nvmrc, vercel.json and microfrontends.json.
```

`Layout.astro` owns the shared head, canonical URL, hreflang tags, Open Graph and Twitter metadata, JSON-LD, theme pre-paint logic, navigation loader, navigation, footer, and skip link. Project content is defined in `src/data/projects.ts`; `projectView.ts` adapts it for localized rendering.

## Routes and internationalization

Spanish is the default locale at the root. English is mirrored below `/en/`.

### Locale routes

| Spanish          | English         |
| ---------------- | --------------- |
| `/`              | `/en/`          |
| `/proyectos/`    | `/en/projects/` |
| `/servicios/`    | `/en/services/` |
| `/cotizar/`      | `/en/quote/`    |
| `/sobre-mi/`     | `/en/about/`    |
| `/contacto/`     | `/en/contact/`  |
| `/herramientas/` | `/en/uses/`     |
| `/privacidad/`   | `/en/privacy/`  |

Case studies live at `/proyectos/[slug]/` and `/en/projects/[slug]/`. `src/i18n/utils.ts` is the source of truth for language pairs, localized routes, alternate URLs, active navigation, and CV links. Update its slug map whenever a page or slug changes.

### Microfrontends

The Vercel Microfrontends contract reserves `/nullbreach` and `/nullbreach/:path*` for the independent NullBreach project. This portfolio owns all remaining paths.

## Design system

### Principles

Version 4 uses the @wavival | Design System v4:

- One-column editorial composition at the full container width.
- Raleway 800 for display typography and Poppins for body copy.
- One accessible blue signal for interactive elements.
- 1px rules instead of shadowed cards.
- Static content with native `<details>` disclosures, no scroll-reveal behavior.
- Atomic components in `src/components/atoms`, `molecules`, and `organisms`.
- Dark theme by default, controlled with the `.dark` class and persisted in `localStorage`.

Tokens live in `src/styles/tokens.css`; component classes live in `src/styles/utilities.css`. Use the token names and complete literal component class names to preserve Tailwind output.

## SEO, discovery and accessibility

### SEO and discovery

Full detail: [`docs/seo.md`](./docs/seo.md) and [`docs/i18n.md`](./docs/i18n.md).

Every route receives a shared metadata baseline from `Layout.astro`:

- Canonical URLs and reciprocal `es`, `en`, and `x-default` hreflang alternates.
- Locale-specific default Open Graph cards: `og-card-es.webp` and `og-card-en.webp`, both 1200x630 WebP. Case studies use their own cards.
- Open Graph, Twitter Card, structured data, sitemap, and robots metadata.
- `Person`, `Organization`, and `WebSite` JSON-LD on all pages, plus page-specific structured data where applicable.
- `public/llms.txt` and `public/llms-full.txt` for AI discovery.
- Umami and Core Web Vitals reporting only when `PUBLIC_UMAMI_SRC` and `PUBLIC_UMAMI_ID` are both configured.

### Accessibility

Full detail: [`docs/accessibility.md`](./docs/accessibility.md). Related: [`docs/performance.md`](./docs/performance.md) and [`docs/security.md`](./docs/security.md).

Accessibility behavior includes a visible skip link, localized state labels, a keyboard-safe mobile menu, native disclosures, 44px icon control targets, explicit image dimensions, and a single visible `<h1>` per route. The automated suite checks desktop and mobile rendering, routes, SEO, i18n, theme behavior, project filters, and design regressions.

## Testing and quality

### Local validation

Run the complete browser suite after building:

```bash
npm run build
npm test
```

### Local hooks

Husky runs `lint-staged` (ESLint and Prettier on staged files) before each commit and Commitlint on each commit message; `commit-msg` also runs `~/.claude/git-hooks/commit-msg` when that executable exists on the developer machine. See [`docs/engineering.md`](./docs/engineering.md).

### CI gates

The `CI` workflow, on pushes and pull requests to `dev`, `stg` and `main`, runs the dependency audit (`scripts/check-audit.mjs`, which accepts only the advisories it lists with a reason), formatting, linting, Astro checks, the production build, CSP hash validation, component CSS validation, Lighthouse, and internal-link checks. The `Commit lint` workflow runs on every push, `Secret scan` (Gitleaks) on every push and on pull requests, and pull requests additionally validate the title and the allowed base branch.

## Delivery flow

### Branch model

```text
feature/*, fix/*, chore/*  ->  dev  ->  stg  ->  main
```

- `main` is production, `stg` is staging, and `dev` is the integration branch.
- Work branches target `dev` through pull requests.
- Promotions are only `dev` to `stg` and `stg` to `main`, always with merge commits. `stg` to `main` is merged only by the owner. Because each promotion merge commit lands only on the base branch, a direct head can show as behind; the only allowed alternative head is a branch whose tree is identical to `dev` (or `stg`), which keeps the base-branch check passing.
- `auto-merge-dev.yml` enables auto-merge on every non-draft PR into `dev` from a `feature/`, `fix/` or `chore/` branch (merge commit, source branch deleted), and the merge waits for the required checks.
- `delete-merged-branches.yml` deletes, every 12 hours or on demand with `dry_run`, the branches of PRs merged into `dev`.
- `release.yml` is run by hand to tag and publish a version, see [`docs/RELEASING.md`](./docs/RELEASING.md).
- Conventional Commit messages follow `type(scope): message`.
- Automated checks include commit title and message validation, quality, Playwright, Lighthouse, internal links, Gitleaks, and pull-request base validation.
- Vercel configuration, redirects, security headers, and cache policies live in [`vercel.json`](./vercel.json).

## Deployment

### Vercel

Vercel builds the project with `npm run build`, installs dependencies with `npm ci`, serves `dist/`, and deploys `api/quote.ts` as the quote-delivery function. Set `BREVO_API_KEY` in Vercel and verify `[email protected]` as a Brevo sender. The site is available at `https://www.wavival.dev`.

`microfrontends.json` defines the Vercel development and path-ownership contract. `vercel.json` defines redirects, immutable asset caches, and production security headers. When editing inline scripts, run `npm run build && npm run csp:check` and update the CSP hash only when required by the check.

## Repository documentation

| Document                                                                   | Scope                                                                                                  |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| [README.md](./README.md)                                                   | Repository overview, local setup, architecture, delivery and documentation map.                        |
| [CLAUDE.md](./CLAUDE.md)                                                   | Claude Code project guide: imports `AGENTS.md` and links to the documentation set.                     |
| [AGENTS.md](./AGENTS.md)                                                   | Operational rules for agents: scope, documentation sources, delivery flow and verification.            |
| [DESIGN.md](./DESIGN.md)                                                   | @wavival Design System v4 tokens, type scale, component classes, composition, and accessibility rules. |
| [COMPONENTS.md](./COMPONENTS.md)                                           | Component and layout contracts, props, and usage details.                                              |
| [CHANGELOG.md](./CHANGELOG.md)                                             | Versioned project history following Keep a Changelog and SemVer.                                       |
| [docs/seo.md](./docs/seo.md)                                               | SEO, indexing, structured data and AI-assistant discovery (GEO).                                       |
| [docs/i18n.md](./docs/i18n.md)                                             | Spanish and English: page map, copy locations and rules.                                               |
| [docs/accessibility.md](./docs/accessibility.md)                           | Accessibility guarantees, component behavior, checks and checklist.                                    |
| [docs/performance.md](./docs/performance.md)                               | Performance decisions, caching, measuring and checklist.                                               |
| [docs/security.md](./docs/security.md)                                     | Headers, CSP, the quote function, data, supply chain and checklist.                                    |
| [docs/engineering.md](./docs/engineering.md)                               | Engineering practices: branches, commits, code, checks, CI and documentation.                          |
| [docs/RELEASING.md](./docs/RELEASING.md)                                   | How a version is chosen, tagged and released with the `Release` workflow.                              |
| [docs/brand.md](./docs/brand.md)                                           | Living personal-brand positioning, voice, visual rules, product relationship, and content boundaries.  |
| [docs/commercial.md](./docs/commercial.md)                                 | Living commercial offer, ideal client, permitted claims, CTAs, and content risks.                      |
| [docs/ROADMAP.md](./docs/ROADMAP.md)                                       | Pending work, decisions that need the owner, and known gaps.                                           |
| [public/cv_valentina_ramirez_es.pdf](./public/cv_valentina_ramirez_es.pdf) | Spanish downloadable CV.                                                                               |
| [public/cv_valentina_ramirez_en.pdf](./public/cv_valentina_ramirez_en.pdf) | English downloadable CV.                                                                               |
| [public/llms.txt](./public/llms.txt)                                       | Public AI-discovery index.                                                                             |
| [public/llms-full.txt](./public/llms-full.txt)                             | Public long-form AI-discovery companion.                                                               |
| [public/robots.txt](./public/robots.txt)                                   | Public crawler directives and sitemap location.                                                        |
| [public/.well-known/security.txt](./public/.well-known/security.txt)       | Public RFC 9116 security contact.                                                                      |
| [SECURITY.md](./SECURITY.md)                                               | Private vulnerability-reporting policy for the repository and deployed site.                           |
| [vercel.json](./vercel.json)                                               | Redirects, immutable asset caches and production security headers.                                     |
| [microfrontends.json](./microfrontends.json)                               | Vercel Microfrontends path-ownership contract (NullBreach).                                            |
| [.env.example](./.env.example)                                             | Local environment-variable template.                                                                   |
| [LICENSE](./LICENSE)                                                       | MIT license.                                                                                           |

## License

MIT. See [LICENSE](./LICENSE).

## Contact

<img src="assets/logo-w.png" alt="Wavival logo" width="48" align="middle"> **Valentina Ramírez · @wavival**

> Thanks for getting here. Let's build great things.

[![LinkedIn](https://img.shields.io/badge/LinkedIn-wavival-407bff?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/in/wavival)
[![Instagram](https://img.shields.io/badge/Instagram-@wavival-407bff?style=for-the-badge&logo=instagram&logoColor=white)](https://www.instagram.com/wavival)
[![Email](https://img.shields.io/badge/[email protected]?style=for-the-badge&logo=gmail&logoColor=white)](mailto:[email protected])
Report a correction

Explore similar templates

Compare previews and descriptions within the same stack and category.