Skip to content

Latest commit

 

History

333 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Personal Portfolio Website

A modern and responsive portfolio website built with Astro and Tailwind CSS. Includes my projects, experience, skills, and more with an elegant, accessible, and multilingual interface.

Features

  • Multilingual: Spanish and English via Astro's native i18n routing (/ and /en/)
  • Accessibility: Font-size controls, accessible image-viewer dialog (focus trap, aria-modal), and prefers-reduced-motion support
  • Animations: Smooth transitions, card animations, and typewriter effect
  • Customizable Theme: Dynamic theme switching (light/dark) with localStorage persistence
  • Responsive: Mobile-first design fully adaptable to all screen sizes
  • Ultra Fast: Statically generated with Astro and WebP-optimized images
  • Tailwind CSS: Modern and customizable styles with dark mode support
  • Bento Grid Layout: Modern asymmetric grid design in About and Contact sections
  • Material Icons: Consistent icon system using Google Material Icons
  • Custom Scrollbar: Gradient-styled scrollbar that adapts to light/dark themes
  • Animated Background: Wave animation effect in the hero section
  • Link Previews: Rich hover previews for LinkedIn, GitHub, Artimark and projects
  • Scroll Progress Indicator: Visual progress bar showing scroll position
  • Schema.org: Structured data (JSON-LD) for improved SEO
  • Skills Slider: Interactive slider with navigation controls for skills display
  • KMP Terminal: Embedded Kotlin/Wasm terminal modal with boot/shutdown sequences and language sync
  • Terminal Effects: Glitch animation on open, full-page vignette power-off on close
  • Hardened Server: Custom Node server with security headers, CSP, rate limiting, and AI-crawler allowlist
  • Custom 404: Real 404 page served with the correct HTTP status
  • Crawler-friendly: robots.txt and llms.txt for search engines and LLMs

Project Structure

src/
├── components/
│   ├── Portfolio.astro          # Composes the full page from sections
│   ├── layout/                  # Header, Footer, AccessibilityControls
│   ├── sections/                # Hero, About, Education, Projects, Experience, Skills, Contact
│   └── ui/                      # Reusable UI primitives (cards, buttons, tags, etc.)
├── config/
│   └── constants.ts             # Shared constants (breakpoints, timings, zoom limits)
├── data/
│   ├── profile.ts               # Single source of personal data + JSON-LD builder
│   ├── project-galleries.ts     # Image galleries per project
│   ├── tech-logos.ts            # Skill → logo mapping
│   └── link-previews.ts         # Hover link-preview data
├── i18n/
│   ├── en.json                  # English translations
│   ├── es.json                  # Spanish translations
│   └── index.ts                 # i18n helpers (getTranslations, detectBrowserLanguage)
├── layouts/
│   └── Layout.astro             # Main layout (meta, Schema.org, i18n data island)
├── pages/
│   ├── index.astro              # Spanish homepage (default locale)
│   ├── en/index.astro           # English homepage
│   └── 404.astro                # Custom 404 page
├── scripts/                     # Client TypeScript modules (bundled by Astro)
│   ├── image-viewer/            # Modular image viewer (modal, zoom, gestures, orchestrator)
│   ├── i18n-bootstrap.ts        # Reads the i18n JSON data island into window.__i18n__
│   ├── theme.ts                 # Light/dark theme system
│   ├── terminal-modal.ts        # KMP terminal modal (boot, shutdown, glitch, vignette)
│   ├── skills-slider.ts         # Skills slider + progress bars
│   ├── scroll-ui.ts             # Scroll progress bar + back-to-top (rAF throttled)
│   ├── scroll-animations.ts     # IntersectionObserver fade-ins
│   ├── text-animations.ts       # Section title / stagger animations
│   ├── accessibility.ts         # Font-size controls + active nav section
│   ├── link-preview.ts          # Rich hover previews
│   └── font-loader.ts           # Non-blocking font loading (CSP-safe)
├── types/
│   └── translations.ts          # TypeScript types for translations
├── assets/                      # Images and resources
└── styles/
    └── global.css               # Global styles, scrollbar, reduced-motion

public/
├── CV/                          # PDF documents (Spanish and English)
├── images/
│   ├── brand/ education/ experience/ hero/ projects/ skills/   # WebP assets
├── wasm/                        # KMP terminal build output (copied from Terminal-KMP)
├── robots.txt                   # Crawler rules (incl. explicit AI bots)
└── llms.txt                     # Site summary for LLMs

server.js                        # Custom static file server (security headers, CSP, rate limit)
scripts/check-i18n-parity.js     # Dev tool: verifies es.json / en.json key parity

Terminal-KMP/                    # Kotlin/Wasm terminal (built into Portafolio-Astro/public/wasm/)
├── shared/                        # Common KMP sources: data, i18n, command engine, Compose UI
├── webApp/                        # WASM executable + copyWasmToAstro Gradle task
└── buildSrc/                      # Custom Gradle plugins

Commands

Command Description
npm install Install project dependencies
npm run dev Start the development server at localhost:4321
npm run build Build the production site to ./dist/
npm run preview Preview the built site locally
node server.js Serve the built ./dist/ in production (used on deploy)
node scripts/check-i18n-parity.js Verify that es.json and en.json share the same keys

Terminal-KMP commands (inside Terminal-KMP/)

Command Description
./gradlew :webApp:copyWasmToAstro Build the KMP terminal and copy it to Portafolio-Astro/public/wasm/
./gradlew :shared:compileKotlinWasmJs Compile the shared KMP module for WASM
./gradlew :webApp:wasmJsBrowserDevelopmentRun Run the terminal standalone in dev mode

Tech Stack

Highlighted Features

Multilingual (i18n)

