{"id":35867799,"url":"https://github.com/gah-code/gilbertoaharo","last_synced_at":"2026-01-08T14:05:46.810Z","repository":{"id":330285871,"uuid":"1122244992","full_name":"gah-code/gilbertoaharo","owner":"gah-code","description":"2026 | Personal SPA React + Design-system-driven UI + Vite site powered by Contentful.","archived":false,"fork":false,"pushed_at":"2025-12-30T22:53:19.000Z","size":135,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-03T22:00:14.947Z","etag":null,"topics":["content-management-system","contentful"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gah-code.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-24T10:51:29.000Z","updated_at":"2025-12-30T22:53:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gah-code/gilbertoaharo","commit_stats":null,"previous_names":["gah-code/gilbertoaharo"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gah-code/gilbertoaharo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gah-code%2Fgilbertoaharo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gah-code%2Fgilbertoaharo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gah-code%2Fgilbertoaharo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gah-code%2Fgilbertoaharo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gah-code","download_url":"https://codeload.github.com/gah-code/gilbertoaharo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gah-code%2Fgilbertoaharo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28245955,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2026-01-08T02:00:06.591Z","response_time":241,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["content-management-system","contentful"],"created_at":"2026-01-08T14:04:44.955Z","updated_at":"2026-01-08T14:05:46.801Z","avatar_url":"https://github.com/gah-code.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Gilberto A. Haro — Personal Site\n\nSingle-page **React + TypeScript + Vite** site powered by **Contentful**.  \nThe landing page is composed from modular CMS sections, and article pages render deep dives with Rich Text.\n\n---\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Features](#features)\n- [Stack](#stack)\n- [Project Structure](#project-structure)\n- [Getting Started](#getting-started)\n- [Environment Variables](#environment-variables)\n- [How It Works](#how-it-works)\n- [Local Preview Checklist](#local-preview-checklist)\n- [Scripts](#scripts)\n- [Troubleshooting](#troubleshooting)\n- [Content Modeling Notes](#content-modeling-notes)\n- [Safety \u0026 Publishing](#safety--publishing)\n- [Docs](#docs)\n\n---\n\n## Overview\n\nThis repo powers a personal landing experience and long-form writing pages:\n\n- **Landing** (`/`) renders a single `pagePersonalLanding` entry composed of modular section entries.\n- **Articles** (`/articles/:slug`) render `article` entries (Rich Text + optional hero image + attachments).\n- A **lean router** keeps this as a lightweight SPA without adding a routing dependency.\n\n---\n\n## Features\n\n- Content-driven landing page from **Contentful modular sections**\n- Article pages with **SEO metadata fallbacks**\n- Minimal, reusable UI primitives (`src/components/ui`)\n- Small, explicit “content layer” (`src/content/`) that isolates CMS concerns from UI concerns\n- Custom SPA router with internal link interception\n\n---\n\n## Stack\n\n- **React 19**, **TypeScript**, **Vite** (path alias `@` → `src`)\n- **Contentful delivery SDK** + light mappers (`src/content/contentful`)\n- Minimal design primitives and section renderers (`src/components/ui`, `src/components/sections`)\n- Custom SPA router for:\n  - `/`\n  - `/articles/:slug`\n  - 404\n\n---\n\n## Project Structure\n\nHigh-level map:\n\n- `src/pages` — `LandingPage`, `ArticlePage`, `NotFoundPage`\n- `src/router` — path parsing and SPA navigation helpers\n- `src/content` — source contract + Contentful client/API/adapters/types; `static/` holds fixtures for UI-first prototyping\n- `src/components/sections` — per-section renderers driven by CMS content type id\n- `src/components/ui` \u0026 `src/components/layout` — lightweight UI primitives and SEO wrapper\n- `src/components/rich-text` — minimal rich text renderer\n- `src/styles` — tokens + base resets\n- `docs/` — IA, design system notes, CMS guidance\n\n---\n\n# 📁 Project Structure — `gilbertoaharo`\n\n\u003e **Architecture stance:**\n\u003e **Single-Page App (Vite + React)** with a **clean CMS boundary**, a **lightweight router**, and a **design-system-driven UI**.\n\u003e Content models live outside UI logic and flow through adapters before rendering.\n\n```text\ngilbertoaharo/\n├─ .env.example               # Sample env vars (Contentful, site config, preview flags)\n├─ LICENSE                    # Project license\n├─ README.md                  # Project overview, setup, and architecture notes\n├─ eslint.config.js            # ESLint rules (React + TS)\n├─ index.html                 # Vite HTML entry template\n├─ package.json               # Scripts, dependencies, metadata\n├─ package-lock.json          # npm dependency lockfile\n├─ tsconfig.json              # Base TS config with project references\n├─ tsconfig.app.json          # TS config for the app bundle\n├─ tsconfig.node.json         # TS config for Node/Vite tooling\n├─ tsconfig.tsbuildinfo       # TypeScript incremental build cache (generated)\n├─ vite.config.ts             # Vite dev/build config (aliases, plugins, tests)\n│\n├─ public/                    # Static assets copied as-is to build output\n│  ├─ _redirects              # SPA redirect rules (Netlify-style hosting)\n│  └─ vite.svg                # Example static asset\n│\n└─ src/                       # Application source\n   ├─ main.tsx                # React entry point (mounts \u003cApp /\u003e)\n   ├─ App.tsx                 # Root app component (router + global wiring)\n   ├─ App.css                 # Legacy Vite starter styles (currently unused)\n   ├─ index.css               # Legacy Vite global styles (currently unused)\n   ├─ env.ts                  # Centralized env parsing + defaults (fail-fast)\n   ├─ vite-env.d.ts           # Vite/TS env type declarations\n   │\n   ├─ assets/                 # Bundled app assets (imported by JS/TS)\n   │  └─ react.svg\n   │\n   ├─ styles/                 # Global styling layer\n   │  ├─ tokens.css           # Design tokens (colors, spacing, typography)\n   │  └─ base.css             # Base resets/global styles (imports tokens)\n   │\n   ├─ router/                 # Lightweight SPA routing (no react-router)\n   │  ├─ routes.ts            # Route parsing \u0026 route definitions\n   │  ├─ Router.tsx           # Route state + view selection\n   │  └─ link.ts              # Internal navigation helpers (history-based)\n   │\n   ├─ pages/                  # Route-level views (thin, data-driven)\n   │  ├─ LandingPage.tsx      # `/` — Personal landing page\n   │  ├─ ArticlePage.tsx      # `/articles/:slug` — Long-form article view\n   │  ├─ NotFoundPage.tsx     # 404 fallback\n   │  └─ DebugPage.tsx        # Debug/diagnostics view (CMS visibility)\n   │\n   ├─ components/             # UI components (design system + composition)\n   │  ├─ layout/              # Page-level layout \u0026 chrome\n   │  │  ├─ PageShell.tsx     # Page wrapper (SEO, spacing, structure)\n   │  │  └─ SeoHead.tsx       # Document head + meta tags\n   │  │\n   │  ├─ rich-text/           # Controlled rich-text rendering\n   │  │  └─ RichTextRenderer.tsx\n   │  │     # Maps allowed Contentful nodes → UI primitives\n   │  │\n   │  ├─ sections/            # Content-driven page sections\n   │  │  ├─ SectionRenderer.tsx # Switch on section content-type ID\n   │  │  ├─ SectionShell.tsx   # Shared section framing (anchors, spacing)\n   │  │  ├─ HeroSection.tsx\n   │  │  ├─ ProjectsSection.tsx\n   │  │  ├─ SkillsSection.tsx\n   │  │  ├─ TimelineSection.tsx\n   │  │  ├─ LearningSection.tsx\n   │  │  └─ ContactSection.tsx\n   │  │\n   │  └─ ui/                  # Design-system primitives (reusable atoms)\n   │     ├─ Badge.tsx         # Badge / label primitive\n   │     ├─ Button.tsx        # Button primitive (CTA)\n   │     ├─ Card.tsx          # Card surface primitive\n   │     ├─ Container.tsx     # Layout container primitive\n   │     ├─ Heading.tsx       # Heading typography primitive\n   │     ├─ Link.tsx          # Styled anchor primitive\n   │     ├─ Stack.tsx         # Stack/spacing layout primitive\n   │     └─ Text.tsx          # Text typography primitive\n   │\n   ├─ content/                # Content layer (CMS abstraction boundary)\n   │  ├─ source.ts            # ContentSource interface (UI-first contract)\n   │  │\n   │  ├─ static/              # UI-first prototyping (no CMS dependency)\n   │  │  ├─ fixtures.ts       # Local fixture content data\n   │  │  └─ staticSource.ts   # Static ContentSource implementation\n   │  │\n   │  └─ contentful/          # Contentful implementation of ContentSource\n   │     ├─ client.ts         # Contentful SDK client (delivery/preview)\n   │     ├─ api.ts            # Raw Contentful query helpers\n   │     ├─ includes.ts       # Include/reference depth helpers\n   │     ├─ types.ts          # Contentful model/type definitions\n   │     ├─ adapters.ts       # CMS → UI data mapping (contracts live here)\n   │     └─ contentfulSource.ts # Contentful ContentSource implementation\n   │\n   └─ preview/                # Preview-mode support (draft content)\n      ├─ previewMode.ts       # Preview state helpers (env + toggles)\n      └─ PreviewBanner.tsx    # Preview mode UI indicator\n```\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- Node: **20.19+** or **22.12+**\n- npm\n\n### Install\n\n```bash\nnpm install\n````\n\n### Run locally\n\n```bash\nnpm run dev\n```\n\nOpen: `http://localhost:5173`\n\n---\n\n## Environment Variables\n\nCopy `.env.example` to `.env.local` (recommended) and fill in your values:\n\n```bash\ncp .env.example .env.local\n```\n\nRequired:\n\n- `VITE_CONTENTFUL_SPACE_ID`\n- `VITE_CONTENTFUL_DELIVERY_TOKEN`\n- `VITE_CONTENTFUL_ENVIRONMENT` (defaults to `master`)\n\nRecommended:\n\n- `VITE_ARTICLE_ROUTE_PREFIX` (defaults to `/articles`)\n- `VITE_SITE_URL` (absolute URL for canonical fallbacks)\n\nOptional (if you support draft preview later):\n\n- `VITE_CONTENTFUL_USE_PREVIEW`\n- `VITE_CONTENTFUL_PREVIEW_TOKEN`\n\n\u003e Never commit `.env.local` or tokens. Commit `.env.example` only.\n\n---\n\n## How It Works\n\n### Landing page\n\n- `LandingPage` fetches the single `pagePersonalLanding` entry.\n- The page’s `sections[]` are rendered by `SectionRenderer`.\n- `SectionRenderer` dispatches to a section component based on **Contentful content type id**.\n\n### Article pages\n\n- `ArticlePage` fetches an `article` entry by `slug`.\n- Renders:\n\n  - title / excerpt\n  - optional hero image\n  - `RichTextRenderer` for body\n  - optional attachments list\n- SEO meta is applied via `SeoHead`:\n\n  - `\u003ctitle\u003e`\n  - meta description\n  - canonical link fallback\n\n### Router\n\n- `Router` listens to `popstate` and routes between landing, article, and not-found states.\n- A lightweight `Link` / navigation helper intercepts internal links for SPA navigation.\n\n### Content source contract\n\n- `src/content/source.ts` defines the `ContentSource` contract.\n- `contentfulSource` is the default implementation.\n- A `staticSource` exists for UI-first prototyping via fixtures (optional workflow).\n\n---\n\n## Local Preview Checklist\n\nUse this when “it loads but content is missing”:\n\n1. Contentful tokens are present and correct (`.env.local`)\n2. You’re using the right environment (`VITE_CONTENTFUL_ENVIRONMENT`)\n3. Entries are **Published** (Delivery API only)\n4. `pagePersonalLanding` exists and has `sections[]`\n5. At least one `article` exists with a known slug (e.g. `article-test`)\n6. Visit:\n\n   - `/` (landing)\n   - `/articles/\u003cslug\u003e` (article route)\n\n---\n\n## Scripts\n\n```bash\nnpm run dev        # start local dev server\nnpm run build      # type-check + build\nnpm run preview    # preview built app\nnpm run lint       # eslint\n```\n\n---\n\n## Troubleshooting\n\n### 401 Unauthorized\n\n- Missing/invalid Contentful token\n- Fix: verify `VITE_CONTENTFUL_DELIVERY_TOKEN`, restart dev server\n\n### Landing page loads but no sections\n\n- `pagePersonalLanding` missing, unpublished, or `sections[]` empty\n- Fix: publish the entry and ensure `sections[]` contains the section entries\n\n### Article 404\n\n- Wrong route prefix or slug mismatch\n- Fix: confirm route prefix (`/articles`) and the article’s `slug` field\n\n### Missing nested references\n\n- Include depth too low or referenced entries unpublished\n- Fix: increase `include` depth in fetch logic and publish referenced entries\n\n---\n\n## Content Modeling Notes\n\n- Internal navigation should prefer **references over hard-coded URLs**:\n\n  - `projectLink.article` (internal) wins over `projectLink.url` (external)\n- Rich Text is intentionally guarded; the renderer supports a minimal set of nodes.\n- Slugs are treated as stable identifiers once published.\n\n---\n\n## Safety \u0026 Publishing\n\n- Secrets: `.env`, `.env.*` are gitignored; commit `.env.example` only.\n- Audit: search for credentials before pushing:\n\n  ```bash\n  rg -i \"secret|token|password|apikey\"\n  ```\n\n- Dependencies: lockfile is `package-lock.json`.\n- Docs are tracked and safe to publish (no env values in docs).\n\n---\n\n## Docs\n\n- `docs/ia.md` — Information architecture \u0026 content mapping\n- `docs/editorial-guidelines.md` — writing/SEO/link rules\n- `docs/design-system.md` — UI primitives and component patterns\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgah-code%2Fgilbertoaharo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgah-code%2Fgilbertoaharo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgah-code%2Fgilbertoaharo/lists"}