Skip to content
Swift IdeasStart a build
Swift Ideas
Start a build
Back to the work

Maternity, newborn and family photography

LJ Photography

A studio portfolio for new families, paced like a quiet session.

LJ Photography home page: the headline 'Soft light, held close.' with 'held' in italic, beside a studio maternity portrait on a pale grey backdrop.

The hero slideshow walks the five chapters; the counter reads 01 / 05, Maternity.

Project facts

Problem
The work lived on Instagram, and parents choosing a newborn photographer need a site that feels calm and clear.
Approach
Five chapters from bump to first birthday, a hand-drawn monogram and slow, gentle motion.
Result
Live at ljphotography.co.uk. Lighthouse on the live site, mobile: 85 for performance, 96 for accessibility and 100 for SEO.
Client
LJ Photography
Where
Bracknell, Berkshire
Year
2026
Status
Live
Work
Brand websites

Our role

  • Art direction
  • Monogram refinement
  • Motion design
  • WebGL development
  • Next.js build
  • Enquiry form and attribution

Built with

Next.js 16.3, React 19.2, GSAP 3.15, Lenis 1.3, OGL 1.0 (WebGL), Tailwind CSS 4, TypeScript, sharp, svgpath, Playwright, Vercel

The problem

Family photographers sell trust as much as images. Parents are choosing someone to photograph a baby in the first days of its life, so the site had to feel calm and considered, and still make it obvious which sessions exist and how to enquire.

The work lived mainly on Instagram. The build needed a structure that turns a feed into a story, with each image in the right chapter, and an image setup that could swap in full-resolution originals later without touching a layout.

The motion needed restraint. The subjects are babies and parents, so nothing could feel like a tech demo. Every effect had to slow the visitor down, not hurry them along.

What we did

We organised the work into five chapters, each with its own kicker: Maternity (the becoming), Newborn (the first days), Family (the in-between), Milestones (the firsts) and Portraits (after dark). Each chapter has its own route, cover and introduction, and the home page walks through them in order.

The brand mark was rebuilt from a hand-drawn letterform. A build script scales it inside an open hairline ring and cuts the ring wherever the J tail crosses it. The intro then draws the mark stroke by stroke at a constant pen speed before it travels into the navigation.

Instrument Serif sets the display type, Inter Tight does the reading, and a Cormorant Garamond italic picks out single words. The palette stays on warm paper and ink so the photographs carry all the colour.

The site went from the first line of code to search-ready in three days, across ten commits, and is live on ljphotography.co.uk.

What we considered

The decisions behind the build, with the reasoning and the evidence for each.

MotionA monogram that draws at pen speed

A logo reveal usually animates every stroke over the same duration, so short strokes crawl and long ones rush. We timed each stroke by its real length so the pen moves at one speed, then eased the whole pass as a single gesture.

  • Stroke duration is proportional to path length inside one paused timeline, scrubbed by a single sine ease
  • The open ring sweeps round in parallel, slightly ahead of the pen, then holds for 0.15s before the mark travels to the nav
  • GSAP autoRound disabled: with pathLength=1, default pixel rounding snaps strokes straight from hidden to drawn
  • mask-check.mts renders the draw mask over the glyphs and counts uncovered pixels; the target is 0
MotionA rich slideshow with a lighter twin for other devices

The hero cross-fades chapter covers with an organic wipe that rises from the bottom, like light filling a room. WebGL does it on capable desktops; everywhere else a CSS version gives the same rhythm at a fraction of the cost.

  • Fragment shader: simplex-noise edge, a soft displacement band along it and a slow zoom settle on the incoming photo
  • 5.2s between slides, 1.8s per transition; the canvas renders only during a transition or resize and is idle between slides
  • The shader loads by dynamic import after a mount delay, so it never blocks first paint
  • Fallback: GSAP clip-path wipe from inset(100% 0 0 0), expo.inOut over 1.5s, with a 1.18 to 1 inner zoom
MotionChoreography: curtain, line-by-line headings, image trail, tonal shift

Each motion has one job in the story of the page. The intro lifts a curtain, headings arrive line by line, the cursor leaves a trail of frames, and the page darkens for the low-key portraits.

  • Intro runs on two CSS variables, --intro and --curtain, so the curtain, its corner radius and the logo's travel move as one
  • Split-text reveals sit in clipped line masks with 0.25em bottom padding so descenders never clip
  • Image trail on fine pointers only: a new frame every 110px of travel, easing out over 0.9s
  • After dark: an ink panel opens from a rounded inset card (inset 12% 10%, 48px radius) to full bleed as it scrolls in
  • Lenis smooth scroll, with a glide back to the top when the visitor changes chapter
TypographyThree typefaces, three jobs

