Files
ulicraft-server-v1/plan/09-landing.md
Oier Bravo Urtasun c8a6c77a8e feat(landing): rework — es default, 2-step join, roster + account polish
- 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>
2026-06-10 22:03:18 +02:00

7.5 KiB
Raw Blame History

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 (own package.json, tsconfig). pnpm only.
  • astro.config.mjsoutDir: "../www". Build overwrites www/; www/ is gitignored (generated artifact).
  • BASE_DOMAIN env read at build time in src/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: pixelated keeps 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 | end
  • hero: centered (default) | split | spotlight
  • headFont: pixelify (default) | 8bit | silkscreen
  • dust: 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].astro with getStaticPaths emits all three routes (/, /en/, /eu/). No runtime/JS routing. The default locale lives at the bare root: its getStaticPaths entry is { lang: undefined }locale: "es"; en/eu get an explicit prefix. The LANG_*_PATH maps in ui.ts mirror this (es → no prefix). Changing the default = swap which locale is undefined in all four pages' getStaticPaths + the LANG_*_PATH maps.
  • Translatable copy lives in src/i18n/ui.ts (ui[locale]), keyed identically across locales. site.ts holds only language-neutral config (URLs, theme, launcher files, stat/feature structure).
  • Product / in-game literals never translated — site.ts LITERALS (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 · EU switcher (.lang-switch); active locale highlighted. <html lang> + <link rel="alternate" hreflang> set per page.
  • Adding a locale: extend the Lang union + ui/LANG_* maps in ui.ts, add a getStaticPaths entry. No template change.

Content (real modded-LAN flow, all in site.ts)

  • Hero: wordmark, eyebrow Modded LAN · NeoForge 1.21.1, tagline, sub, server address mc.${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.

Page links to fixed names so they survive launcher version bumps:

  • fjord-windows-setup.exe
  • fjord-macos.dmg
  • fjord-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 logolanding/public/logo.png (current 707×148). Referenced as /logo.png in the hero ([...lang].astro, the .hero-logo img). Replace the file; keep a wide transparent PNG. Update the width/height attrs if the aspect changes.
  • Faviconlanding/public/favicon.png (square, ~175×175). Referenced as /favicon.png in 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 the Creeper.astro SVG. To change it, set server-icon.png on the Minecraft server (64×64), not in the landing.
  • Features icons — two options (PixelIcon.astro, fed by site.ts features[]):
    1. Procedural glyph (default): pick glyph ∈ shield|diamond|orb|heart and gold per feature in site.ts. Add a glyph by editing the 7×7 GLYPHS map.
    2. Custom image: drop a file in landing/public/icons/<name>.(png|svg) and add img: "/icons/<name>.png" to that feature's entry in site.ts features[]. PixelIcon renders the image (.picon-img) instead of the glyph.

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 → verified www/ output + screenshot
  • Extend fetch-launcher.sh: symlink stable names → versioned files
  • Confirm ingress serves apex from ./www and /launcher/latest/* from ./launcher
  • Optional: try mood: nether / hero: split for party day
  • Optional: wire real player count (option C) via mc-monitor JSON if wanted