Editorial One Page Template

£10

A vertical-agnostic editorial one-pager whose cover exists twice: a chalk plate on top and an ink negative beneath it, revealed through a soft aperture that follows the cursor and stretches on the axis it is moving along.

ScrollTriggerSplitText Lenis intermediate
6 more details
mask-revealmouse-followscrubtext-animationscroll-revealstagger
Editorial One Page Template - website template preview

This template reads better at your own screen size than in the frame below:

About this template

A complete one-page site that takes its identity from print discipline rather than from a business type. The fictional occupant is Margin, an independent creative practice in Leeds, but nothing about the structure is tied to that: a strict column grid, hairline rules, marginalia set in the outer column, a drop cap, a ruled ledger of figures and two large pull quotes will carry a studio, a practice, a personal site, a product or a manifesto without being restyled.

Read the full template overview

The signature is the cover. The composition exists twice in the markup: the chalk-ground version in normal flow, and beneath it a full-bleed ink "negative" of the same thing, chalk type on ink with the artwork inverted. A soft circular aperture follows the cursor and reveals the negative through it, and the aperture is layered rather than literal: its radius eases behind the pointer, it stretches along the axis you are moving on and squashes across it, a one-pixel accent rim trails a little behind, and when the pointer settles it springs back to a circle. It reads like a plate being inspected under a moving light. On a touch screen the aperture drifts on a slow orbit of its own and springs to wherever you tap, and a real button opens it to full bleed on any device, which is also the keyboard route.

The second moment is quieter and just as useful: as the cover leaves, a scrubbed ScrollTrigger walks the large cover wordmark into the fixed top bar's own wordmark slot, measured live from the two elements so it lands exactly whatever the type size or viewport, then hands over invisibly.

Everything else is disciplined. Two typefaces, one ground, one accent, and three colour values that reskin the whole page. Replace the copy, the four designed plates (all CSS and inline SVG, so there are no images to license or optimise), the five ledger figures and the two quotes, point the contact links at your own inbox, and deploy the folder as static files. There is no build step.

What's included

8 items
  • Complete deployable one-page site: fixed bar, full-viewport editorial cover, lede with drop cap and marginalia, four designed plates, a ruled ledger, two pull quotes and a colophon with contact details
  • The torch cover: an ink negative of the whole composition masked to a soft aperture that follows the cursor, stretches on the axis of fast movement, trails a 1px accent rim and springs back to a circle when the pointer settles, all written from a single ticker pass
  • A docking wordmark: the large cover wordmark is walked into the fixed bar's slot on a scrubbed ScrollTrigger, measured live so it lands on the slot to the pixel, then hands over to the bar's own mark invisibly
  • A real coarse-pointer answer rather than a disabled one: the aperture drifts on its own slow orbit, springs to a tapped point with an elastic settle and resumes the drift without a jump, plus a "Reveal the plate" button that opens it to full bleed from the keyboard, on any device, and still works under reduced motion
  • Four plates of designed artwork with captions set in the margin: a chalk-on-ink halftone field, an eighteen-pass plotted contour in inline SVG, a typographic monogram and a registration study. No photography, no image files, nothing to license or optimise
  • Print-set detail throughout: masked headline lines, a drawn signature, a hairline-ruled ledger whose rules draw from the left row by row, and pull quotes split into lines with SplitText after the real faces have loaded
  • Three colour values with RGB triples and a measured contrast table in the stylesheet header, including which surfaces the accent is banned from and why; a reskin is a three-value edit. Prata and Lexend only, two faces doing five jobs
  • Degrades completely and cleans up after itself: with JavaScript off the cover is the plain chalk plate and every section reads in full, reduced motion is honoured in both CSS and the script's own motion branch, every section's selectors are guarded so any of them can be deleted, and Lenis is wired onto the GSAP ticker and entirely optional

Perfect for

6 use cases
  • Design studios and creative practices that want a type-led site with one memorable interaction
  • Independent consultants, architects and writers with a personal site where the work is mostly words
  • Product or manifesto pages where a single strong idea has to be stated and defended
  • Small practices publishing real figures, with a ledger section that reads like a printed statement rather than a stat grid
  • Any vertical that needs an editorial identity without commissioning photography, since all four plates are CSS and inline SVG
  • Learning masked reveals and pointer-driven masking: a working example of driving mask-size and mask-position from eased state on one rAF pass

How it works

5 sections

Structure

