# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is The TLE Community website — a static site for a community-run reboot of the game _The Lacuna Expanse_. Built with Eleventy (11ty) v3, derived from the `eleventy-base-blog` starter. It contains a blog plus a large developer-documentation tree under `content/developers/**` (API reference, frontend, and server docs) imported from an older Hugo site; many of those pages are dated 2022 and self-describe as out of date. ESM project (`"type": "module"` — all `.js` config uses `import`/`export`). Requires Node 22 or newer (`package.json` engines is `>=22`; `.nvmrc` pins `22`). ## Commands Scripts are orchestrated with `npm-run-all2` (`run-s` serial, `run-p` parallel). Tailwind CSS is compiled by the `tailwindcss` CLI into `css/dist/tailwind.css` (git-ignored) before / alongside Eleventy. - `npm start` — `run-p css:watch eleventy:serve`. Tailwind watcher + dev server with live reload at http://localhost:3000. - `npm run build` — `run-s css:build eleventy`. Compiles+minifies CSS, then production build into `public/`. Runs with `ELEVENTY_RUN_MODE=build`, which makes the drafts preprocessor drop any page with `draft: true`. - `npm run css:build` / `css:watch` — Tailwind only (`-i css/tailwind.css -o css/dist/tailwind.css`). - `npm run debug` / `npm run debugstart` — build / serve with `DEBUG=Eleventy*` (CSS step first). - `npm run benchmark` — build with `DEBUG=Eleventy:Benchmark*`. - `npm run format:check` / `npm run format:fix` — Prettier over the whole repo. `.prettierignore` excludes `public/`, `css/dist/`, `assets/`, `package-lock.json`. There is no test suite and no linter other than Prettier. Prettier config (`.prettierrc.json`): no semicolons, single quotes, 2-space indent, 100-col print width, `proseWrap: always` (Markdown is reflowed to 100 cols on format). Editor formats on save (`.vscode/settings.json`). ## Architecture ### Directory layout is customised — do not assume starter defaults `eleventy.config.js` exports a `config` object at the bottom that overrides `dir`: - Input: `content/` (not `src/`) - Output: `public/` (git-ignored) - Includes / layouts: `includes/` - Global data: `data/` Passthrough copy (in `eleventy.config.js`): - `addPassthroughCopy('assets')` — site-wide chrome (`assets/fonts/promethean.ttf`, `assets/favicon.png`, `assets/img/*` panel/logo textures, `assets/lacuna-assets-license.txt`) served at `/assets/**`. Reference these with **absolute** URLs (they're used from inlined CSS). - `addPassthroughCopy('content/**/images')` — per-page images. `content//images/` is copied to `public//images/` (the `content/` prefix is stripped, since `content/` is the input dir), so it's served at `//images/…` — e.g. `content/media/images/` → `/media/images/…`, `content/images/` (home) → `/images/…`. Used for full-resolution download links. - Inline `` that should be optimised uses a **relative** `images/foo.jpg` src (eleventy-img transforms it). Site-chrome `` under `/assets/` carries `eleventy:ignore` so the transform leaves the passthrough path alone. Treat `public/` as build output only. Template engines: Nunjucks processes both `.md` and `.html` (`markdownTemplateEngine` / `htmlTemplateEngine` = `njk`). Recognised formats: `md`, `njk`, `html`, `liquid`, `11ty.js`. ### `eleventy.config.js` is the single wiring point It registers everything: the drafts preprocessor, per-page CSS/JS bundles, and all plugins — `eleventy-plugin-syntaxhighlight`, `eleventy-navigation`, `HtmlBasePlugin`, `InputPathToUrlTransformPlugin`, `IdAttributePlugin`, the RSS `feedPlugin` (Atom feed at `/feed/feed.xml` from the `posts` collection), and `eleventy-img`'s `eleventyImageTransformPlugin` (auto-optimises `` in output to avif/webp). Custom filters live in `config/filters.js` and are added as a plugin (`pluginFilters`). ### Drafts Two coordinated pieces: 1. `data/eleventyDataSchema.js` — a Zod `eleventyDataSchema` that coerces/validates the `draft` front-matter field on every page (a bad value throws and fails the build). 2. The `drafts` preprocessor in `eleventy.config.js` — appends `(draft)` to the title always, and excludes the page entirely when `ELEVENTY_RUN_MODE === 'build'`. So drafts are visible under `npm start` but absent from `npm run build`. ### Design system — legacy Lacuna look The site replicates the deployed legacy _Lacuna Expanse_ marketing site (dark sci-fi, bold full-bleed space art, glowing cyan "Promethean" nav text, two navbar modes). Source repos it was ported from: `../homepage` (static HTML site) and `../server/assets` (game art repo — see the license note carried in `assets/lacuna-assets-license.txt`; resize/reformat only). - **Promethean** display font: `assets/fonts/promethean.ttf`, `@font-face` in `css/tailwind.css`, Tailwind `font-display`. - **Glow**: `.text-glow` / `.text-glow-hot` / `.text-glow-lg` utilities in `css/tailwind.css` (`@layer utilities`) — the legacy 4-way cyan `text-shadow`. - **Two navbars**, hardcoded partials (NOT `eleventyNavigation`): `includes/partials/nav-home.njk` (transparent, glowing, over the hero) and `includes/partials/nav-interior.njk` (solid amber "tab" buttons — `.tab` / `.tab-play` / `.tab-help` components). Both link out to the community forums/wiki/news; those links are mirrored, never fetched. - **Interior chrome**: `.site-crown` (wordmark), `.site-bodytop` (blue title bar from `bkg_crown.png`, shows the page `title`), `.site-body` (dark panel). Body background is the tiled `field.png` starfield site-wide (set in `base.njk`); the home page draws its own full-bleed hero image on top of it (`content/index.njk`). ### Content model - `content/content.11tydata.js` sets `layout: layouts/page.njk` as the default for all content. - `content/blog/blog.11tydata.js` overrides blog posts to `layout: layouts/post.njk` and tags them `posts` (this drives `collections.posts`, the archive, and the feed). - Layout inheritance uses real Nunjucks `{% extends %}` + `{% block %}` (blocks: `head_extra`, `body_class`, `nav`, `main`, `article`, `footer`): - `base.njk` — HTML shell. Inlines the compiled Tailwind CSS via `` (from `data/styles.js`) plus the per-page `css` bundle. No top `
`/nav here — children supply the `nav` block. - `page.njk` — extends base; interior navbar + `.site-crown`/`.site-bodytop`/`.site-body` panel; wraps content in `prose prose-invert`. Default for the whole `content/developers/**` tree. - `post.njk` — extends `page.njk`; adds Prism theme + `css/prism-diff.css` in `head_extra`; renders the post `

`, metadata, prev/next in `article`. - `home.njk` — extends base; home navbar; full-bleed, no panel, no `prose` (`content/index.njk` sets `layout: layouts/home.njk`). - The primary nav is hardcoded; `eleventyNavigation` front matter has been removed from content pages. `eleventy-navigation` is still registered but unused by templates. - Site-wide metadata: `data/metadata.js`. Feed metadata is a separate copy inside the `feedPlugin` config in `eleventy.config.js` — update both when site title/author changes. ### CSS/JS - **Tailwind CSS v3** (`tailwind.config.js`, ESM). Input `css/tailwind.css` (`@tailwind` directives, `@font-face`, `@view-transition`, `@layer utilities`/`components`), compiled to `css/dist/tailwind.css`. `data/styles.js` reads that file; `base.njk` inlines its text verbatim with `| safe` + `eleventy:ignore` (no `{% include %}` — avoids Nunjucks re-parsing the CSS). - `css/index.css` is the pre-redesign hand-written stylesheet, kept for reference only — nothing includes it and the Tailwind CLI never targets it. Its still-used component classes (`.links-nextprev*`, `.post-metadata`, `.post-tag`, `.visually-hidden`) were migrated into `css/tailwind.css` `@layer components`. The news archive and tag listings (`content/news.njk`, `content/tags.njk`, `content/tag-pages.njk`, `includes/postslist.njk`) were since restyled with plain Tailwind utilities, so the old `.postlist*` classes are gone. - Preflight resets bare-element styling, so long-form content (docs, blog) relies on `@tailwindcss/typography`'s `prose prose-invert` wrapper in `page.njk`. Prism okaidia is kept readable inside `prose` via guards in `css/tailwind.css`. - `addWatchTarget` covers `css/**/*.css`, `assets/**`, and content images. - `@zachleat/heading-anchors` is inlined from `node_modules` into the JS bundle in `base.njk`.