- 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>
146 lines
7.5 KiB
Markdown
146 lines
7.5 KiB
Markdown
# 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.mjs` → `outDir: "../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.
|
||
|
||
## Launcher download links (Option A — stable symlinks)
|
||
|
||
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 logo** — `landing/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.
|
||
- **Favicon** — `landing/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
|
||
|
||
- [x] Vendor fonts (`tooling/fetch-fonts.sh`) → `public/fonts/`
|
||
- [x] Port design → static Astro (components + main.css + index.astro)
|
||
- [x] Real content in `site.ts`; theme knobs replace TweaksPanel
|
||
- [x] i18n: en/es/eu via `[...lang].astro` + `i18n/ui.ts`; nav language switcher
|
||
- [x] `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
|
||
```
|