Stage raw files in upload/, publish with make sync-media/publish, and polish the lightbox: autoplay, remembered volume, Escape to close, and image/video icons without poster or caption clutter. Co-authored-by: Cursor <cursoragent@cursor.com>
Technical Kiwi Limited — Website
A small business site built with Go, templ (server-side HTML templates), and HTMX for gallery interactions.
Requirements
- Docker and Docker Compose
Quick start
cp .env.example .env # optional SMTP settings
make dev # live reload → http://localhost:7331
Make targets
| Target | Description |
|---|---|
make dev |
Dev with Air + templ browser live reload (default) |
make build |
Build production website image |
make up |
Run production website service |
make down |
Stop compose services |
make generate |
Run templ generate in dev container |
make tidy |
Run go mod tidy in dev container |
make logs |
Follow dev container logs |
make sync-media |
Copy photos and convert videos from upload/ → app/images/ |
make thumbs |
Generate gallery thumbnails and video posters under app/images/ |
make publish |
sync-media then thumbs |
Features
- Single-page layout: hero, services, gallery, contact
- Gallery loads via HTMX (
hx-get="/gallery") - Lightbox modal with previous/next navigation
- Contact form with SMTP relay (HTMX submit, no page reload)
- Serves gallery media from
app/images/(published copy; not in git) - Password-protected gallery admin at
/admin/
Gallery admin
Set both ADMIN_USER and ADMIN_PASSWORD in .env, then open http://localhost:8080/admin/ (or via your public URL).
- Sign in with username/password
- Upload JPEGs into an album (or create a new album folder)
- Delete images (removes originals and generated thumbs/hero)
- Changes appear on the public site immediately
Contact form (SMTP)
Set relay details in .env:
| Variable | Description |
|---|---|
SMTP_HOST |
Relay hostname (required) |
SMTP_PORT |
Port (default 587) |
SMTP_USER / SMTP_PASSWORD |
Auth if your relay requires it |
SMTP_FROM |
Envelope/header From address (required) |
SMTP_TO |
Where contact messages are delivered (required) |
SMTP_TLS |
auto, tls (465), or plain (25/local relay) |
If SMTP is not configured, the page shows a mailto fallback instead of the form.
Dev container
make dev mounts ./app and ./upload (raw media staging) plus your host Go caches (~/go/pkg/mod, ~/.cache/go-build by default). Override with GOMODCACHE / GOCACHE in .env.
Use http://localhost:7331 in the browser — the templ proxy injects live reload on .templ, .go, and .css changes. Port 8080 hits the app directly without auto-reload.
Only run dev or website at a time if you map both to the same host ports.
Production website mounts ./app/images into the container. It joins the external caddy Docker network for reverse proxy labels.
Project layout
app/
cmd/server/ HTTP server entrypoint
internal/gallery/ Scan and sort images from disk
internal/contact/ Form validation
internal/mail/ SMTP relay
internal/handlers/ Routes and HTMX partials
templates/ templ components (.templ → generated Go)
static/ CSS
images/ Published gallery (served at /images/)
upload/ Raw media — run `make sync-media` to publish into app/images/
Dockerfile Production image
Dockerfile.dev Dev image (Go + templ + Air)
docker-compose.yaml
.env.example
Gallery media
- Drop photos and videos into repo-root
upload/(album folders, same layout as the gallery). - Run
make sync-media— JPEGs are copied; videos are converted to H.264 MP4 inapp/images/. - Run
make thumbs(ormake publishfor both steps) — buildsthumbs/andhero/derivatives underapp/images/.
The site serves files from app/images/ at /images/…. Thumbnails and hero images are gitignored and rebuilt when sources are newer.
Deploy notes
After adding media, run make publish on the host before or after deploy. First request may take a moment while any missing thumbnails are generated.