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 intopublic/. Runs withELEVENTY_RUN_MODE=build, which makes the drafts preprocessor drop any page withdraft: 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 withDEBUG=Eleventy*(CSS step first).npm run benchmark— build withDEBUG=Eleventy:Benchmark*.npm run format:check/npm run format:fix— Prettier over the whole repo..prettierignoreexcludespublic/,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/(notsrc/) - 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/<page>/images/is copied topublic/<page>/images/(thecontent/prefix is stripped, sincecontent/is the input dir), so it's served at/<page>/images/…— e.g.content/media/images/→/media/images/…,content/images/(home) →/images/…. Used for full-resolution download links.- Inline
<img>that should be optimised uses a relativeimages/foo.jpgsrc (eleventy-img transforms it). Site-chrome<img>under/assets/carrieseleventy:ignoreso 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 <img> in output to avif/webp). Custom filters live in config/filters.js and are
added as a plugin (pluginFilters).
Drafts #
Two coordinated pieces:
data/eleventyDataSchema.js— a ZodeleventyDataSchemathat coerces/validates thedraftfront-matter field on every page (a bad value throws and fails the build).- The
draftspreprocessor ineleventy.config.js— appends(draft)to the title always, and excludes the page entirely whenELEVENTY_RUN_MODE === 'build'. So drafts are visible undernpm startbut absent fromnpm 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-faceincss/tailwind.css, Tailwindfont-display. - Glow:
.text-glow/.text-glow-hot/.text-glow-lgutilities incss/tailwind.css(@layer utilities) — the legacy 4-way cyantext-shadow. - Two navbars, hardcoded partials (NOT
eleventyNavigation):includes/partials/nav-home.njk(transparent, glowing, over the hero) andincludes/partials/nav-interior.njk(solid amber "tab" buttons —.tab/.tab-play/.tab-helpcomponents). 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 frombkg_crown.png, shows the pagetitle),.site-body(dark panel). Body background is the tiledfield.pngstarfield site-wide (set inbase.njk); the home page draws its own full-bleed hero image on top of it (content/index.njk).
Content model #
content/content.11tydata.jssetslayout: layouts/page.njkas the default for all content.content/blog/blog.11tydata.jsoverrides blog posts tolayout: layouts/post.njkand tags themposts(this drivescollections.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<style eleventy:ignore>{{ styles.tailwind | safe }}</style>(fromdata/styles.js) plus the per-pagecssbundle. No top<header>/nav here — children supply thenavblock.page.njk— extends base; interior navbar +.site-crown/.site-bodytop/.site-bodypanel; wraps content inprose prose-invert. Default for the wholecontent/developers/**tree.post.njk— extendspage.njk; adds Prism theme +css/prism-diff.cssinhead_extra; renders the post<h1>, metadata, prev/next inarticle.home.njk— extends base; home navbar; full-bleed, no panel, noprose(content/index.njksetslayout: layouts/home.njk).
- The primary nav is hardcoded;
eleventyNavigationfront matter has been removed from content pages.eleventy-navigationis still registered but unused by templates. - Site-wide metadata:
data/metadata.js. Feed metadata is a separate copy inside thefeedPluginconfig ineleventy.config.js— update both when site title/author changes.
CSS/JS #
- Tailwind CSS v3 (
tailwind.config.js, ESM). Inputcss/tailwind.css(@tailwinddirectives,@font-face,@view-transition,@layer utilities/components), compiled tocss/dist/tailwind.css.data/styles.jsreads that file;base.njkinlines its text verbatim with| safe+eleventy:ignore(no{% include %}— avoids Nunjucks re-parsing the CSS). css/index.cssis 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 intocss/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'sprose prose-invertwrapper inpage.njk. Prism okaidia is kept readable insideprosevia guards incss/tailwind.css. addWatchTargetcoverscss/**/*.css,assets/**, and content images.@zachleat/heading-anchorsis inlined fromnode_modulesinto the JS bundle inbase.njk.