feat(landing): pixel design ported to static Astro + en/es/eu i18n

Adopt the "Carved from stone" pixel design (landing/design/) as the apex
onboarding page, ported from its React/Babel-via-CDN prototype to static
Astro so it works on an offline LAN. Real modded-LAN content replaces the
prototype's fictional public-SMP copy.

- Vendor web fonts locally (tooling/fetch-fonts.sh -> public/fonts/) so the
  page renders without the Google Fonts CDN at party time.
- Port the design system (main.css: mood/hero/bevels) and split markup into
  Astro components (Creeper, PixelIcon, CopyChip, ServerListPanel).
- site.ts holds language-neutral config + theme knobs (mood/hero/headFont/
  dust) that replace the design's in-browser TweaksPanel; LITERALS holds
  never-translated product/in-game terms.
- i18n: English (/), Spanish (/es/), Euskera (/eu/) generated from one
  [...lang].astro via getStaticPaths; copy lives in src/i18n/ui.ts. Nav has
  an EN/ES/EU switcher; html lang + hreflang set per page.
- Status panel shows a static server-list row + honest stat tiles (no fake
  live count). Copy + scroll-reveal are the only client JS.

Build emits to ../www (gitignored). Euskera strings pending a native review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 18:18:36 +02:00
parent 5cec2993d7
commit 3d52951355
60 changed files with 3388 additions and 228 deletions

View File

@@ -1,11 +1,28 @@
// Single source of truth for the landing page content.
// Single source of truth for the landing page's *non-translatable* config:
// domains, URLs, theme, launcher files, and the structural shape of the
// stat/feature lists. Translatable copy lives in src/i18n/ui.ts.
// BASE_DOMAIN is read at build time; falls back to ulicraft.local.
const BASE_DOMAIN = process.env.BASE_DOMAIN ?? "ulicraft.local";
// Minecraft / pack facts, surfaced in a few places.
const MC_VERSION = "1.21.1";
export const site = {
name: "Ulicraft",
tagline: "LAN-party Minecraft server",
baseDomain: BASE_DOMAIN,
mcVersion: MC_VERSION,
// Design knobs (replace the design's in-browser TweaksPanel). Baked at build.
// mood: "grass" | "nether" | "end"
// hero: "centered" | "split" | "spotlight"
// headFont: "pixelify" | "8bit" | "silkscreen"
// dust: floating pixel particles in the hero
theme: {
mood: "grass",
hero: "centered",
headFont: "pixelify",
dust: true,
},
// Connect target shown to guests. No port needed — a dnsmasq SRV record
// (_minecraft._tcp.mc.<domain>) points clients at 25565.
@@ -21,10 +38,35 @@ export const site = {
// Caddy local CA root — guests import this to trust the offline asset mirror.
caCertUrl: `http://${BASE_DOMAIN}/ca.crt`,
// Static server-list panel (Status section). No fake live count — see plan
// 09-landing.md option B.
status: {
slots: 10,
},
// Honest stat tiles. Keys (numbers/versions) are language-neutral; labels
// come from ui.ts (same order).
stats: [
{ key: "50+" },
{ key: MC_VERSION },
{ key: "10" },
{ key: "6h" },
],
// Feature cards. glyph ∈ shield|diamond|orb|heart; gold = gold pixel fill.
// Text (h/p) comes from ui.ts (same order).
features: [
{ glyph: "diamond", gold: true },
{ glyph: "shield", gold: false },
{ glyph: "orb", gold: false },
{ glyph: "heart", gold: true },
],
// Launcher downloads. Stable filenames are symlinks created by
// tooling/fetch-launcher.sh — keep these in sync with that script.
// tooling/fetch-launcher.sh — keep these in sync with that script. OS names
// and hints are technical, kept language-neutral here.
launcher: {
name: "FjordLauncherUnlocked",
name: "Fjord Launcher",
base: "/launcher/latest",
downloads: [
{ os: "Windows", file: "fjord-windows-setup.exe", hint: "x64 installer" },
@@ -33,3 +75,18 @@ export const site = {
],
},
} as const;
// Product / in-game UI literals — never translated (they match what guests see
// on screen or type verbatim).
export const LITERALS = {
authlib: "authlib-injector",
multiplayer: "Multiplayer",
addServer: "Add Server",
} as const;
// Head-font CSS stacks, keyed by theme.headFont.
export const HEAD_FONTS: Record<string, string> = {
pixelify: "'Pixelify Sans', system-ui, sans-serif",
"8bit": "'Press Start 2P', monospace",
silkscreen: "'Silkscreen', system-ui, sans-serif",
};