One script initialises the whole page with a single setup and teardown path, with one motion branch keyed on reduced motion and pointer type, so every device lands in exactly one branch. Every querySelector is guarded, so any section can be deleted and the rest keeps working. All reveal start states are double-gated behind a has-js class added synchronously in the head and a motion media query, so a blocked CDN and a reduced-motion preference both leave the complete static page.

The torch aperture

The negative plate carries a static radial-gradient mask; the script only ever writes mask-size and mask-position, because a custom property inside a gradient string cannot be tweened. Pointer position, radius and the two stretch factors live on one eased state object, and a single per-frame callback turns that object into the mask box and the rim's transform. The stretch comes from pointer velocity, biased toward whichever axis is moving faster and squashed on the other; the settle is an elastic tween back to a circle, fired when the pointer has been still for a tenth of a second.

The docking wordmark

A scrubbed ScrollTrigger runs from the top of the cover for just over half its height. Its x, y and scale are functions that measure the cover wordmark and the bar's wordmark live, so the tween's end state is wherever the slot actually is; the y value also cancels the scroll the page has done over the range, which is why the ease has to be none. At the end of the range the cover wordmark is switched off and the bar's own wordmark switched on in the same frame, at the same size and position, so the handover is invisible.

Accessibility and reduced motion

Under reduced motion nothing moves: the page is the plain chalk plate with every line set, Lenis is never created, and the aperture is not animated at all - but the reveal button still works, snapping the plate open through a CSS class. On a coarse pointer the aperture drifts autonomously and answers taps, so a phone visitor sees the same idea. The button is a real button with aria-pressed and a changing label, focusing it opens the aperture in the middle of the plate, and every interactive element has a visible focus ring.

Without JavaScript

There are no JS-built elements. The negative plate and the reveal control are both gated behind the has-js class, so with scripting off the cover is simply the chalk composition with its headline set, the wordmark sits in the bar as well as on the cover, and every section below reads top to bottom in full.

Plugins ScrollTrigger, SplitText
Difficulty Intermediate
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Questions about this template

What vertical is this for?

Deliberately none. It is an editorial one-pager for work that is mostly words: design studios, consultants, architects, writers, or a manifesto page where one idea has to be stated and defended. The ledger section suits small practices publishing real figures rather than a stat grid.

The torch effect follows the cursor. What happens on touch?

It gets a real answer rather than being switched off. The aperture drifts on its own slow orbit, springs to a tapped point with an elastic settle and resumes without a jump. There is also a Reveal the plate button that opens it to full bleed from the keyboard, on any device, and under reduced motion.

Do I need any images?

None. All four plates are designed artwork in CSS and inline SVG: a chalk halftone field, an eighteen-pass plotted contour, a typographic monogram and a registration study. Nothing to license, source or optimise, which is the practical reason a wordy practice picks this one.

How much work is a repalette?

Three values. The token block carries RGB triples and a measured contrast table in the stylesheet header, including which surfaces the accent is banned from and why. Two faces, Prata and Lexend, do five jobs between them, so the type system needs no decisions either.

Can I delete sections I do not need?

Yes. Every section's selectors are guarded, so the plates, ledger, pull quotes or colophon can be removed and the rest keeps working. With JavaScript off the cover is simply the plain chalk plate and every section reads in full.

Paid template

Purchase to unlock the code.

