Le post d'ouverture annonçait un studio de deux personnes, dans son extrait comme dans son corps. L'extrait remonte sur la home, donc la page d'accueil contredisait le compteur "3 People" affiché juste au-dessus. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Highland Games Studio — Website
Marketing and devblog site for Highland Games Studio. Where summits become worlds.
Built with Next.js 16 (App Router), React 19, Tailwind CSS 4 and Framer Motion.
Devblog posts are authored in MDX and rendered with next-mdx-remote.
Getting started
npm install
npm run dev
Open http://localhost:3000.
Scripts
| Command | Description |
|---|---|
npm run dev |
Start the dev server |
npm run build |
Production build |
npm run start |
Serve the production build |
npm run lint |
Run ESLint |
Asset generation scripts (require sharp, already a devDependency):
node scripts/build-favicon.mjs # favicon + app icon from the mountain mark
node scripts/build-wordmark.mjs # transparent wordmark from "Full Logo.png"
Project structure
app/ Routes, layouts, server actions, metadata (sitemap/robots/feed/OG)
app/components/ui/ Design-system primitives (Section, SectionHeading, ButtonLink, Logo…)
app/components/layout/ Navbar, Footer, SocialLinks
app/components/game/ GameCard, ScreenshotGallery
app/components/home/ Home hero
app/components/devblog/ PostList
app/components/forms/ ContactForm, Newsletter
content/devblog/ Devblog posts as .mdx (frontmatter: title, date, excerpt, tags, game)
lib/ Data + helpers (games registry, devblog loader, site config, formatting)
public/ Static assets (studio logos, game art, hero video)
scripts/ One-off asset build scripts
Content lives in lib/, never in a page
lib/site.ts— studio identity, navigation, contact channels, social accounts, brand assets.lib/games.ts— the games registry: title, description, genres, key art, banner and screenshots.
Pages read from these. That is what keeps the home page and /games/[slug] in sync — the
featured block, the "From the workshop" gallery and the game page all render the same record.
Design rule: no text over artwork
The Emberwild key art is light cream and the in-game captures are bright green, so body text
laid over them is unreadable at any gradient strength. Cards therefore put the image on one
panel and the text on a solid bg-surface panel; the game page shows the banner alone and
places the title underneath. Keep it that way when adding new layouts.
Adding a game or new screenshots
Drop the files in public/image/Games/<Game>/, then add or edit the entry in lib/games.ts:
{
slug: "emberwild",
title: "Emberwild",
status: "in-development", // in-development | coming-soon | released
tagline: "One readable line.",
description: "Full pitch, shown in the About block.",
genres: ["Survival", "Crafting"],
releaseWindow: "TBA",
keyArt: "/image/Games/Emberwild/Cover.png", // illustration, shown contained
banner: "/image/Games/Emberwild/Capture_1.png", // wide in-game shot, page banner
screenshots: [
{ src: "/image/Games/Emberwild/Capture_2.png", caption: "Autumn treeline" },
],
}
The gallery, the home page teaser, /games, the sitemap and the JSON-LD all pick it up.
Configuration
Copy .env.example to .env.local and fill in the values.
- Site URL — set
NEXT_PUBLIC_SITE_URLper environment (used by metadata, sitemap, robots, RSS and JSON-LD). Defaults tohttps://highlandgamesstudio.com— seelib/site.ts. - Contact form (Resend) — the contact form sends email via Resend.
Set
RESEND_API_KEY,CONTACT_TO_EMAIL(who receives messages) andCONTACT_FROM_EMAIL(sender, must be on a domain verified in Resend). Without these, the form returns a friendly error instead of sending. Seeapp/actions/contact.ts.
Adding a devblog post
Create a file in content/devblog/, e.g. 2026-06-01-my-post.mdx:
---
title: "My post title"
date: "2026-06-01"
excerpt: "One-line summary for listings and SEO."
author: "Highland Games Studio"
tags: ["devlog"]
game: "emberwild"
---
Your MDX content here.
Deployment
Pushes to master deploy to the Hetzner server via the Gitea Actions workflow
(.gitea/workflows/deploy.yml), which SSHes in, pulls, builds, and restarts the
PM2 process (highland, in /var/www/highland).
Requires these repo secrets (Gitea → repo → Settings → Actions → Secrets):
SSH_HOST, SSH_USER, SSH_PORT, SSH_PRIVATE_KEY.
Server-side prerequisites (one-time):
- A registered Gitea
act_runnerwith theubuntu-latestlabel. /var/www/highlandis a clone of this repo whoseorigincan pull the private Gitea repo non-interactively (deploy key or token in the remote URL).- Node +
pm2installed, with the app running as thehighlandprocess.
Known TODOs
See TODO.md for the full list. The two blockers are the 84 MB hero video
(public/Video/Header_Video.mp4) and the social links, which are still unpublished
(SOCIAL_LINKS in lib/site.ts — entries with href: null are hidden rather than
rendered as dead # links).