New TLE Community website using Eleventy. tlecommunity.com
site CLAUDE.md
8.7 kB
Markdown
at main

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/<page>/images/ is copied to public/<page>/images/ (the content/ prefix is stripped, since content/ 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 relative images/foo.jpg src (eleventy-img transforms it). Site-chrome <img> 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 <img> 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 <style eleventy:ignore>{{ styles.tailwind | safe }}</style> (from data/styles.js) plus the per-page css bundle. No top <header>/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 <h1>, 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.