Languages use Astro's native i18n routing: Spanish is the default at / and English lives at /en/. All text is rendered at build time from src/i18n/*.json. For the few client scripts that need translated strings, the active language is embedded as a non-executable JSON data island and read by i18n-bootstrap.ts into window.__i18n__.

Accessibility Controls

Includes options to increase font size, improving the experience for all users. Controls are accessible via a dedicated component in the header.

Animations

  • Card animations: Shimmer effect and hover transitions
  • Typewriter effect: Dynamic text animation in the hero section
  • Scroll animations: Fade-in effects when elements come into view
  • Animated background: Wave animation in the hero section
  • Terminal boot/shutdown: Typed terminal sequences on modal open/close
  • Glitch effect: RGB channel split + scanlines when opening the terminal modal
  • Vignette power-off: Full-page dark radial pulse after the terminal shuts down

Dynamic Theme

A lightweight theme switching system (light/dark) stored in localStorage. The theme persists across sessions and adapts all UI elements including the custom scrollbar.

Bento Grid Layout

Modern asymmetric grid design used in:

  • About section: Cards for professional description, languages, current status, continuous learning, and driver's license
  • Contact section: Cards for email, phone, LinkedIn, GitHub, and address with different sizes for visual hierarchy

Schema.org Integration

Structured data (JSON-LD) for improved SEO including:

  • Person profile with job title
  • Contact information
  • Educational background (alumniOf)
  • Skills and expertise (knowsAbout)

KMP Terminal

An embedded Kotlin/Wasm terminal accessible:

  • Opens in a full-screen modal
  • Boot sequence with typed system messages
  • Shutdown sequence when closing
  • Glitch opening effect with portfolio brand colors
  • Full-page vignette "power-off" after shutdown
  • Language sync between the Astro page and the terminal via postMessage
  • Close button hidden during boot, shutdown, and glitch animations

Skills Slider

Interactive slider with:

  • Navigation buttons (prev/next)
  • Technology logos from /public/images/skills/
  • Progress bars showing skill levels
  • Responsive grid layout (1-3 columns)

Terminal-KMP Project

A Kotlin Multiplatform module that compiles an interactive terminal UI to Kotlin/Wasm and embeds it into the Astro portfolio.

Structure

Terminal-KMP/
├── shared/
│   └── src/commonMain/kotlin/org/portafolio/terminalkmp/
│       ├── data/                  # Portfolio data models (Lang, PortfolioData)
│       ├── i18n/
│       │   └── SystemStrings.kt     # All terminal UI strings in ES/EN
│       └── terminal/
│           ├── engine/              # Command engine (DefaultCommands.kt, TerminalEngine.kt)
│           ├── ui/                  # Compose terminal screen + state
│           └── engine/Span.kt       # Terminal output styling
├── webApp/
│   ├── build.gradle.kts             # Defines copyWasmToAstro task
│   └── src/wasmJsMain/kotlin/       # WASM-specific entry point + platform hooks
├── buildSrc/                        # Custom Gradle plugins/tasks
└── nixpacks.toml (root)             # Deployment build pipeline

How It Integrates

  1. KMP sources live in Terminal-KMP/
  2. ./gradlew :webApp:copyWasmToAstro builds the terminal and copies the output to Portafolio-Astro/public/wasm/
  3. Portafolio-Astro/src/scripts/terminal-modal.ts creates the modal, loads /wasm/index.html in an iframe, and orchestrates boot/shutdown/glitch/vignette animations
  4. Portfolio data is generated from Portafolio-Astro/src/i18n/es.json and en.json via the generatePortfolioData Gradle task
  5. Language sync uses postMessage (setLanguage) and a localStorage fallback
  6. public/wasm/ is ignored in git; Nixpacks rebuilds it on every deploy

Local Development

After modifying the KMP terminal, run:

cd Terminal-KMP
./gradlew :webApp:copyWasmToAstro

Then reload the Astro page. If you changed data in Portafolio-Astro/src/i18n/*.json, rebuild KMP so the generated data is refreshed.

Translations

Terminal strings are centralized in Terminal-KMP/shared/src/commonMain/kotlin/org/portafolio/terminalkmp/i18n/SystemStrings.kt:

  • ES and EN constructors hold every translatable string
  • Command descriptions, labels, CV prompts, and stack items all live there
  • To add a new language or string, update SystemStrings and use ctx.strings inside commands

Commands

Commands are implemented in Terminal-KMP/shared/src/commonMain/kotlin/org/portafolio/terminalkmp/terminal/engine/DefaultCommands.kt:

  • HelpCommand, AboutCommand, ProjectsCommand, ExperienceCommand, SkillsCommand, StackCommand, ContactCommand, LanguagesCommand, WhoamiCommand, CvCommand, OpenCommand, GalleryCommand, LangchangeCommand, ClearCommand, EchoCommand, BannerCommand
  • Each command implements Command and returns CommandResult(lines, clear, newLang)
  • TerminalEngine dispatches input, manages history, and provides CommandContext with data, strings, and platform services

Security & Server

The production server (server.js) is a dependency-free Node static file server that adds:

  • Security headers: CSP, X-Frame-Options, X-Content-Type-Options, HSTS, Referrer-Policy, Permissions-Policy
  • Rate limiting: per-IP, with static assets and known AI/search crawlers exempted
  • Path-traversal protection and a real 404 response for unknown routes

Performance

  • Project images served as WebP (~87% smaller than the original PNGs)
  • Fonts loaded non-blocking and subset to the weights actually used
  • Scroll handlers throttled with requestAnimationFrame and registered as passive
  • Animations respect prefers-reduced-motion

Resources


About

My Portfolio Web built with Astro, Tailwind CSS, TypeScript and KMP, it includes my relevant info like my Proyects, Education, Contact and more.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages