Quick Start
Copy the folder. Everything the page needs is in
index.htmlandassets/(style.css,script.js). The CDN<script>tags for GSAP, ScrollTrigger and Lenis are already inindex.html.Add your photos. The page references eight images in
assets/img/:File Size Where hero.webp1600 x 2000 the portrait hero link-01.webp…link-06.webp800 x 1000 each the six link covers feature.webp1200 x 800 the Latest campaign cover Until they exist, a designed gradient placeholder (graded from the current palette) fills every slot, so the page looks finished from the first open. See Images below for the bundled bake pipeline, or just drop files with those exact names into
assets/img/.Rewrite the copy. Replace the brand name (
Noor Vidal), handle, bio, stats, the six link labels/meta, the Latest block and the footer with yours. Point eachhref="#"at your real destination.Pick a palette (optional). See Palettes below.
Sections and options
Every section is independent: the script guards each lookup, so you can delete
any block from index.html and the rest keeps working.
- Nav (
.nav) — wordmark and one CTA. It turns solid on scroll via adata-stuckattribute the script toggles. - Hero (
.hero) — name, handle, one-line bio and a<dl>of stats beside the portrait. The portrait drifts on scroll (parallax); the image is overscaled inside its frame so an edge is never uncovered. Edit or remove.hero__metaitems freely. - Link index (
.hub) — six rows, each a real<a>. Add or remove rows by copying a.link-rowblock; keep thedata-indexsequential and add a matchingdata-preview-imgcover in.link-previewand a.ph__tile--cNclass if you want a distinct placeholder tint. The01–06numbers and the meta line under each label are yours to change. - Latest (
.latest) — the newest drop or campaign: a wide cover with a small scrimmed tag, a title, a blurb and a button. - Social row (
.social) — plain text links; add or remove freely. - Footer (
.foot) — copyright, a "Built with" line, and a back-to-top link.
Palettes
The template ships four full palettes, each paired with its own display face
so the four read as four identities rather than one design in four colours.
They are a feature, not scaffolding:
each is a complete token block in assets/style.css — the default :root
(Runway) plus three :root[data-palette="…"] blocks at the foot of the file.
| Palette | data-palette |
Look | Display face |
|---|---|---|---|
| Runway | (default, no attribute) | glossy warm-black, champagne accent | Bodoni Moda 600, a high-contrast Didone |
| Neon | neon |
dark ground, electric magenta | Space Grotesk 700, a geometric grotesk |
| Editorial | editorial |
light cream ground, ink type, muted terracotta | Newsreader 600, a warm print serif |
| Studio | studio |
calm greige, warm sand accent | Figtree 600, a humanist sans |
The face and the colours are independent tokens (--font-display and
--font-display-weight sit in each block next to the colours), so keep the
palette and swap the face, or the other way round. All four faces are in the
Google Fonts <link> in index.html; a browser only downloads the one the page
uses, but once you have settled, trim the link to the family you kept. The body
face (Hanken Grotesk) is the same in every palette.
To use one, set the attribute on the <html> tag in index.html:
Code snippet omitted: it ships with the download.
To make one the permanent default, copy its token values into the :root
block at the top of style.css, then delete the alternate blocks. That leaves
the stylesheet stating one palette rather than four.
Each block restates every colour token and its --*-rgb triple. Every
translucent rule, scrim and placeholder gradient on the page resolves from those
triples, so a swap repaints the whole page — placeholders included — with no
stale colour left behind. Reskinning to a brand-new palette is a matter of
editing those five colours (and their triples) in one block.
All four palettes were contrast-checked as used, at size; the measured pairs and
the tightest one on the site are recorded in the style.css header comment.
Images
Images are meant to ship as bundled, optimised WebP derivatives with the template's grade baked into the pixels, so the set reads as one campaign and the files are art-directed rather than raw stock.
assets/img-manifest.jsonlists each output (outname,width,height,quality,grade). Point eachsrcat your source file or URL.- Bake them with
bun scripts/build-template-assets.ts --only link-hub-template, which crops, resizes and grades each source intoassets/img/at the exact namesindex.htmlreferences. - To swap a photo, either replace the file in
assets/img/directly, or edit itssrcin the manifest and re-run the bake. Setbrightnessper image so mixed sources match in perceived exposure.
If you would rather not use the pipeline, dropping correctly named files into
assets/img/ works too; the <img> width/height are set for layout stability.
Accessibility
- Reduced motion. With
prefers-reduced-motion: reduce, all reveals and the parallax are disabled through both a CSS media query and the matchMedia branch, and the index renders as the static thumbnail list. Nothing is left hidden. - No JavaScript. The page is a clean, readable document: nav, hero, all six
links, the Latest block, the social row and the footer are present and
reachable. The floating preview never appears, because nothing wakes it. The
link rows are plain
<a>elements, so they tab and open natively. - Touch. The cursor preview is fine-pointer only; coarse pointers get the static thumbnail index, fully tappable.
- Keyboard. Every row is a real link. Focus mirrors the hover spotlight — the focused row lights its underline and arrow and, when the enhanced index is active, brings up its cover — so a keyboard user gets the same cue a mouse does.
Dependencies
- GSAP 3.15.0 core + ScrollTrigger (CDN tags included)
- Lenis 1.3.17 smooth scroll (optional; the page works without it)
- Fonts: Bodoni Moda (display) and Hanken Grotesk (body), loaded from Google Fonts
No build step. No framework. Static HTML, CSS and one JavaScript file.
Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.