refactor(docs): rebuild the site around reading, not launching

The generated site had the shape of a product launch page: a near-black navy
ground with mint/coral radial glows, a gradient-clipped headline, a glowing
pill badge, nine emoji feature cards and scroll-reveal animations. None of it
told a reader anything about the language.

It is now typographic and light-first — a warm paper ground, a serif display
face, one ink-blue accent used only where it means something, and rules
instead of floating cards. Colour is reserved for code. Dark mode is the same
design with the ground inverted, defined once as tokens under a single
prefers-color-scheme block.

Structurally:

- base.css holds the tokens and shared chrome; site.css and docs.css hold what
  is specific to the landing page and the reference pages. They ship as linked
  files rather than being inlined into all 900+ pages, which takes the site
  from 16 MB to 5 MB and means a design change no longer needs a regenerate to
  be seen.
- api.css was dead — the generator never referenced it, rendering the API index
  with item.css — and is gone. docs.css replaces item.css and covers all four
  reference page kinds.
- Grids draw their separators as cell borders instead of bleeding a ruled
  background through gaps, so a final row with fewer cards than columns stops
  cleanly instead of leaving a grey hole. The feature card count is not a
  multiple of the column count at any breakpoint.
- Inline code loses its tinted chip; in a language reference, a box behind
  every keyword turns a paragraph into confetti.
- Fonts are the platform's own, so the site makes no webfont request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-05 01:47:36 +03:00
parent 583783449a
commit 2f3833a295
12 changed files with 1046 additions and 571 deletions

View file

