38 KiB
38 KiB
Axil Accountants — Implementation Plan
Based on:
Concept.mdv1.0 Stack: Next.js 15 · TypeScript · Tailwind CSS v4 · Payload CMS 3 · PostgreSQL (Neon) · Vercel Each feature is isolated and can be developed, tested, and reviewed independently.
Legend
[ ]— Not started[x]— Complete[~]— In progress[!]— Blocked / needs decision
Feature 1 — Project Setup & Repository
- Scaffold Next.js 16.1.6 project with App Router and TypeScript (
create-next-app) - Configure TypeScript (
tsconfig.json): strict mode, path aliases (@/*) - Install and configure Tailwind CSS v4
- Install and configure ESLint 9 (Next.js preset)
- Install and configure Prettier with Tailwind class sorting plugin
- Set up Husky 9 + lint-staged 16 (pre-commit: lint + format)
- Define
.env.localstructure and.env.exampletemplate - Create
src/directory structure (app, components/ui/layout/sections/three/cms, lib, hooks, types, payload) - Initialise Git repository, create
mainanddevelopbranches - Docker: multi-stage Dockerfile (dev/build/production) + docker-compose.yml (app + PostgreSQL 17)
- Create Vercel project, connect to Git repository
- Configure Vercel environment variables (production + preview)
Feature 2 — Database & Cloud Infrastructure
- Create Neon PostgreSQL project (serverless, auto-scaling)
- Copy connection string to
DATABASE_URIenvironment variable - Verify database connectivity from local environment
- Set up Uploadthing account for media storage
- Create app, get
UPLOADTHING_SECRETandUPLOADTHING_APP_ID - Configure allowed file types: images (jpg, png, webp, svg), documents (pdf)
- Create app, get
- Set up Resend account for transactional emails
- Create API key, add to
RESEND_API_KEY - Verify sending domain
- Create API key, add to
Feature 3 — Payload CMS — Core Installation
- Install Payload CMS 3 into the Next.js project (
npx create-payload-appor manual) - Configure
payload.config.ts:serverURLfromNEXT_PUBLIC_SITE_URLsecretfromPAYLOAD_SECRETdb: PostgreSQL adapter pointing toDATABASE_URIeditor: Lexical rich text editoradminpanel configuration
- Mount Payload API handler at
src/app/(payload)/api/[...slug]/route.ts - Mount admin panel at
src/app/(payload)/admin/[[...segments]]/page.tsx - Configure Uploadthing storage adapter for Payload media
- Configure Resend email adapter for Payload email notifications
- Create initial admin superuser (seed script or first-run prompt)
- Verify admin panel loads at
/admin - Install Payload Form Builder plugin (
@payloadcms/plugin-form-builder)
Feature 4 — CMS Collections Schema
Define all Payload collections and globals. Each collection = one content type the client manages.
4.1 — Media Collection
Mediacollection with Uploadthing adapter- Fields:
alt(text, required),caption(text, optional) - Image sizes:
thumbnail(400×300),card(800×600),hero(1920×1080)
- Fields:
4.2 — Users Collection
Userscollection (extends Payload default)- Fields:
name,email,role(select: admin | editor) - Role-based access control: editors can manage content, not settings
- Fields:
4.3 — Services Collection
Servicescollectiontitle(text, required)slug(slug, auto from title, unique)icon(select: bookkeeping | tax | payroll | vat)tagline(text — short subtitle)description(rich text — full description)whatsIncluded(array of bullet strings)whoItsFor(array:{ audience: select, description: text })howItWorks(array of 3:{ step: number, title: text, description: text })faq(array:{ question: text, answer: rich text })relatedTestimonials(relationship → Testimonials, many)seogroup:{ metaTitle, metaDescription, ogImage → Media }publishedAt(date),status(select: draft | published)
4.4 — Blog Posts Collection
Postscollectiontitle(text, required)slug(slug, unique)author(relationship → Users)category(relationship → Categories)coverImage(upload → Media)excerpt(textarea, 150 chars max)content(rich text — Lexical with full feature set)readingTime(number, auto-calculated on save via hook)publishedAt(date)status(select: draft | published | scheduled)tags(array of text)seogroup:{ metaTitle, metaDescription, ogImage → Media }
4.5 — Categories Collection
Categoriescollectionname(text, required)slug(slug)colour(text — hex, for tag colour)
4.6 — Team Members Collection
TeamMemberscollectionname(text, required)role(text)qualifications(text — e.g. "ACCA, MAAT")bio(textarea)photo(upload → Media)linkedIn(text — URL)order(number — display order)
4.7 — Testimonials Collection
TestimonialscollectionclientName(text, required)businessName(text)businessType(select: sole-trader | limited-company | startup | other)rating(select: 1–5)quote(textarea, required)photo(upload → Media, optional)source(select: google | manual)service(relationship → Services, optional — for filtering)featured(checkbox — show in homepage carousel)publishedAt(date)
4.8 — FAQs Collection
FAQscollection (global FAQ bank)question(text, required)answer(rich text)service(relationship → Services, optional)global(checkbox — show on generic FAQ sections)order(number)
4.9 — Form Builder (Payload Plugin Collections)
- Enable
@payloadcms/plugin-form-builder— provides:Formscollection (drag-and-drop field builder in admin)FormSubmissionscollection (stores all submissions)
- Configure supported field types:
text,email,phone,select,checkbox,textarea - Configure submission handlers:
- Email notification (to: configurable per form)
- Webhook trigger (URL: from webhook registry in Site Settings)
- Redirect URL or confirmation message (per form)
4.10 — Navigation Global
Navigationglobalitems(array):label(text)href(text, optional — for simple links)isDropdown(checkbox)children(array, conditional):{ label, href, icon, description }
4.11 — Footer Global
Footerglobalcolumns(array of 4):heading(text)links(array:{ label, href })
contactInfogroup:{ address, phone, email }socialLinksgroup:{ linkedIn, facebook, instagram }legalLinks(array:{ label, href })copyrightText(text)
4.12 — Site Settings Global
SiteSettingsglobal- Brand:
siteName,tagline,logo → Media,logoDark → Media,favicon → Media - Contact:
address,phone,email,officeHours - Social:
linkedIn,facebook,instagram - Analytics:
googleAnalyticsId,googleTagManagerId,facebookPixelId - Booking:
calendlyUrl,calendlyInline(checkbox) - Chat Bot:
chatBotMode(select: disabled | ai | livechat | leadcapture),chatBotCustomCode(code field — for Tawk.to etc.) - Webhooks Registry:
webhooks(array:{ name, url, active }) - Email:
notificationEmail(default recipient for form submissions) - SEO Defaults:
defaultMetaTitle,defaultMetaDescription,defaultOgImage → Media - Scripts:
headerScripts(code),footerScripts(code) — for third-party embeds
- Brand:
Feature 5 — Design System & Base UI Components
- Configure Tailwind CSS with custom design tokens:
// tailwind.config.ts colors: { sage: { DEFAULT: '#6BAF7D', light: '#A8C5A0' }, forest: { DEFAULT: '#2E7D52', dark: '#1A2E1F' }, mint: '#F4FAF5', charcoal: '#1A2E1F', muted: '#6B7280', } borderRadius: { card: '16px', hero: '24px', pill: '999px' } fontFamily: { sans: ['Inter', 'sans-serif'], display: ['Satoshi', 'sans-serif'], mono: ['DM Mono', 'monospace'], } - Set up fonts:
Satoshi(variable font) — self-hosted via@font-faceor Fontshare CDNInter— Google Fonts /next/fontDM Mono— Google Fonts /next/font
- Create CSS variables in
globals.cssfor all design tokens (enables runtime theming) - Button component (
src/components/ui/Button.tsx):- Variants:
primary(green fill),secondary(outline),ghost(text only) - Sizes:
sm,md,lg - States: hover (scale + colour shift), focus (ring), loading (spinner), disabled
- Optional: trailing arrow icon, leading icon
- Variants:
- Icon system (
src/components/ui/icons/):- Custom SVG line icons for each service (Bookkeeping, Tax, Payroll, VAT)
- Shared icons: ArrowRight, CheckCircle, Star, Menu, X, ChevronDown
- All exported as React components with
sizeandcolorprops
- Heading component —
h1–h4with correct size + weight from design system - Tag/Badge component — pill with colour variant (green, grey)
- StarRating component — 1–5 filled star SVGs
- GlassCard component — frosted glass base with sage green border, hover glow
- Section layout wrapper —
max-w-[1440px], horizontal padding,pyspacing - Divider component — subtle sage green horizontal rule
- Spinner component — loading state for forms and async content
Feature 6 — Animation Infrastructure
- Install dependencies:
gsap,@gsap/react,lenis,framer-motion,@react-three/fiber,three - Lenis smooth scroll provider (
src/lib/lenis.tsx):LenisProviderwraps the app in root layout- Integrates with GSAP ticker for synchronized animations
- Disabled on mobile if causing performance issues
- GSAP context provider (
src/lib/gsap.ts):- Registers ScrollTrigger plugin
- Exports
gsapinstance with plugins pre-registered - Custom hook
useGSAPfor component-level animation cleanup
useAnimateOnScrollhook (src/hooks/useAnimateOnScroll.ts):- Accepts
ref,animation type(fadeUp, fadeIn, slideLeft, etc.),delay,stagger - Returns trigger state
- Uses ScrollTrigger under the hood
- Accepts
useCountUphook (src/hooks/useCountUp.ts):- Accepts
target(number),duration,prefix(£,+),suffix(%,+) - Triggers animation when element enters viewport
- Returns current display value (string)
- Accepts
AnimatedTextcomponent (src/components/ui/AnimatedText.tsx):- Splits text into words or lines
- Each word/line slides up on a stagger delay
- GSAP-driven, respects
prefers-reduced-motion
FadeInSectioncomponent (src/components/ui/FadeInSection.tsx):- Wraps any content
- Fades + translates up when enters viewport
- Accepts
delayanddurationprops
- Framer Motion page transition (
src/components/layout/PageTransition.tsx):- Wraps each page with
AnimatePresence - Default: fade + subtle Y translate
- Used in root layout
- Wraps each page with
prefers-reduced-motionutility — all animation components check and skip if enabled
Feature 7 — Header & Navigation
Headercomponent (src/components/layout/Header.tsx):- Fixed/sticky at top,
z-50 - On scroll: applies
backdrop-blur+ white/85% opacity background (Framer Motion) - Transparent when at top of page
- Max-width container, flex layout: logo | nav | CTA
- Fixed/sticky at top,
- Logo component — renders logo image from CMS Site Settings, supports light/dark variant
- Desktop navigation (
src/components/layout/DesktopNav.tsx):- Renders items from CMS Navigation global
- Simple links: hover underline slide-in animation
- Dropdown trigger:
Services ▾— rendersServicesDropdown
ServicesDropdowncomponent:- Opens on hover/focus, closes on blur/outside click
- Grid of service cards: icon + name + short description
- Framer Motion: animate-in (scale + fade from top)
- Header CTA button — "Book a Free Consultation" → opens booking modal
MobileMenucomponent (src/components/layout/MobileMenu.tsx):- Hamburger icon button (animated → X on open)
- Full-screen overlay,
fixed inset-0, dark green tint background - Framer Motion: slide-in from right or fade-in
- Staggered nav item reveal
- Large CTA button at bottom
- Closes on link click or backdrop click
- Fetch navigation data from Payload at build/request time (cached)
Feature 8 — Footer
Footercomponent (src/components/layout/Footer.tsx):- 4-column grid (collapses to 2×2 on tablet, 1 column on mobile)
- Column 1: Logo, tagline, social icon links
- Column 2: Services links
- Column 3: Company links (About, Blog, Contact)
- Column 4: Contact info (address, phone, email) + ICAEW/ACCA badges
- Social icon links (LinkedIn, Facebook, Instagram) — SVG icons, hover colour transition
- ICAEW / ACCA badge images (stored in media, linked from Site Settings)
- Bottom bar: copyright text | Privacy Policy | Cookie Policy
- Data fetched from CMS Footer global + Site Settings global
Feature 9 — 3D Hero Scene (Three.js / R3F)
- Install:
@react-three/fiber,@react-three/drei,three GlobeScenecomponent (src/components/three/GlobeScene.tsx):- Canvas with
@react-three/fiber - Camera position, fog, ambient + directional lighting (soft green tint)
- Canvas with
Globemesh:SphereGeometrywith high segment count- Custom
PointsMaterialshader: dot-matrix surface pattern - Colour: sage green
#6BAF7Don near-white background - Slow auto-rotation around Y axis (
useFrame)
ParticleNetwork:- 150–300 floating particle nodes (reduced on mobile: 60–80)
- Lines connecting nearby particles (
LineSegments) - Nodes float with subtle Perlin noise movement
- Colour: soft mint with 40% opacity
- Mouse parallax interaction:
- Track
mousemoveon hero section - Globe and particle network shift slightly (opposite to mouse direction)
useSpring(React Spring or Framer Motion) for smooth interpolation
- Track
- Device-adaptive performance:
- Detect mobile / low-power device
- Reduce particle count, disable post-processing effects
React.Suspenseboundary with skeleton loader during WebGL init
- Mobile fallback (
src/components/three/GlobeFallback.tsx):- 2D SVG animated globe (CSS animation, no WebGL)
- Shown when WebGL unavailable or
prefers-reduced-motionis set
useWebGLSupporthook — detects WebGL availability, returns boolean
Feature 10 — Home Page: Hero Section
HeroSectioncomponent (src/components/sections/home/HeroSection.tsx)- Full-viewport height (
min-h-screen), flex / grid split layout - Left column:
- Eyebrow tag (e.g. "Trusted by 500+ UK Businesses") — Badge component, fade-in
AnimatedTextH1: "Smart Accounting for Growing British Businesses" — word stagger- Subheadline paragraph — fade-in with delay
- Button group: Primary CTA + Secondary CTA (with arrow icon)
- Trust strip: star rating + "4.9 Google Rating" · "ICAEW Certified" · "500+ Clients Served"
- Right column:
GlobeScene(orGlobeFallbackon mobile/no WebGL) - Mobile layout: stacked, 3D scene below text, reduced height
- Content driven from CMS Pages collection (hero fields)
Feature 11 — Home Page: Pain Points Section
PainPointsSectioncomponent- Dark green background (
#1A2E1F), full-width - Headline + subtext —
AnimatedTextreveal - 3
GlassCardcomponents (frosted glass on dark background):- Card 1: "Missed deadlines & HMRC penalties"
- Card 2: "Confused by tax rules that keep changing"
- Card 3: "Hours wasted on bookkeeping instead of growing"
- ScrollTrigger staggered reveal: cards slide up 100ms apart
- Subtle parallax: background shifts 20px on scroll
Feature 12 — Home Page: Services Overview Section
ServicesSectioncomponent- Section header: headline + subheadline
- 2×2
GlassCardgrid (CSS Grid, responsive) - Each card:
- Custom SVG icon (from icon system)
- Service name (heading)
- Short description (from CMS)
- "Learn More →" link to
/services/[slug] - Hover:
translateY(-4px)+ sage green border glow (box-shadow)
- ScrollTrigger staggered card reveal
- CTA block below grid: "Not sure which service you need? → Let's Talk"
- Data: fetch all Services from Payload
Feature 13 — Home Page: Why Choose Axil Section
WhyAxilSectioncomponent- Section headline: "Why Businesses Choose Axil"
- Stat counter row (4 stats):
500+Businesses Served98%Client Retention Rate£2M+Tax Saved for Our Clients12+Years of Experience- Each uses
useCountUphook, triggers on ScrollTrigger enter - Numbers in
DM Monofont
- 4 USP blocks (icon + heading + description):
- ICAEW & ACCA Qualified
- Fixed Monthly Engagement
- Dedicated Account Manager
- Cloud-Based & Paper-Free
FadeInSectionwrapper per block, staggered
Feature 14 — Home Page: Testimonials Section
TestimonialsSectioncomponent- Auto-scrolling marquee carousel (infinite horizontal scroll, CSS animation)
- Pause on hover
- Two rows scrolling in opposite directions (optional premium touch)
TestimonialCardcomponent:- Client name + business type
StarRating(1–5)- Quote text (truncated at 3 lines, expand on click)
- Optional client avatar (from Media, fallback to initials)
- Google Reviews badge (static badge image or API widget)
- ICAEW / ACCA certification badge display
- CTA below: "Join hundreds of satisfied UK businesses → Book Your Free Consultation"
- Data: fetch featured testimonials from Payload (where
featured = true)
Feature 15 — Home Page: Who We Work With Section
AudienceSectioncomponent- 3 audience cards:
- Sole Traders — illustrated icon, description, "Learn More →"
- Limited Companies — illustrated icon, description, "Learn More →"
- Startups — illustrated icon, description, "Learn More →"
- Illustrated icons: custom SVG per audience type
- Cards:
GlassCard, hover lift effect - FadeIn reveal on scroll
Feature 16 — Home Page: How It Works Section
HowItWorksSectioncomponent- 3-step horizontal layout (flex row on desktop, vertical stack on mobile)
- Each step: numbered circle + title + description
- Animated connecting line:
- SVG path between steps
- GSAP
DrawSVGPluginor strokedashoffsetanimation - Draws left-to-right as section scrolls into view
- Step data driven from CMS (global or hardcoded — TBD with client)
Feature 17 — Home Page: Blog Preview Section
BlogPreviewSectioncomponent- Section headline + "Visit Our Blog →" link
- Fetch 3 most recent published posts from Payload
BlogCardcomponent (reused on blog index):- Cover image (
next/image, lazy-loaded) - Category tag badge
- Title (heading)
- Excerpt (truncated)
- Author name + date + read time
- Hover: image subtle zoom, card lift
- Cover image (
- Grid: 3 columns desktop, 1 column mobile
Feature 18 — Home Page: Final CTA Section
FinalCTASectioncomponent- Full-width, deep forest green background (
#1A2E1For#2E7D52) - Centred content:
- Large headline: "Ready to take the stress out of your finances?"
- Subheadline
- Large primary CTA button (white on green): "Book a Free Consultation"
- Trust reassurance: "★ 4.9/5 on Google · ICAEW Certified · No lock-in contracts"
- Optional: subtle animated background (particle drift or gradient shift)
Feature 19 — Consultation Booking Modal & Form
This is the primary conversion mechanism. Opens from every CTA across the site.
BookingModalcomponent (src/components/BookingModal.tsx):- Global state:
useBookingModal()Zustand store or React Context - Triggered by
openBookingModal()from any component AnimatePresence+ Framer Motion: fade-in backdrop, slide-up modal- Close on backdrop click, Escape key, close button
- Accessible: focus trap,
role="dialog",aria-modal
- Global state:
- Two modes (configurable in CMS Site Settings):
- Mode A — Calendly: renders
<iframe>fromcalendlyUrlsetting - Mode B — Custom Form: renders the consultation form
- Mode A — Calendly: renders
- Consultation form fields:
- Full name (text, required)
- Email address (email, required)
- Phone number (text, optional)
- Business type (select: sole trader / limited company / startup / other)
- What do you need help with? (select: bookkeeping / tax / payroll / VAT / not sure)
- Message (textarea, optional)
- Form validation: client-side (React Hook Form + Zod schema)
- Submit handler (API route
POST /api/contact):- Save to Payload FormSubmissions collection
- Send notification email via Resend to admin email (from Site Settings)
- Trigger webhook if configured (from webhooks registry)
- Return success/error response
- Success state: animated checkmark + "We'll be in touch within 1 business day" message
- Error state: clear error messages per field + general error fallback
Feature 20 — Services Pages
20.1 — Services Overview Page (/services)
- Grid of all 4 service cards (large format)
- Each card links to individual service page
- Intro section: headline + subheadline
- CTA at bottom
20.2 — Individual Service Pages (/services/[slug])
- Dynamic route with
generateStaticParams(ISR) - Fetch service data by slug from Payload at build/revalidate time
- Hero section: service name, bold tagline, illustration/icon, CTA button
- What's Included section: bullet list of deliverables (from CMS
whatsIncluded) - Who It's For section: audience cards filtered to relevant segments
- How It Works section: 3-step process (service-specific, from CMS)
- FAQ section: accordion component
- Fetch FAQs related to this service from Payload
- Animated expand/collapse (Framer Motion
AnimatePresence+ height animation) - JSON-LD
FAQPageschema injected viagenerateMetadata
- Testimonials section: filtered by service (from Payload relationship)
- Final CTA section
generateMetadata: SEO title, description, OG image from CMSseogroup
Feature 21 — About Page (/about)
- Fetch team members, site settings from Payload
- Hero section: large headline + team group photo (from Media)
- Our Story section: rich text from CMS (Lexical renderer)
- Team grid section:
TeamMemberCardcomponent: photo, name, role, qualifications, LinkedIn link- Responsive grid: 3 col desktop, 2 col tablet, 1 col mobile
- Hover: subtle overlay with LinkedIn button
- Data from Payload TeamMembers collection (ordered by
orderfield)
- Core Values section: 4 value blocks (icon + title + description)
- Certifications section: ICAEW + ACCA logo badges with descriptions
- Final CTA section
Feature 22 — Blog System
22.1 — Blog Index Page (/blog)
- Fetch all published posts (ISR, revalidate: 3600s)
- Search input: client-side filter by title/excerpt
- Category filter tabs: "All" + each category — filters post grid
- Blog post card grid (3 columns desktop, 1 mobile) —
BlogCardcomponent - Pagination (10 posts per page, Payload
limit/pagequery) - SEO metadata for blog index
22.2 — Individual Blog Post Page (/blog/[slug])
generateStaticParams+ ISR (revalidate: 3600s)- Fetch post by slug from Payload
- Lexical rich text renderer (
src/lib/lexical-renderer.tsx):- Renders all Lexical nodes: headings, paragraphs, lists, blockquotes, images, code blocks
- Custom component for pull quotes (green left border, large text)
- Syntax highlighting for code blocks (
shikiorprism)
- Article layout: hero image (full-width), title, author + date + read time + category tag
- Table of Contents (auto-generated from H2/H3 headings):
- Sticky sidebar on desktop
- Active heading highlight on scroll (Intersection Observer)
- Social share buttons: copy link, Twitter/X, LinkedIn
- Related posts section: 2–3 posts from same category
generateMetadata: full SEO + Open Graph from CMS seo group
Feature 23 — Contact Page (/contact)
- Hero: "Get in Touch" + brief description
- Contact form (renders a Payload Form via form builder):
- Fields: name, email, phone, subject, message
- Submission saves to Payload FormSubmissions + email notification
- Contact info panel: address, phone, email, office hours
- Google Maps embed (iframe, optional — lazy-loaded)
- Office hours block
Feature 24 — Legal Pages (/privacy-policy, /cookie-policy)
- Static page routes
- Content editable via CMS Pages collection (rich text fields)
- Standard typography layout: table of contents sidebar + main content
- Last updated date (from CMS)
Feature 25 — Chat Bot Module
ChatWidgetcomponent (src/components/chat/ChatWidget.tsx):- Floating button (bottom-right), "Chat with us" label
- Slide-up panel animation (Framer Motion)
- Reads
chatBotModefrom Site Settings global
- Mode: disabled — component renders nothing
- Mode: livechat (Tawk.to or custom):
- Reads
chatBotCustomCodefrom Site Settings - Injects script via
useEffect(no SSR) - Custom button hidden; Tawk.to button shown instead
- Reads
- Mode: ai — AI Chat:
- Pre-chat form: collect visitor name + email
- Chat messages UI: user + bot bubbles, loading dots
- API route
POST /api/chat:- Sends message to OpenAI/Claude with system prompt
- System prompt: company info, services, FAQs — fetched from Payload
- Streams response back (SSE or JSON)
- Escalation: if bot confidence low → "Let me connect you with our team" → email notification
- Conversation log saved to Payload (custom
ChatLogscollection or email)
- Mode: leadcapture — Scripted Bot:
- Step 1: "Hi! What's your name?"
- Step 2: "Great, {name}! What's your email address?"
- Step 3: "Are you a sole trader, limited company, or startup?"
- Step 4: "What's your main concern? (tax / bookkeeping / payroll / other)"
- Step 5: "Thanks! Our team will be in touch shortly."
- On completion: POST lead to
/api/contact→ email + webhook
Feature 26 — SEO & Metadata System
generateMetadatafunction utility (src/lib/metadata.ts):- Accepts CMS seo object + fallback to Site Settings defaults
- Returns Next.js
Metadataobject - Handles: title, description, OG image, canonical URL
- Open Graph image generation (
src/app/og/route.tsx):- Dynamic OG images for blog posts using
@vercel/og - Template: Axil logo + post title + sage green background
- Dynamic OG images for blog posts using
- Structured data helpers (
src/lib/structured-data.ts):LocalBusinessschema (name, address, phone, opening hours)AccountingServiceschemaFAQPageschema (for service pages)BlogPostingschema (for blog articles)- Injected via
<script type="application/ld+json">in page<head>
sitemap.ts(src/app/sitemap.ts):- Fetches all published blog posts + service slugs from Payload
- Returns
MetadataRoute.Sitemaparray - Revalidates on ISR
robots.ts(src/app/robots.ts):- Allow all crawlers
- Point to sitemap
- Block
/admin/*
- Verify all pages have correct
<title>tags in browser devtools
Feature 27 — Analytics, Tracking & Cookie Consent
AnalyticsProvidercomponent (src/components/AnalyticsProvider.tsx):- Reads GA ID, GTM ID, FB Pixel ID from CMS Site Settings
- Conditionally loads scripts only after cookie consent granted
- Google Tag Manager integration:
<Script>in<head>and<noscript>in<body>- Only loads after consent
- Cookie consent banner (
src/components/CookieBanner.tsx):- GDPR / UK GDPR compliant
- Framer Motion: slides up from bottom on first visit
- Buttons: "Accept All" | "Reject Non-Essential" | "Manage Preferences"
- Stores consent in
localStorage - Triggers analytics scripts on accept
useConsenthook — reads/writes consent state, exposeshasConsent(category)- Custom code injection from CMS Site Settings:
headerScripts— injected in<head>via Next.js root layoutfooterScripts— injected before</body>
Feature 28 — Form Builder Frontend Renderer
Allows any Payload-built form to be rendered on the frontend.
FormRenderercomponent (src/components/cms/FormRenderer.tsx):- Accepts
formIdprop (Payload form document ID or slug) - Fetches form schema from Payload API
- Dynamically renders all field types:
text,email,phone,textarea→<input>/<textarea>select→<select>or custom dropdowncheckbox→ styled checkbox
- React Hook Form + Zod validation from field config (required, type)
- Accepts
FormFieldcomponent — renders individual field with label, input, error message- Submit handler:
POST /api/form-submissions- Saves to Payload FormSubmissions
- Triggers email + webhook from form config
- Success / error UI states
- Usage: embed
<FormRenderer formId="..." />anywhere on any CMS-managed page
Feature 29 — Performance Optimisation
- All images use
next/imagewith correctsizesprop andpriorityfor LCP images next/fontfor Inter + DM Mono (eliminates layout shift from font load)- Dynamic imports for heavy components:
GlobeScene—dynamic(() => import(...), { ssr: false })ChatWidget—dynamic(() => import(...), { ssr: false })- Lexical renderer —
dynamicimport
- ISR (Incremental Static Regeneration):
- Service pages:
revalidate = 86400(24h) - Blog posts:
revalidate = 3600(1h) - Homepage:
revalidate = 3600
- Service pages:
- Install
@next/bundle-analyzer, run bundle analysis, eliminate large dependencies - Lazy-load all sections below fold (Intersection Observer or Next.js
loading="lazy") - Verify no unused CSS (Tailwind PurgeCSS is automatic)
- Run Lighthouse on all key pages — target: Performance 95+, SEO 100, Accessibility 95+
- Fix any Lighthouse findings before delivery
Feature 30 — Accessibility Audit & Fixes
- Add
<a href="#main-content">Skip to content</a>as first focusable element - Verify all interactive elements are keyboard-navigable (Tab, Enter, Escape)
- All images have meaningful
alttext (enforced by Payload Media collection) - All form fields have associated
<label>elements - All icon-only buttons have
aria-label - Modals: focus trap,
role="dialog",aria-modal, return focus on close - Navigation dropdowns:
aria-expanded,aria-haspopup - Colour contrast: check all text/background combos meet WCAG AA (4.5:1 body, 3:1 large text)
- Test with screen reader (VoiceOver / NVDA)
- Verify
prefers-reduced-motiondisables all non-essential animations
Feature 31 — Responsive Design QA
- Test every page at:
320px,375px,414px,768px,1024px,1280px,1440px,1920px - Verify 3D scene: WebGL on desktop, SVG fallback on mobile
- Verify hamburger menu: opens, nav links work, closes correctly
- Verify booking modal: fills screen on mobile, scrollable content
- Verify all form fields usable with touch keyboard (no content hidden behind keyboard)
- Verify testimonials marquee / carousel: touch-swipeable on mobile
- Verify blog grid: 1-column on mobile, readable font sizes
- Verify footer: stacks correctly on mobile
Feature 32 — Error Handling & Monitoring
- Sentry setup (
@sentry/nextjs):- Install and configure with
SENTRY_DSN - Client + server + edge error capture
- Source maps upload on build
- Install and configure with
- Custom 404 page (
src/app/not-found.tsx):- Brand-styled, helpful message
- Links back to home + contact
- Custom 500 page (
src/app/error.tsx):- Brand-styled error page
- Reports error to Sentry
- All API routes return proper HTTP status codes + JSON error bodies
- Form submission error states: clear per-field errors + general fallback
- Payload API fetch failures: graceful degradation (show fallback content, not crash)
Feature 33 — Deployment & CI/CD
- Configure Vercel project settings:
- Build command:
pnpm build - Output directory:
.next - Node.js version: 20.x
- Build command:
- Add all production environment variables to Vercel dashboard
- Configure preview deployments: auto-deploy on every PR to
develop - Configure production deployment: auto-deploy on merge to
main - Set up custom domain (e.g.
axilaccountants.co.uk) in Vercel - Verify SSL/HTTPS certificate
- Configure
www→ apex redirect (or vice versa) - Run production build locally (
pnpm build && pnpm start) — verify no build errors - Run Lighthouse on production URL — confirm scores meet targets
- Set up Vercel Speed Insights (optional)
Feature 34 — Content Seeding & CMS Setup
- Create seed script (
src/payload/seed.ts):- Site Settings: company name, logo, contact details, placeholder social URLs
- 4 Services: Bookkeeping, Tax Returns, Payroll, VAT Returns (full content per service)
- 3 Sample Testimonials (marked as featured)
- 3 FAQs per service
- Blog Categories: "Tax Tips", "Business Advice", "HMRC Updates", "Payroll Guide"
- Navigation: full nav structure from concept
- Footer: all columns with links
- Run seed script against production database
- Verify all seeded content displays correctly on frontend
- Create CMS editor user account for client
Feature 35 — Client Handover & Documentation
- CMS User Guide (
docs/cms-guide.md):- How to log into the admin panel
- How to edit page content (services, hero text, etc.)
- How to write and publish a blog post
- How to add a testimonial
- How to add a team member
- How to create a contact form
- How to set up a webhook on a form
- How to change the chat bot mode
- How to update integration settings (GA, GTM, Calendly URL)
- Handover checklist:
- Client has admin login credentials
- Client has Vercel access (viewer role)
- Client has Resend account access (for email settings)
- DNS properly pointing to Vercel
- Google Analytics connected and receiving data
- Booking form submissions arriving in CMS + email
- Mobile tested on real device (iPhone + Android)
- Lighthouse scores confirmed ≥ 95 Performance
Summary: Feature Dependency Order
Feature 1 (Project Setup)
└── Feature 2 (Database)
└── Feature 3 (Payload CMS Core)
└── Feature 4 (CMS Collections Schema)
├── Feature 5 (Design System) ← parallel from here
├── Feature 6 (Animation Infrastructure)
└── Feature 7 + 8 (Header + Footer)
└── Feature 9 (3D Scene)
└── Feature 10 (Hero Section)
└── Features 11–18 (Home Page Sections)
└── Feature 19 (Booking Modal) ← needed by all CTAs
├── Feature 20 (Services Pages)
├── Feature 21 (About Page)
├── Feature 22 (Blog)
├── Feature 23 (Contact Page)
└── Feature 24 (Legal Pages)
├── Feature 25 (Chat Bot)
├── Feature 26 (SEO)
├── Feature 27 (Analytics)
├── Feature 28 (Form Renderer)
├── Feature 29 (Performance)
├── Feature 30 (Accessibility)
├── Feature 31 (Responsive QA)
├── Feature 32 (Error Handling)
└── Feature 33 (Deployment)
├── Feature 34 (Content Seed)
└── Feature 35 (Handover)
Total features: 35 | Estimated tasks: ~220 Last updated: February 2026