Buying Editorial One Page Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£10 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

  1. Copy the whole folder to your web server or any static host. There is no build step.
  2. In index.html, replace the wordmark in two places: .bar-mark in the fixed bar and .cover-mark on the cover. They have to match, because one docks into the other.
  3. Replace the cover copy. The headline is three hand-broken lines, and it appears twice (once in .plate--chalk, once in .plate--negative) so the negative stays in register. Edit both, or see "Deleting the torch" below.
  4. Replace the rest: the lede standfirst and its two marginal notes, the four plate captions, the five ledger rows, the two quotes, and the colophon details.
  5. Point the contact links at your own inbox: the mailto: on .mailto, the tel: in the details list, and the three placeholder social links (they currently point back at #colophon).
  6. Deploy. The page works with JavaScript disabled, so nothing else is required.

The GSAP scripts load from a CDN in index.html. Swap assets/script.js for assets/script.min.js in production if you prefer the smaller file.

The two signature moments

The torch, and the negative plate

How it is built. .cover-stage holds two copies of the cover composition. .plate--chalk is in normal flow and defines the height. .plate--negative is absolutely positioned over it with an ink background, chalk type and its artwork inverted, and it carries a static radial-gradient mask. The script writes only mask-size and mask-position, from one gsap.ticker pass; that is deliberate, because a custom property inside a gradient string cannot be tweened.

What responds to what. Pointer position, aperture radius and two stretch factors live on one state object eased by gsap.quickTo. Pointer velocity biases the stretch toward the axis you are moving on and squashes the other, so a fast horizontal flick smears the aperture sideways. The rim (.torch-rim, an inline SVG circle with vector-effect="non-scaling-stroke", so it stays 1px at any scale) lags behind with a simple lerp. When the pointer has been still for a tenth of a second, an elastic tween springs the stretch back to a circle.

Tuning it, all in the torch block of assets/script.js:

What Where Note
Aperture size baseR = Math.max(96, Math.min(215, Math.min(box.w, box.h) * 0.26)) A fraction of the stage's short edge, clamped
Edge softness the mask-image gradient in .has-js .plate--negative Widen the soft band past about 18% of the radius and it reads as a smudge rather than an edge
How far it stretches mag = speed * 0.46 in stretchTargets 0 disables the stretch entirely
How hard it springs back ease: 'elastic.out(1, 0.42)' in relax Raise the second number to damp it
Rim lag var k = 0.17 in render Higher is tighter; 1 removes the lag
Rim position on the edge lag.r * 0.82 in render Match this to the gradient's opaque stop if you change it
Touch drift path the two Math.sin terms in render The multipliers are speed, the * box.w * 0.28 terms are amplitude

Deleting the torch. Delete the whole .plate--negative block and the .cover-tools block from index.html. The script finds no negative plate, skips the entire torch section, and the cover is just the chalk plate. Nothing else changes, and you then only maintain one copy of the headline.

The docking wordmark

A scrubbed ScrollTrigger runs from the top of #cover for cover.offsetHeight * 0.55. Its x, y and scale are functions that measure [data-cover-mark] and [data-bar-mark] live, so the end state is wherever the bar's slot actually is, at any type size or viewport. The y value also has to cancel the scroll the page performs over the range, which is why the ease is none: any easing would push the wordmark off the straight line into the slot.

At the end of the range the cover wordmark is hidden and the bar's own wordmark shown in the same frame, at the same size and position, so the handover is invisible. Both are re-measured on resize and on every ScrollTrigger refresh.

  • Change the travel distance: geo.range in initDock.
  • Change where it lands: nothing to change. Restyle .bar-mark and the dock follows it.
  • Remove it: delete [data-bar-mark] from the bar. The dock is skipped and the cover wordmark simply scrolls away.

Sections and their options

Every section is independent: the script guards all of its selectors, so any of these can be deleted wholesale.

Section id Hooks the script reads
Fixed bar (header) data-bar (measured for anchor offsets), data-bar-mark (the dock's target slot)
Cover #cover data-cover-mark, data-cover-fade, data-stage, data-negative, data-torch-rim, data-torch-toggle, data-torch-label, data-lines + data-line, data-sign
Lede #lede data-reveal on the main block and on each marginal note
Plates #plates data-reveal-group (staggers its direct children), data-plate-art (a small scrubbed drift)
Ledger #ledger data-ledger, data-row, data-row-rule
Voices #voices data-reveal, data-split-lines (SplitText, line by line)
Colophon #colophon data-reveal
Attribute Put it on What it does
data-reveal any element Fades and lifts it in once, when its top passes 88% of the viewport
data-reveal-group a grid or list container Staggers its direct children instead of itself. The container stays visible; the children are what animate
data-split-lines a paragraph Splits it into lines with SplitText and reveals them in sequence. Falls back to a plain fade if SplitText is absent. Do not put it on the drop-capped standfirst: splitting a floated first letter into per-line blocks breaks the wrap around it
data-lines / data-line a headline / each line inside it Masked line reveal. Each line needs the .mask / .mask-in wrapper pair for the overflow mask
data-row / data-row-rule a ledger row / its hairline Row-by-row reveal with the rule drawing from the left
data-plate-art a plate's artwork box A small scrubbed vertical drift, so the artwork and its margin caption are never quite locked together
data-sign an SVG <path> Drawn on with stroke-dashoffset during the cover intro

Adding a section is a matter of adding the hooks; nothing is registered by index or by count, so nothing breaks when you delete a block.

Palette

Three colour values, declared once in :root at the top of assets/style.css, each with an RGB triple so every translucent value derives from a token and a reskin never leaves an old hue in a rule:

Code snippet omitted: it ships with the download.

Repaint those six values and the page reskins. The header comment above :root records the measured contrast table; read it before adjusting. The constraints that matter:

  • Body copy floor 4.5:1. --dim is ink at .68 alpha, 5.13:1 on the ground, and it carries every label, caption and secondary paragraph on the page. Do not thin it: .66 measures 4.83 and .62 measures 4.28, which fails. There is deliberately no second, fainter type variant.
  • The accent is banned on ink, as type and as a mark: marine on ink is 1.63:1, under both the 4.5 text floor and the 3:1 non-text floor. On any ink surface the accent's job is done by chalk. That is why the ink plate artworks carry no accent, and why the pull quote on the ink inset has a chalk quote mark where the one on the ground has a marine one.
  • Rules are split by job. --rule and --rule-faint (ink at .18 / .14, about 1.4:1) are decorative hairlines only: the plate grid, plate frames, the halftone bar. Any rule that structures content, meaning section rules, ledger rows and the bar underline, uses --rule-firm (ink at .50, 3.05:1) so it clears the 3:1 floor for non-text marks.
  • The aperture's marine rim straddles the chalk/ink boundary. It is decorative, aria-hidden, and carries no information; the aperture reads perfectly without it.

If you change the ground, re-measure --dim and the accent against it before shipping, and update the table in the header comment.

Type

Two faces, five jobs.

  • Prata carries every display size and nothing else: the wordmark, the cover headline, section titles, the ledger figures, the quotes, the contact address. It has a single weight, so emphasis comes from size and space, never from bolding it.
  • Lexend does all the reading, and at 0.68rem uppercase with .16em letter-spacing it is also every label, caption, folio and marginal note.

Both load from Google Fonts in the <head>. If you swap faces, keep the roles: one display serif with real contrast, one neutral sans that survives being set small and wide.

The cover display is capped at 4.4rem. It is meant to read as set, not shouted.

Artwork

There are no image files. Every piece of artwork is CSS or inline SVG, so there is nothing to license, optimise or lazy-load:

  • Plate I is two overlapping dot fields on ink, each with its own gradient mask, giving a halftone that fades across the frame.
  • Plate II is one SVG path repeated eighteen times with <use> at increasing offsets and opacities, plus one heavier pass in the accent. Edit the #ridge path and all nineteen lines change together.
  • Plate III is a single glyph in the display face over a hairline grid.
  • Plate IV is nested hairline boxes, crop marks, one solid block and a registration cross, all position: absolute percentages.
  • The cover's grid, dot field, registration mark and signature are the same kind of thing, and the signature is a single <path> drawn on during the intro.

To swap a plate for a photograph, replace the .art div's contents with an <img>, keep the aspect-ratio on .art, and give the image alt text plus width/height.

Accessibility

  • Reduced motion is honoured twice, in a CSS prefers-reduced-motion block and in a gsap.matchMedia branch. Nothing moves, nothing is hidden, Lenis is never created, and a preference changed mid-visit restores the static page. The reveal button still works: it snaps the plate open through a CSS class instead of easing it.
  • Touch and coarse pointers get a real version of the signature, not a disabled one: the aperture drifts on its own path and springs to a tapped point.
  • Keyboard: the reveal control is a real <button> with aria-pressed and a label that changes to match its state. Focusing it opens the aperture in the middle of the plate; blurring closes it. Every link and button has a visible focus ring, and a skip link jumps past the cover to the text.
  • No JavaScript: the negative plate and the reveal control are both gated behind a has-js class added synchronously in the <head>, so with scripting off the cover is simply the chalk composition with its headline set, and every section below reads in full. The same happens if the GSAP CDN is blocked, because the script removes that class when GSAP is missing.
  • Screen readers: the negative plate is a decorative duplicate and is aria-hidden, as are the grid, dot fields, artwork, rim, signature and ledger rules. The cover headline is the page's only <h1>; the negative's copy of it is a <p>.

Browser support

Modern browsers. Uses mask-image (prefixed and unprefixed), CSS custom properties, aspect-ratio, 100svh, clamp() and document.fonts. Not compatible with IE11.

Dependencies

  • GSAP 3.14+ (core)
  • ScrollTrigger - the dock scrub, the plate drift and every scroll reveal
  • SplitText - the pull quotes, split line by line (free since GSAP 3.12)
  • Lenis - optional smooth scrolling, wired onto the GSAP ticker so scrubbed tweens never read a scroll position a frame behind the one being painted. Delete the Lenis block in script.js and its <script> tag to drop it; nothing else refers to it.

All loaded from a CDN in index.html, deliberately without integrity hashes so a future patch release cannot break your deployed copy. If any script fails to load, the page degrades to the complete static document.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

Everything in the catalogue, plus everything we release next.