01 · Overview
A darkroom, not a gallery
Photographers' websites usually make one of two mistakes: a lightbox grid that treats every image as interchangeable, or a full-bleed slideshow that lets you look at nothing for very long. For this concept, I imagined Noctis as a studio that grades every frame by hand and takes eighteen weddings a year. The site had to be as selective as the booking calendar.
So it behaves like a darkroom. The ground is near-black, the only colour is a signal red used the way a camera uses it, reveals arrive as a shutter wipe rather than a fade, and the work is presented on a strip you drag — a contact sheet, not a carousel.
02 · The idea
Available light, hand-graded
Red is a signal
Record dots, frame counters, one CTA. Never a background, never decoration — exactly the discipline of the equipment.
Wipes, not fades
Every reveal is a clip-path travelling in one direction. It reads as a shutter, which is the one metaphor this brand has earned.
A cursor that behaves
A custom dot, on fine pointers only, that never replaces the system cursor over text or form fields.
03 · Goals
What the page had to do
Show work at a size worth showing
A horizontal strip where each frame is big enough to judge, and moving through it is a deliberate act.
Explain three disciplines without three pages
Weddings, editorial and commercial as one section with one grade, because the studio treats them the same way.
Publish real availability
A booking diary that opens on the first of the month rather than a form that promises someone will be in touch.
Never trap the reader in the dark
If the script fails, the
onerrorhandler strips the JS class so nothing stays hidden behind a wipe that will never run.
04 · Build order
Ten modules, in order
The grade
Nineteen custom properties: near-black, a lifted black for panels, three greys, and the two reds. The lifted black exists so panels read without a border.
Header and overlay nav
A condensing header and a full-screen menu with a tab trap — the first two modules, because everything else sits under them.
The wipe
One easing curve, one clip-path direction, applied through a single class so every section reveals identically.
The drag rail
Pointer capture, arrow buttons, a live counter and keyboard focus safety — the largest single module in the build.
The diary and the form
Availability computed from the current date, then a five-field booking enquiry that quotes a reference back.
05 · Architecture
Three files
├── index.html 29.8 KB — 6 sections, 10 photographs
├── styles.css 39.4 KB — 19 custom properties, 19 media queries
└── script.js 25.6 KB — 795 lines, 10 numbered modules
The stack
- Pointer capture
- clip-path
- Custom cursor
- IntersectionObserver
- Date maths
- Marcellus
- Figtree
Accessibility
- Tab trap
- Offset anchors
- Keyboard rail
- onerror fallback
- Reduced motion
- pointer: fine gate
06 · What fought back
What fought back
Drag-to-scroll built with document-level mousemove listeners breaks the moment the cursor leaves the element, and leaks a listener every time the pointer is lost.
setPointerCapture. The element keeps receiving events wherever the cursor goes, and the browser releases the capture on pointerup or pointercancel without any cleanup code.
A drag that finishes on top of a link fires a click, so releasing the mouse after moving the rail navigates away from the page.
Track the total travel and swallow the click in the capture phase if it exceeds a few pixels. Below the threshold it is a click; above it, it was a drag.
A custom cursor is the fastest way to make a site feel broken: it lags on a trackpad, disappears over inputs and is meaningless on touch.
Gated behind (pointer: fine), so it never exists on touch, and the real cursor is restored over every interactive element rather than hidden globally.
Clip-path reveals hide content by default, and this page is the darkest in the set — a failed script would leave a black screen with no explanation.
The script tag carries an onerror that removes the JS class, so a failed load reverts every wipe to its settled state instead of hiding the studio's entire portfolio.
07 · Worth keeping
The parts worth keeping
Selected Works
A drag strip with arrows, a counter and keyboard paging. Every input method reaches every frame.
The booking diary
Availability derived from the actual date, opening on the first of the month — real scarcity rather than a claimed one.
The camera HUD
A frame readout in the hero that borrows the viewfinder's own typography. Pure CSS, no canvas.
We keep the lights off
An atelier section that explains the grade — the one place the studio talks about method instead of showing results.
08 · Run it live
Run it here
The real build, embedded and running. It loads on demand — nothing is fetched until you ask for it.
Skip the live preview
The real build, running in the page. The live preview is an embedded page; press Escape to leave it.
Open full screen ↗




