- Default language Spanish at root /, English moves to /en/ (eu unchanged):
getStaticPaths {lang:undefined}->es + LANG_*_PATH maps swapped in ui.ts.
- Section order: hero -> join -> status -> members -> mods -> features.
- Join flow 3 -> 2 steps (drop the Connect/IP step; launcher handles the
address). Remove the hero IP CopyChip. Header CTA renamed Play/Jugar/Jolastu
and gains Register + Login links on all four pages.
- Account page: hide the whole login block once authenticated (not just the
form), add an avatar Refresh button (cache-busted NMSR re-fetch), flatten the
avatar (no bevel). New i18n key account.refresh.
- Register page: new REGISTRATION_SHOW_INVITE env (default false/hidden) toggles
the invite-code field independently of the REGISTRATION_MODE backend gate.
- Members roster: drop the `admin` account, render full-body via a new
ROSTER_AVATAR_MODE (default fullbodyiso); portrait tiles.
- Style: flatten emboss on feature icons (.picon) + player tiles (.roster-tile).
PixelIcon gains optional image support (/icons/<name>.png|svg).
- Favicon now /favicon.png (committed) on all pages.
- Docs: plan/09-landing.md Assets section (logo/favicon/features-icons drop
points) + default-language note; .env.example documents the two new vars.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.5 KiB
Landing page — guest onboarding site
Summary
Apex ulicraft.net serves a static page guiding guests through joining:
download launcher → add account → import modpack → connect. Built with Astro +
TypeScript in ./landing/; pnpm run build emits straight into ./www/,
which Caddy serves at the apex (behind host nginx). No runtime framework —
output is plain HTML/CSS plus one tiny vanilla script (copy-to-clipboard +
scroll reveal).
The visual is the "Carved from stone" pixel design ("Claude Design",
landing/design/): dark overworld palette, MC-GUI bevel buttons, pixel type,
creeper mark, server-list panel. That design was a React/Babel-via-CDN prototype
for a fictional public SMP; it was ported to static Astro with the real
modded-LAN content and vendored assets.
Stack & wiring
landing/— Astro project (ownpackage.json,tsconfig). pnpm only.astro.config.mjs→outDir: "../www". Build overwriteswww/;www/is gitignored (generated artifact).BASE_DOMAINenv read at build time insrc/data/site.ts(process.env.BASE_DOMAIN ?? "ulicraft.net"). Bake before deploy:BASE_DOMAIN=ulicraft.net pnpm run build.- Logo: wide wordmark from the design at
landing/public/logo.png(hero + favicon).image-rendering: pixelatedkeeps it crisp.
Source layout
landing/
├── design/ # "Claude Design" prototype (reference, not built)
├── public/
│ ├── logo.png # hero wordmark (from design/assets/ulicraft-logo.png)
│ └── fonts/ # vendored woff2 + fonts.css (self-contained)
└── src/
├── data/site.ts # single source: content + theme knobs
├── styles/main.css # ported design system (mood/hero/bevels)
├── components/ # Creeper, PixelIcon, CopyChip, ServerListPanel
└── pages/index.astro # composition + copy/reveal script + dust
Fonts — vendored locally
The design pulled fonts from Google Fonts CDN; we vendor them instead to keep the
page self-contained. tooling/fetch-fonts.sh downloads the woff2 + a
URL-rewritten fonts.css into
landing/public/fonts/. index.astro links /fonts/fonts.css. Re-run the
script only if the font set changes (keep families in sync with main.css
--font-*): Pixelify Sans, Space Grotesk, Press Start 2P, Silkscreen, VT323.
Theme config (replaces the design's in-browser TweaksPanel)
site.ts theme is baked at build — edit + rebuild to change:
mood:grass(default) |nether|endhero:centered(default) |split|spotlightheadFont:pixelify(default) |8bit|silkscreendust: floating hero particles (CSS animation; respects reduced-motion)
index.astro sets <html data-mood data-hero data-head> + --font-head from these.
Languages (i18n)
Three locales, static, build-time — Spanish (default, /),
English (/en/), Euskera (/eu/).
- One template
src/pages/[...lang].astrowithgetStaticPathsemits all three routes (/,/en/,/eu/). No runtime/JS routing. The default locale lives at the bare root: itsgetStaticPathsentry is{ lang: undefined }→locale: "es"; en/eu get an explicit prefix. TheLANG_*_PATHmaps inui.tsmirror this (es → no prefix). Changing the default = swap which locale isundefinedin all four pages'getStaticPaths+ theLANG_*_PATHmaps. - Translatable copy lives in
src/i18n/ui.ts(ui[locale]), keyed identically across locales.site.tsholds only language-neutral config (URLs, theme, launcher files, stat/feature structure). - Product / in-game literals never translated —
site.tsLITERALS(authlib-injector,Multiplayer,Add Server). OS names + version too. - Sentences with links/kbd are split into parts (pre/post/link, pa/pb) so markup stays in the template, not the strings.
- Nav has a pixel
EN · ES · EUswitcher (.lang-switch); active locale highlighted.<html lang>+<link rel="alternate" hreflang>set per page. - Adding a locale: extend the
Langunion +ui/LANG_*maps inui.ts, add agetStaticPathsentry. No template change.
Content (real modded-LAN flow, all in site.ts)
- Hero: wordmark, eyebrow
Modded LAN · NeoForge 1.21.1, tagline, sub, server addressmc.${BASE_DOMAIN}(copy chip), "How to Join". - Status: static server-list panel (no fake live count — option B) + 4 honest stat tiles (Mods / NeoForge ver / Player slots / Backups).
- Features ×4: kitchen-sink pack · self-hosted Drasl identity+skins · one-click packwiz · built-to-last (homelab + 6h backups).
- How to Join ×4: 1 Fjord launcher (3 OS) → 2 authlib account
(
auth.${BASE_DOMAIN}/authlib-injector) + register link → 3 packwiz import (pack.${BASE_DOMAIN}/pack.toml) → 4 connect. - Footer: Mojang trademark disclaimer.
Launcher download links (Option A — stable symlinks)
Page links to fixed names so they survive launcher version bumps:
fjord-windows-setup.exefjord-macos.dmgfjord-linux-x86_64.AppImage
tooling/fetch-launcher.sh must create these as symlinks under
launcher/latest/ pointing at the real versioned assets. Keep the names in
sync between site.ts and the script. Mounted so they resolve at
/launcher/latest/… (see Caddy 10-static.caddy).
Assets — where to drop images
All static images live under landing/public/ and are referenced by an absolute
/path (Astro copies public/ to the site root verbatim). After changing any,
rebuild the landing (pnpm run build).
- Header / hero logo —
landing/public/logo.png(current 707×148). Referenced as/logo.pngin the hero ([...lang].astro, the.hero-logo img). Replace the file; keep a wide transparent PNG. Update thewidth/heightattrs if the aspect changes. - Favicon —
landing/public/favicon.png(square, ~175×175). Referenced as/favicon.pngin the<link rel="icon">of all four pages ([...lang].astro,register,account,fjord). Replace the file. - Server-status icon — NOT a file you place here. The ServerCard pulls the
live server icon over the SLP query (
ServerCard.astro, the favicon data-URI), falling back to theCreeper.astroSVG. To change it, setserver-icon.pngon the Minecraft server (64×64), not in the landing. - Features icons — two options (
PixelIcon.astro, fed bysite.tsfeatures[]):- Procedural glyph (default): pick
glyph ∈ shield|diamond|orb|heartandgoldper feature insite.ts. Add a glyph by editing the 7×7GLYPHSmap. - Custom image: drop a file in
landing/public/icons/<name>.(png|svg)and addimg: "/icons/<name>.png"to that feature's entry insite.tsfeatures[].PixelIconrenders the image (.picon-img) instead of the glyph.
- Procedural glyph (default): pick
Tasks
- Vendor fonts (
tooling/fetch-fonts.sh) →public/fonts/ - Port design → static Astro (components + main.css + index.astro)
- Real content in
site.ts; theme knobs replace TweaksPanel - i18n: en/es/eu via
[...lang].astro+i18n/ui.ts; nav language switcher BASE_DOMAIN=ulicraft.net pnpm run build→ verifiedwww/output + screenshot- Extend
fetch-launcher.sh: symlink stable names → versioned files - Confirm ingress serves apex from
./wwwand/launcher/latest/*from./launcher - Optional: try
mood: nether/hero: splitfor party day - Optional: wire real player count (option C) via mc-monitor JSON if wanted