@ -1,108 +1,233 @@
{
"brand": "Ludic",
"tagline": "The opinionated, compiled language for 2D games.",
"tagline": "A compiled language for 2D games, with the entity system in the syntax.",
"repo_url": "https://git.workshopsoft.io/workshopsoft/ludic",
"meta": {
"title": "Ludic — the opinionated game language",
"description": "Ludic is an opinionated, compiled language for 2D games. The entity system, rendering, named colors, input, deterministic math and save/load are part of the language — so you build a game, not a framework.",
"og_title": "Ludic — the opinionated game language",
"og_description": "An opinionated, compiled language for 2D games. The ECS, rendering, colors, input and save/load are built in — build a game, not a framework."
"title": "Ludic — a compiled game language",
"description": "Ludic compiles ahead of time to a native binary. The entity-component system is part of the syntax, the runtime is deterministic fixed-point, and the compiler is written in Ludic — no C is generated, compiled or linked in a build.",
"og_title": "Ludic — a compiled game language",
"og_description": "Ahead-of-time compiled, with an ECS in the syntax and a deterministic fixed-point runtime. The compiler is written in Ludic; no C is generated, compiled or linked in a build."
},
"nav_links": [
{ "label": "Features", "href": "#features" },
{ "label": "Examples", "href": "#showcase" },
{ "label": "Get started", "href": "#start" },
{ "label": "API Reference", "href": "api.html" }
{
"label": "Features",
"href": "#features"
},
{
"label": "Examples",
"href": "#showcase"
},
{
"label": "Get started",
"href": "#start"
},
{
"label": "API Reference",
"href": "api.html"
}
],
"ref_nav_links": [
{ "label": "Home", "href": "index.html" },
{ "label": "API Reference", "href": "api.html" }
{
"label": "Home",
"href": "index.html"
},
{
"label": "API Reference",
"href": "api.html"
}
],
"hero": {
"pill": "Opinionated · Batteries-included · No framework",
"title_pre": "Build a game,",
"title_accent": "not a framework.",
"lead": "Ludic is an <b>opinionated, compiled language for 2D games</b>. The entity system, drawing, named colors, input, deterministic math and save/load aren't libraries you wire up — they're <b>part of the language</b>. You write the game; there's nothing to assemble first.",
"title_pre": "A compiled language",
"title_accent": "for 2D games.",
"lead": "Ludic compiles ahead of time to a native binary — no engine to install, no interpreter, nothing shipped beside the executable. The entity-component system is <b>part of the syntax</b>, the maths is <b>deterministic fixed-point</b>, and <code>ludicc</code> is itself written in Ludic: <b>no C is generated, compiled or linked in a build</b>.",
"snippet": "docs/site/snippets/hero.ludic",
"snippet_name": "hello.ludic",
"primary_cta": { "label": "Get started →", "href": "#start" },
"secondary_cta": { "label": "API Reference", "href": "api.html" },
"pipeline": [".ludic", "ludicc", "optimized IR", "native binary"],
"pipeline_note": "One step from your code to a native binary. No engine to install, no interpreter, no runtime to ship alongside it — the game <i>is</i> the executable."
"primary_cta": {
"label": "Get started →",
"href": "#start"
},
"secondary_cta": {
"label": "API Reference",
"href": "api.html"
},
"pipeline": [
".ludic",
"ludicc",
"LLVM IR",
"native binary"
],
"pipeline_note": "<code>ludicc</code> lowers straight to LLVM IR and links a native binary. There is no C step in between and no runtime to ship alongside — the game is the executable."
},
"features": {
"kicker": "Why Ludic",
"title": "A language shaped around the game.",
"intro": "The things you normally bolt on — an entity-component system, a renderer, a deterministic clock, save/load — are primitives of the language itself. One opinionated way to do each, so there's little to decide and nothing to wire up.",
"intro": "The things you normally bolt on — an entity-component system, a renderer, a deterministic clock, save/load — are primitives of the language itself. One way to do each, so there is little to decide and nothing to wire up.",
"cards": [
{ "icon": "🧩", "title": "ECS in the syntax", "html": "<code>property</code>, <code>model</code>, and <code>handler</code> are keywords. Query entities with <code>query [A, B, {Tag}]</code> and iterate matches directly — no framework to wire up." },
{ "icon": "⏱️", "title": "Deterministic runtime", "html": "Q16.16 <code>fixed</code>-point math and a seeded RNG mean the same inputs produce the same frame — byte-for-byte — every run. Ideal for replays and lockstep netcode." },
{ "icon": "🎨", "title": "Drawing &amp; named colors", "html": "The <code>Screen</code> API draws rectangles, text and pixels with named arguments; <code>Color.Crimson</code> and 220 more names read like English and cost nothing at runtime." },
{ "icon": "📦", "title": "One self-contained binary", "html": "A game compiles to a single native executable — no engine to install, no interpreter, no runtime shipped beside it. Build, and run the file." },
{ "icon": "💾", "title": "Snapshot save/load", "html": "<code>save()</code> and <code>load()</code> serialize the entire ECS world — every entity, property and program <code>var</code> — in one call." },
{ "icon": "🎬", "title": "Scenes &amp; state machines", "html": "<code>scene</code>/<code>layer</code>/<code>become</code> model mutually-exclusive game states with enter/exit hooks; <code>match</code>/<code>machine</code>/<code>state</code> handle dispatch and per-entity FSMs." },
{ "icon": "🖼️", "title": "Retained UI &amp; assets", "html": "Declare a widget tree as data with <code>ui</code> — panels, labels, buttons, 9-slice skins, keyboard focus. Sprites decode from PNG at runtime; text is real TrueType." },
{ "icon": "🔌", "title": "Modules &amp; native calls", "html": "<code>module</code> + <code>@export fn</code> builds a shared library of plain native symbols; <code>extern fn … = \"symbol\"</code> reaches out to any native library when you need the platform." }
{
"title": "ECS in the syntax",
"html": "<code>property</code>, <code>model</code>, and <code>handler</code> are keywords. Query entities with <code>query [A, B, {Tag}]</code> and iterate matches directly — no framework to wire up."
},
{
"title": "Deterministic runtime",
"html": "Q16.16 <code>fixed</code>-point math and a seeded RNG mean the same inputs produce the same frame — byte-for-byte — every run. Ideal for replays and lockstep netcode."
},
{
"title": "Drawing &amp; named colors",
"html": "The <code>Screen</code> API draws rectangles, text and pixels with named arguments; <code>Color.Crimson</code> and 220 more names read like English and cost nothing at runtime."
},
{
"title": "One self-contained binary",
"html": "A game compiles to a single native executable — no engine to install, no interpreter, no runtime shipped beside it. Build, and run the file."
},
{
"title": "Snapshot save/load",
"html": "<code>save()</code> and <code>load()</code> serialize the entire ECS world — every entity, property and program <code>var</code> — in one call."
},
{
"title": "Scenes &amp; state machines",
"html": "<code>scene</code>/<code>layer</code>/<code>become</code> model mutually-exclusive game states with enter/exit hooks; <code>match</code>/<code>machine</code>/<code>state</code> handle dispatch and per-entity FSMs."
},
{
"title": "Retained UI &amp; assets",
"html": "Declare a widget tree as data with <code>ui</code> — panels, labels, buttons, 9-slice skins, keyboard focus. Sprites decode from PNG at runtime; text is real TrueType."
},
{
"title": "Modules &amp; native calls",
"html": "<code>module</code> + <code>@export fn</code> builds a shared library of plain native symbols; <code>extern fn … = \"symbol\"</code> reaches out to any native library when you need the platform."
}
]
},
"showcase": {
"kicker": "Show, don't tell",
"title": "Real programs, one toolchain.",
"intro": "The same <code style=\"color:var(--mint)\">ludicc</code> that builds a JRPG builds a from-scratch Snake and a scene demo. Nothing is hardcoded to a genre — and every token below links into the reference.",
"intro": "The same <code>ludicc</code> that builds a JRPG builds a from-scratch Snake and a scene demo. Nothing is hardcoded to a genre — and every token below links into the reference.",
"samples": [
{ "label": "snake", "name": "snake.ludic", "file": "docs/site/snippets/snake.ludic",
"note": "A complete Snake — grid, growth, food, game-over — from primitives. State is named vars, colors are named, and every draw call says what each argument is." },
{ "label": "scenes", "name": "scenes.ludic", "file": "docs/site/snippets/scenes.ludic",
"note": "One active scene at a time. `become` runs the old scene's on-exit and the new one's on-enter; layers draw in declaration order. State is a plain named var." },
{ "label": "lifecycle", "name": "toggle.ludic", "file": "docs/site/snippets/lifecycle.ludic",
"note": "Enable/disable at three scopes — entity, model, handler. Disabling never destroys data: a property's values persist, so a later enable restores them." },
{ "label": "hello", "name": "hello.ludic", "file": "docs/site/snippets/hero.ludic",
"note": "The smallest program that exercises the whole pipeline: properties, a spawn, a queried handler, and a render/quit." }
{
"label": "snake",
"name": "snake.ludic",
"file": "docs/site/snippets/snake.ludic",
"note": "A complete Snake — grid, growth, food, game-over — from primitives. State is named vars, colors are named, and every draw call says what each argument is."
},
{
"label": "scenes",
"name": "scenes.ludic",
"file": "docs/site/snippets/scenes.ludic",
"note": "One active scene at a time. `become` runs the old scene's on-exit and the new one's on-enter; layers draw in declaration order. State is a plain named var."
},
{
"label": "lifecycle",
"name": "toggle.ludic",
"file": "docs/site/snippets/lifecycle.ludic",
"note": "Enable/disable at three scopes — entity, model, handler. Disabling never destroys data: a property's values persist, so a later enable restores them."
},
{
"label": "hello",
"name": "hello.ludic",
"file": "docs/site/snippets/hero.ludic",
"note": "The smallest program that exercises the whole pipeline: properties, a spawn, a queried handler, and a render/quit."
}
]
},
"philosophy": {
"kicker": "The philosophy",
"title": "No framework. No glue. Just the game.",
"title": "Batteries in the language, not in a framework.",
"paras": [
"Most game code is plumbing — registering systems, wiring a renderer, threading state through a framework. Ludic makes those decisions for you and bakes them into the language, so the code you write is the game's actual logic.",
"Opinionated on purpose: one clear way to spawn an entity, draw a frame, name a color, run a scene. Less to choose, less to learn, less to maintain."
"Most game code is plumbing — registering systems, wiring a renderer, threading state through a framework. Ludic makes those decisions once and bakes them into the language, so the code you write is the game's actual logic.",
"That extends to the toolchain. <code>ludicc</code> is written in Ludic and recompiles its own source to a byte-identical binary, rebuilding from a checked-in IR seed with clang and nothing else."
],
"stats": [
{ "big": "0", "lbl": "engines to install" },
{ "big": "1", "lbl": "clear way to do each thing" },
{ "big": "=", "lbl": "deterministic frames" }
{
"big": "0",
"lbl": "lines of C in a build"
},
{
"big": "1",
"lbl": "backend: LLVM IR"
},
{
"big": "=",
"lbl": "byte-exact self-rebuild"
}
]
},
"start": {
"kicker": "Get started",
"title": "From clone to a native window.",
"intro": "Build the toolchain once, then your game. Everything runs from the repo root.",
"intro": "Bootstrap the task runner once from the checked-in IR seed, then build the toolchain and your game. Everything runs from the repo root.",
"steps": [
{ "title": "Build the toolchain", "html": "<code>bin/x build</code> produces <code>ludicc</code>, the compiler you'll use for everything below." },
{ "title": "Compile &amp; run an example", "html": "<code>bin/x app examples/games/snake.ludic</code> turns a <code>.ludic</code> file into a native binary. Run it to open a real window." },
{ "title": "Go headless for tests", "html": "<code>--headless</code> renders frames to a <code>.ppm</code> from piped input — deterministic output you can diff." },
{ "title": "Edit with full tooling", "html": "<code>bin/x tools</code> builds the formatter and language server; every editor gets completion, diagnostics and go-to-definition." }
{
"title": "Bootstrap from the seed",
"html": "<code>bin/</code> is not checked in, so it is created first; then <code>clang</code> assembles the compiler's own checked-in LLVM IR seed, and that compiler builds <code>bin/x</code>, the task runner. This is the only step Ludic cannot do for itself."
},
{
"title": "Build the toolchain",
"html": "<code>bin/x build</code> produces <code>ludicc</code>, <code>ludic</code>, <code>ludic-fmt</code> and <code>ludic-lsp</code> — all compiled by Ludic, from Ludic."
},
{
"title": "Compile &amp; run an example",
"html": "<code>bin/x app examples/games/snake.ludic</code> turns a <code>.ludic</code> file into a native binary. Run it to open a real window."
},
{
"title": "Go headless for tests",
"html": "<code>--headless</code> renders frames to a <code>.ppm</code> from piped input — deterministic output you can diff in CI."
}
],
"terminal_name": "zsh — ludic",
"terminal": [
{ "comment": "build the toolchain (once)" },
{ "cmd": "bin/x build" },
{ "blank": true },
{ "comment": "build and run an example (opens a window)" },
{ "cmd": "bin/x app examples/games/snake.ludic" },
{ "cmd": "./build/snake" },
{ "blank": true },
{ "comment": "deterministic headless render for tests" },
{ "cmd": "bin/x app examples/games/snake.ludic --headless" },
{ "cmd": "printf 'ddddwww' | ./build/snake_headless" },
{ "out": "→ writes out.ppm" }
{
"comment": "bootstrap the task runner (clang + the IR seed, once)"
},
{
"cmd": "mkdir -p bin && clang selfhost/ludicc.seed.ll -o bin/ludicc"
},
{
"cmd": "bin/ludicc tools/x/main.ludic -o bin/x"
},
{
"blank": true
},
{
"comment": "build the toolchain, then an example (opens a window)"
},
{
"cmd": "bin/x build"
},
{
"cmd": "bin/x app examples/games/snake.ludic"
},
{
"cmd": "./build/snake"
},
{
"blank": true
},
{
"comment": "deterministic headless render for tests"
},
{
"cmd": "bin/x app examples/games/snake.ludic --headless"
},
{
"cmd": "printf 'ddddwww' | ./build/snake_headless"
},
{
"out": "→ writes build/out.ppm"
}
]
},
"editors": {
"kicker": "Editor experience",
"title": "One language server, every editor.",
"intro": "<code>ludic-lsp</code> speaks LSP 3.17 over stdio: context-aware completion, diagnostics from the compiler itself, go-to-definition and rename across <code>import</code>ed files, and comment-preserving formatting. It even understands <code>```ludic</code> fences in Markdown.",
"list": ["VS Code", "JetBrains IDEs", "Neovim", "Helix", "Emacs", "Sublime Text", "Zed"],
"list": [
"VS Code",
"JetBrains IDEs",
"Neovim",
"Helix",
"Emacs",
"Sublime Text",
"Zed"
],
"note": "<code>bin/x tools</code> builds <span class=\"mono\">ludic-fmt</span> and <span class=\"mono\">ludic-lsp</span> — the same formatter runs as a CLI for pre-commit hooks and CI."
}
}

View file

@ -9,7 +9,7 @@ program Hello {
spawn Enemy { Position { column: 10, row: 2 }, Velocity { delta_x: 0, delta_y: 1 } }
}
# a handler declares the models it touches; the body
# a handler declares the entities it touches; the body
# runs once per match, each property bound by name.
@Queries(these: [Position, Velocity])
handler AdvancePositions phase FixedUpdate {