# Content Guide How to add and edit content on the site. Four sections use **Content Collections** (typed markdown/MDX/YAML); the other interest pages are hand-built `.astro` files. --- ## Projects (Professional page) Projects render from `src/content/projects/*.md` (or `.mdx`). Each file is one project card. The schema lives in `src/content.config.ts`. ### Add a project Create `src/content/projects/my-project.md`: ```md --- title: 'My Awesome Project' role: 'Lead Developer' # optional, default: 'Developer' tech: ['TypeScript', 'Node.js', 'SAP BTP'] # rendered as pills summary: 'A 2-3 sentence description of what it does and your contribution.' repo: 'https://github.com/you/repo' # optional demo: 'https://demo.example.com' # optional caseStudy: 'https://blog.example.com' # optional cover: '/images/my-project.png' # optional, public path order: 20 # lower = shown first, default 100 placeholder: false # true hides it from "real" styling --- Optional longer writeup in markdown here. The card only shows `summary`, but you can link to a case study for depth. ``` ### Field reference | Field | Type | Required | Notes | |---|---|---|---| | `title` | string | yes | Project name | | `role` | string | no | e.g. "Lead Developer" | | `tech` | string[] | no | Tag pills | | `summary` | string | yes | Short card description | | `repo` / `demo` / `caseStudy` | URL | no | Outbound links | | `cover` | string | no | Image path in `public/` | | `order` | number | no | Sort weight | | `placeholder` | boolean | no | Marks it as a TODO card | To remove the seed placeholder, delete `placeholder-project.md`. Aim for 3-6 real entries per the spec. --- ## Experience (Professional timeline) Experience renders from `src/content/experience/*.md`. Each file is one role on the vertical timeline. ### Add a role Create `src/content/experience/another-role.md`: ```md --- company: 'Previous Co.' title: 'Junior Developer' period: '2016 - 2020' # free-form date label focus: 'What you primarily did.' # optional icon: 'briefcase' # mdi icon name for the marker order: 20 # lower = more recent / first --- - Bullet point of an accomplishment. - Another bullet, ideally with a metric. - One more, showing impact. ``` The body renders inside an expandable "Show details" panel. Use markdown lists for bullets. Replace the bracketed placeholders in the seed `implico.md` with real outcomes, metrics, and dates. ### Icon names The `icon` field takes an mdi name without the `mdi:` prefix. Browse names at [pictogrammers.com](https://pictogrammers.com/library/mdi/) or search the installed set: ```bash node -e "const m=require('./node_modules/@iconify-json/mdi/icons.json'); console.log(Object.keys(m.icons).filter(n=>n.includes('cloud')).join('\n'))" ``` --- ## Games (Gaming page) The Gaming page (`src/pages/interests/gaming.astro`) has two content-driven sections that read from collections: **Now playing** and the **Gaming Hall of Fame**. The platforms grid and favorite-genre cards are still edited inline on the page. ### Hall of Fame — one file per game Each game is a markdown file in `src/content/games/hall-of-fame/`. Create a new file (e.g. `hades.md`): ```md --- title: 'Hades' platform: 'Steam' # free-form; drives the icon + platform pill year: 2020 # optional, shown as a small detail art: '/images/games/hades.jpg' # optional, public path (see fetch tool below) artAlt: 'Hades key art' # optional, defaults to the title icon: 'sword-cross' # optional, overrides the platform-derived icon blurb: 'A 2-3 sentence case for why it earned a Hall of Fame slot.' order: 15 # lower = shown first, default 100 --- ``` | Field | Type | Required | Notes | |---|---|---|---| | `title` | string | yes | Game name | | `platform` | string | yes | e.g. "PlayStation 5", "Steam", "Nintendo Switch" | | `blurb` | string | yes | Why it's a favorite | | `art` | string | no | `/images/games/.jpg` (public path) | | `artAlt` | string | no | Alt text, defaults to title | | `year` | number | no | Release year | | `icon` | string | no | mdi icon override (else derived from platform) | | `order` | number | no | Sort weight | `platform` is matched case-insensitively to pick an icon: `playstation` → `sony-playstation`, `switch` → `nintendo-switch`, `steam`/`pc` → `steam`, `xbox` → `microsoft-xbox`, anything else → `gamepad-variant`. Override with `icon` if you want something specific. ### Now playing — single list Edit `src/content/games/now-playing.yaml`. This is the transient list of games you're actively playing; each entry is a title + platform only: ```yaml games: - title: "Elden Ring" platform: "PlayStation 5" - title: "Balatro" platform: "Steam" ``` If the list is empty, the page shows a TODO placeholder instead. ### Fetching box art (RAWG) `scripts/fetch-box-art.mjs` downloads box/banner art from the [RAWG API](https://rawg.io/apidocs) into `public/images/games/`, then prints the public path to paste into a game file's `art:` field. RAWG covers Steam, PlayStation, Xbox, and Nintendo in one place. 1. Get a free key at and add it to `.env` (copy from `.env.example`): ``` RAWG_API_KEY=your_key_here ``` 2. Run (Node 20.6+ can auto-load `.env` via `--env-file`): ```bash node --env-file=.env scripts/fetch-box-art.mjs "Elden Ring" node --env-file=.env scripts/fetch-box-art.mjs "Hollow Knight" --platform pc node --env-file=.env scripts/fetch-box-art.mjs --id 3328 # by RAWG id node --env-file=.env scripts/fetch-box-art.mjs "Celeste" --out celeste.jpg ``` 3. It prints something like: ``` ✔ Saved: public/images/games/elden-ring.jpg art: "/images/games/elden-ring.jpg" ``` Paste that `art:` value into the game file's frontmatter. > RAWG serves **landscape** banner/screenshot art (not portrait Steam > capsules), which matches the Hall of Fame cards (`aspect-[4/3]`). --- ## Board games (TTRPGs page) The "Board games" section on the TTRPGs page reads from a JSON file and renders an interactive shelf: a list of games on the left, and a tall image on the right that cross-fades to a game's BGG cover when you hover its name. Clicking a name opens the game on BoardGameGeek. ### The list — `src/data/boardgames.json` A flat JSON array. Each entry needs a `name` and a `bggId`; `note` and `image` are optional: ```json [ { "name": "Wingspan", "bggId": 266192, "note": "Engine builder" }, { "name": "Brass: Birmingham", "bggId": 224517 } ] ``` | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display label + link text | | `bggId` | number | yes | BoardGameGeek numeric id (from its URL) | | `note` | string | no | One-line caption shown under the name | | `image` | string | no | Cover path/URL; populated by the fetch tool | Find a game's `bggId` in its BoardGameGeek URL: `https://boardgamegeek.com/boardgame//`. The shelf photo at rest is `/images/ttrpg-boardgame-shelf.jpg` — drop a real photo of your shelf at that path. The image is a **progressive enhancement**: without JS the list still works (links + shelf photo); games without an `image` just keep the shelf photo on hover. ### Fetching covers (BoardGameGeek) `scripts/fetch-bgg-covers.mjs` fills in the `image` field for any game missing one, using BGG's public XML2 API (no key needed): ```bash # Default: download covers to public/images/boardgames/.jpg (recommended) node scripts/fetch-bgg-covers.mjs # Instead hotlink BGG's CDN URLs (no download, depends on their CDN) node scripts/fetch-bgg-covers.mjs --remote # Re-fetch even games that already have an image node scripts/fetch-bgg-covers.mjs --force # Slow down between BGG calls (they rate-limit; default 1200ms) node scripts/fetch-bgg-covers.mjs --sleep 2000 ``` It's idempotent — run it again any time you add games to the JSON. --- ## TCGs (Trading Card Games page) The TCGs page (`src/pages/interests/tcgs.astro`) is hand-built, like the other interest pages. Two parts are data-driven: - **The games I play** and **Proxy making** are inline frontmatter arrays of `{ icon, title, body }` — edit them directly on the page. - **Magic: The Gathering decks** are pulled **live from Archidekt** on the server (the site is SSR, so no CORS or API key is needed). ### Archidekt integration `src/lib/archidekt.ts` fetches Greg's public decks from: ``` GET https://archidekt.com/api/decks/v3/?ownerUsername= ``` - The username is the `ARCHIDEKT_USERNAME` export (currently `Patiun`). Change it there to point at a different account. - Results are cached to `node_modules/.cache/archidekt-.json` for `CACHE_TTL_MS` (default **1 hour**) so repeat visits are instant and we're gentle on Archidekt. Tune the TTL via that export. - Only public, non-unlisted decks are shown. If Archidekt is unreachable, the section falls back to a "Manage on Archidekt" link — it never breaks the page. Each deck renders as a `DeckCard` (featured card art, mana-color pips, card count, format, last-updated) linking to its Archidekt page. To add or remove decks, just change them on Archidekt — the page picks them up automatically (within the cache window). --- ## Interest pages Each interest is a hand-built page in `src/pages/interests/`. They follow the spec's template: **Header** (title + tagline), **Overview** (narrative), **Highlights** (card grid), **Currently** (active now), **Related** (cross-links). To edit content, open the relevant `.astro` file and change the copy in the frontmatter arrays or the section bodies. To add a new interest: 1. Create `src/pages/interests/my-interest.astro` (copy an existing page as a template). 2. Use ``, ``, ``, and ``. 3. Add a card for it in `src/pages/interests/index.astro` (the hub). --- ## Global content (nav, socials, tagline) Edit `src/lib/site.ts`: - `siteIdentity` — name, tagline, domain - `navGroups` — the two-group top nav - `socials` — footer + contact links (replace the `[Greg to fill]` handles) --- ## Finding placeholders Every `[Greg to fill]` gap from the spec is rendered as a styled **Placeholder** slot (dashed border + pencil badge + "TODO · label"). To find them all: ```bash grep -rn "Placeholder label" src/ grep -rn "Greg to fill\|\[Greg" src/ ``` Replace each with real content as you gather it.