Imported from alejandropd-1/Portfolio_2026_astro (
AGENTS.md). Install upstream withnpx skills add alejandropd-1/Portfolio_2026_astro. Copyright stays with the author.
AGENTS.md
This file provides guidance to AI coding agents when working with code in this repository.
Commands
Browser verification
Preferir e2e y WebMCP para verificaciones funcionales con resultados compactos; usar capturas solo cuando aporten evidencia visual. Leer docs/TESTING_MCP.md y node_modules/e2e/skills/e2e/SKILL.md antes de escribir/ejecutar pruebas e2e. Config MCP de este proyecto: C:/www/Portfolio_2026_astro/e2e.config.ts. No iniciar otro Tina sobre puertos ocupados. Los tests deterministas no necesitan modelos ni API keys.
npm run dev # Start TinaCMS + Astro dev server (always use this, not astro dev alone)
npm run build # Build TinaCMS then Astro for production
npm run preview # Preview the production build
npx astro check # TypeScript/Astro type checking
Node ≥ 24 is required. TinaCMS requires TINA_CLIENT_ID and TINA_TOKEN env vars to connect to the cloud CMS; without them the local dev server still runs but CMS features are limited.
Architecture
This is an Astro + React + TinaCMS portfolio site. The key pattern throughout: Astro pages do all data-fetching at build time, then pass data down as props to React client components.
src/pages/index.astro → fetches projects → <ClientHome client:load />
src/pages/about.astro → fetches page entry → <ClientAbout client:load />
src/pages/projects/[...slug].astro → dynamic project detail pages
Content layer (src/content/): Two TinaCMS collections:
projects/— MDX files. Astro schema insrc/content.config.ts. Key frontmatter fields:title,year,date,type,description,stack[],role,client,order,image,showInResume,showInPortfolio,points[],categories[].pages/— MDX files forhome,about,archive,resume— each with different frontmatter schemas (defined per-template intina/config.ts).
Astro content schema (src/content.config.ts): Defines the Zod schema for the projects collection. Critical Astro v6 limitation: custom frontmatter fields are NOT accessible via p.data, and import.meta.glob on MDX files does NOT expose mod.frontmatter. The canonical workaround — already implemented for categories — is:
import matter from "gray-matter"; // transitive dep, no install needed
const rawFiles = import.meta.glob<string>("../content/projects/*.mdx", {
eager: true,
query: "?raw",
import: "default",
});
const fieldMap: Record<string, any> = {};
for (const [path, content] of Object.entries(rawFiles)) {
const slug = path.split("/").pop()!.replace(".mdx", "").toLowerCase(); // lowercase — Astro lowercases p.id
fieldMap[slug] = matter(content).data.yourField ?? defaultValue;
}
// In .map(): yourField: fieldMap[p.id] ?? defaultValue
Three gotchas that caused bugs here: (1) mod.frontmatter is undefined in Astro v6 MDX glob imports, (2) Windows CRLF breaks naive regex parsing — gray-matter handles both, (3) filenames like FOlder-agrado.mdx become p.id = 'folder-agrado' (Astro lowercases), so always .toLowerCase() the key.
TinaCMS (tina/config.ts): Defines CMS UI schemas and a custom PortfolioDashboard screen plugin. The generated types live in tina/__generated__/ — do not edit those files directly. Never import from tina/config.ts in React client components — it pulls in TinaCMS internals incompatible with Vite's ESM bundling (the color-string issue). Instead, put shared constants in src/lib/ and import from there in both places.
Filter categories (src/lib/categories.ts): Single source of truth for the sidebar filter options (PROJECT_CATEGORIES). Imported by both tina/config.ts (to populate CMS checkboxes) and ClientHome.tsx (to render filter buttons). Add/rename categories here only.
Styling: SCSS with a design system at src/styles/abstracts/. All abstracts are auto-forwarded globally via Vite's additionalData in astro.config.mjs, so any .scss file can use $clr-brand-primary, $font-sans, breakpoint mixins, etc. without an explicit @use. CSS Modules (.module.scss) are used for component-scoped styles.
Path alias: @/ maps to src/ in both TypeScript and Vite.
Design System ("The Compiled Soul")
The visual identity is a dark editorial/terminal aesthetic. Key constraints to respect:
- No-line rule: Never use
1px solidborders for sectioning. Boundaries are defined by background color shifts, subtle gradients, or negative space only. Theghost-borderpattern (15% opacity on$clr-brand-tertiary) is the only permitted container edge. - Color roles:
$clr-brand-primary= neon green (accents/CTAs),$clr-brand-secondary= flame orange (keys in key-value pairs),$clr-brand-tertiary= cyan (icons, highlights). - Surface layering: Level 0 =
$clr-brand-surface(page bg), Level 1 =surface-container-low, Level 2 =surface-container-high(cards). Never use the same surface color for a container and its parent. - Typography: Inter only (no secondary fonts for headings). Roboto Mono for code/metadata. Navigation link labels and CTA text use uppercase.
- CSS vars for colors: Color SCSS variables (
$clr-brand-*) are references to CSS custom properties (--clr-brand-*), enabling the light/dark theme toggle via a.lightclass on<html>.
UI Components
Shared primitives live in src/components/UI.tsx:
<SyntaxCard>— the standard card container with optional#labelheader<Tag>— polymorphic: renders as<span>by default, as<button>whenonClickis passed.activevariant fills the bg with primary color.<KeyValue k v>— renderskey = "value";syntax pairs (orange key, white value)
Title formatting convention
Project and page titles use // as a marker for the accent-colored word: "Compiled // Visions." renders with "Visions." in $clr-brand-primary. Use formatTitle() from src/helpers/text-helpers.tsx in JSX, and cleanTitle() for alt attributes or plain text contexts (it strips the // marker).
Home page interactive features
ClientHome.tsx manages two independent UI states:
- Layout (
'cards' | 'list'): toggles between the glass-card grid and typographic list grouped by projecttype. - Filter (
'all' | <category value>): filtersprojects[]byp.categories.includes(activeFilter)before rendering either view. ThefilteredProjectsarray drives both the featured card (filteredProjects[0]) and the grid/list.
The sidebar's # Filters and # Layout blocks are rendered inside home__sidebar which applies a global SyntaxCard override (transparent bg, no padding) — do not add other SyntaxCard children outside home__sidebar without checking that override.
Git Workflow
- NUNCA crear branches automáticamente
- NUNCA crear worktrees
- Trabajar siempre sobre la branch activa actual
- Commits directos a la branch en uso
- Si necesitás cambiar de branch, pedíselo al usuario