A narrow, high-contrast serif gives the display type the stillness of a printed album. A tight grotesk keeps the practical information plain. One italic accent face is used for single words, never whole lines.

  • Display: Instrument Serif 400, tracking -0.02em, line-height 0.92
  • Text and captions: Inter Tight; captions 11px uppercase at 0.16em tracking
  • Accent: Cormorant Garamond italic, weights 300 and 400, for words such as 'held' and 'hello'
  • All three self-hosted through next/font, with the page language set to en-GB
DesignPaper, ink and one stone

Newborn and maternity photography is mostly skin tones and soft neutrals. Any strong interface colour would fight it, so the interface stays on paper and ink and uses one warm stone for quiet emphasis.

  • Paper #f6f4f0 ground; bone #edeae4 behind the intro curtain
  • Ink #141312 for text, mute #6d6861 for secondary copy
  • Stone #c9b8a3 as the single warm accent
  • A fixed grain layer sits over everything to give the paper some tooth
ShowHide the other 4 decisions
ContentImage descriptions that cannot be wrong about a real family

We removed guessed captions and the detailed image descriptions that screen readers read aloud (alt text). Describing a parent or a baby incorrectly is worse than a neutral description, so alt text stays at chapter level until the photographer confirms what each image shows.

  • Alt text such as 'Newborn portrait by LJ Photography', set per chapter in photos.ts
  • Commit on 24 Sep 2026: 'remove guessed photo captions and descriptive alts'
  • Each photo carries a placeholder flag while it is an Instagram-sourced stand-in for the original file
  • Light or dark tone falls back to measured luminance when a photo is not curated by hand
AccessibilityMotion only when the visitor wants it and the device can handle it

The richer effects run only on devices that can handle them, and the visitor's reduce-motion setting removes them completely. The page reads the same without any of it.

  • A head script decides whether the intro plays before first paint; reduced motion, deep links and repeat visits skip it
  • prefers-reduced-motion clamps animations to 1ms and shows every reveal at once
  • WebGL, the custom cursor and the image trail need a fine pointer at 768px+, 4 CPU cores and 4 GB of memory
  • Lighthouse accessibility 96 on the live site
PerformanceMeasured on the live site

We tested the live site, not a local copy, with Google Lighthouse set to simulate a slower mobile phone and connection. The WebGL layer is kept out of the critical path.

  • Lighthouse mobile, 29 Sep 2026: performance 85, best practices 100, SEO 100
  • Cumulative layout shift 0; total blocking time 40ms
  • WebGL textures request next/image optimised sizes (640 to 1920 wide), never the originals
  • Blur placeholders and intrinsic sizes come from a generated photo manifest
SearchEnquiries counted by the page that earned them

A portfolio site succeeds when enquiries arrive, so we count them against the page where the visit started. This works without analytics cookies and never sends the enquiry itself.

  • Each delivered enquiry reports only the sending page and the landing path to our own monitoring dashboard
  • 3s timeout; a reporting failure is logged and ignored, because the enquiry has already reached the studio
  • Sitemap with 10 URLs (five pages plus five chapters), canonical URLs, robots rules and a generated Open Graph image
  • 9 unit tests cover path parsing and reporting

Colour and type

  • Paper#F6F4F0Page ground
  • Bone#EDEAE4Intro curtain and quiet panels
  • Ink#141312Text and the After dark panel
  • Mute#6D6861Secondary copy and captions
  • Stone#C9B8A3The single warm accent
  • Soft light, held close.

    Instrument SerifDisplay

    Weight 400, tracking -0.02em, line-height 0.92. Narrow and high-contrast, set large.

  • Timeless portraits of the becoming, the first days and everything in between.

    Inter TightText and captions

    Body copy, navigation and 11px uppercase captions at 0.16em tracking.

  • held · hello · after dark

    Cormorant GaramondItalic accent

    Italic 300 and 400 only, for single words inside Instrument Serif headlines.

By the numbers

Lighthouse accessibility on the live site
96
Source: Google Lighthouse test of the live site, mobile setting, single run, 29 September 2026
Cumulative layout shift on the live home page
0
Source: Google Lighthouse test of the live site, mobile setting, single run, 29 September 2026
Chapters, each with its own route and cover
5
Source: Counted on the live site. Its sitemap lists 10 pages
From first commit to SEO pass
3 days
Source: The project's commit history: 10 commits, 23 to 25 September 2026

Where it landed

Live on ljphotography.co.uk with a home page, a work index, five chapter pages, about, services and contact.

Google Lighthouse on the live site, mobile setting: performance 85, accessibility 96, best practices 100, SEO 100, with no content jumping as the page loads (single run, 29 Sep 2026).

The image setup is ready for the original files. Replacing the Instagram-sourced copies and rerunning one script updates image sizes and loading previews with no layout changes.

Start a build like this