- HTML 70%
- CSS 19.1%
- JavaScript 9.8%
- PHP 1.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| assets | ||
| css | ||
| deploy | ||
| js | ||
| .gitignore | ||
| .htaccess | ||
| 400.html | ||
| 403.html | ||
| 404.html | ||
| 422.html | ||
| 500.html | ||
| 502.html | ||
| 503.html | ||
| contact.php | ||
| favicon.ico | ||
| index.html | ||
| LICENSE | ||
| lore.html | ||
| maintenance.html | ||
| privacy.html | ||
| README.md | ||
| robots.txt | ||
| sitemap.xml | ||
| terms.html | ||
| worlds.html | ||
Starfall
A static personal site template. Plain HTML, one stylesheet, one script, no no framework, and no third-party requests at runtime, fonts and icons are self-hosted, so no CDN or analytics provider ever sees a visitor.
Built for yusarina.dev and open-sourced so others can use it. Everything identifying is swappable; see Making it yours.
Design: a deep blue-black ground with a parallax starfield, accented in two colours sampled from a character illustration.
What you get
- Home, worlds/portfolio, privacy, terms, and seven themed error pages
- A hidden multi-act story with a puzzle chain (
lore.html), delete it if you don't want one - A fully self-contained maintenance page that renders even when the rest of the server is unreachable
- A PHP contact form with a rotating question challenge and a honeypot
- Responsive,
prefers-reduced-motionandprefers-contrastaware, dark-first - Generated Open Graph cards
Requirements
Static hosting is enough for everything except the contact form, which needs PHP-FPM and a working local MTA. Nothing else has dependencies: no Node, no build pipeline.
Setup
git clone <this repo> mysite
cd mysite
The repo ships without images (see What is not included), so add your own before deploying.
- Serve it. Any static host works for a first look:
python -m http.server 8000 - Configure nginx using
deploy/nginx.conf.example. Replace everyexample.com, then check the three lines markedADJUST. - Get a certificate:
sudo certbot --nginx -d example.com -d www.example.com - For the contact form: install PHP-FPM and an MTA, then point
fastcgi_passat the right socket. See Contact form.
Making it yours
Search and replace, in roughly this order:
| Find | Replace with |
|---|---|
yusarina.dev |
your domain: appears in canonical tags, OG tags, sitemap.xml, robots.txt |
Yusarina |
your name: page titles, nav brand, footer |
hello@yusarina.com |
your contact address: contact.php and the legal pages |
Then:
- Content: rewrite
index.html. The nav, footer and icon sprite are duplicated across pages, so a change to one needs applying to all. - Colour: the whole palette is CSS custom properties in the token block at
the top of
css/starfall.css. Change those and the entire site follows. - Legal pages:
privacy.htmlandterms.htmlare written for a UK sole individual with no analytics and no cookies. They will not be accurate for you. Read them line by line, they name specific hosting and email providers, a retention period, and a legal basis. Wrong details are worse than none. - Lore: delete
lore.html,js/lore.jsandjs/lore-data.js, plus the footer link, if you don't want an easter egg. - Fonts: Chakra Petch, Inter and JetBrains Mono, all SIL Open Font
License. Swap the files in
assets/fonts/and the@font-faceblock at the top ofcss/starfall.css.
Structure
index.html home
worlds.html portfolio / gallery with lightbox
privacy.html privacy policy
terms.html terms of use
lore.html hidden story + puzzle chain (optional)
maintenance.html self-contained, zero external requests
400|403|404|422|500|502|503.html
contact.php contact form backend
css/starfall.css the entire design system, @font-face included
js/starfall.js starfield, nav, lightbox, contact form
js/lore.js puzzle chain
deploy/ server config
assets/fonts/ woff2 subsets (latin + latin-ext)
assets/Images/ artwork, OG cards, screenshots
assets/icons/ favicon + marks
Colour
Accents are sampled from the character illustration rather than picked by eye, which is why they feel like they belong to the artwork. If you swap the art, sample your own: pull the dominant hues out of the image and use those.
Values and their WCAG contrast ratios are documented in the token block at the
top of css/starfall.css. The convention is:
- one colour for structure and navigation
- one for identity, emphasis and live state
- a highlight tone for small accents only
- a decorative tone for gradients, never for text
Effects
One canvas (#starfield) and one static overlay (.atmosphere). The starfield
caps device pixel ratio at 1.5, caps star count by area, starts only once the
page is idle, and stops entirely when the tab is hidden.
backdrop-filter is used only on the nav. Blurring several large regions
over an animating canvas re-rasterises them every frame, which is the fastest
way to make a page feel sluggish on integrated graphics.
Server
The included .htaccess is an Apache artefact. If you run nginx it is never
read, and everything it describes is implemented in
deploy/nginx.conf.example instead. That file covers:
- extensionless URLs, and a 301 from the
.htmlform so each page has exactly one indexable URL - themed error pages
- maintenance mode
- security headers, with the inheritance trap documented
- cache rules per file type
- denials for dotfiles,
deploy/, and stray.php
Never reload without nginx -t first: a syntax error takes down every site on
the box, not just this one.
Maintenance mode
Create an empty .maintenance file in the document root, delete it to turn off.
nginx then returns 503 with Retry-After and serves maintenance.html.
That page inlines its fonts, CSS, JS and icons, so it renders even when nothing else on the server is reachable. It also polls the origin on a backoff and reloads itself once the site answers 200.
Contact form
contact.php uses PHP's mail(), which shells out to sendmail_path. A
local MTA is a hard dependency, and without one, mail() returns false and the
endpoint answers 500. That is not obvious from the code, and a server rebuild
silently removes it.
sudo apt install postfix # "Internet Site", system mail name = your domain
ls -l /usr/sbin/sendmail # must exist, this is what mail() calls
sudo tail -20 /var/log/mail.log # the real diagnostic; look for status=sent
Three things have to line up, and each fails differently:
| symptom | cause |
|---|---|
200, application/octet-stream |
no PHP block in nginx: the source is being served |
| 502 | PHP-FPM socket path wrong (ls -l /run/php/) |
| 500 | PHP ran, mail() failed, usually no MTA installed |
Test CLI mail before testing the form, so you are debugging one thing at a time.
Known limitations
- The challenge answer is only verified client-side;
contact.phpchecks the field is present, not correct. A direct POST bypasses it. The honeypot is the only server-side spam check. Harden this if you get abuse. - Mail is sent as
noreply@yourdomain. If it bounces, check that your SPF record authorises the sending IP and that reverse DNS matches.
What is not included
The MIT licence covers the code. It does not cover artwork, so a fresh clone renders with missing images, which is intentional, not a bug.
Gitignored the character illustration and its generated variants, the OG cards
that composite it, screenshots, third-party marks, the live server config, and
.maintenance (committing that would 503 the site on next deploy).
Fonts are included, all three are SIL Open Font License and freely redistributable.
Notes
- Error pages and the lore page are
noindexand excluded fromsitemap.xml robots.txtallows search/answer AI agents and blocks training crawlers; adjust to taste
License
MIT for the code. See terms.html for the distinction between the code and the
site content.