Skip to content

M46 Error Page

Read the following context files first:

  • docs/PRD/07_Modules_and_Templates.md
  • docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md
  • docs/dev-frontend-guides/05_BEM_SASS_THEMING.md
  • docs/dev-frontend-guides/01_FIGMA_TO_CODE_WORKFLOW.md
  • docs/dev-frontend-guides/04_STORYBOOK_FIRST_DEVELOPMENT.md
  • packages/modules/src/m06-hero-simple/ (all files in this directory)
  • packages/modules/src/registry.ts
  • packages/ui/src/ (scan for available UI components)

Now create a new module with the following details:

Module ID: M46 Module Name: Error Page Figma Desktop 404: https://www.figma.com/design/VFdCObxYyZjSRFdK5pU1df/01_Savoy_library?node-id=1513-242605&m=dev Figma Desktop 500: https://www.figma.com/design/VFdCObxYyZjSRFdK5pU1df/01_Savoy_library?node-id=1513-242642&m=dev Figma Mobile: N/A (responsive by design — no separate mobile Figma) Component Type: STATIC

Description: Error page module used exclusively on 404 (Not Found) and 500 (Internal Server Error) pages. Content is managed via a dedicated “Error Pages” tab on the siteRoot Document Type in Umbraco. For 404 pages, content is fetched from CMS at runtime. For 500 pages, content is pre-rendered as static fallback (no API dependency when server/DB is down).

Props:

  • label?: HtmlHeading — Error type label (e.g., “ERROR”, “INTERNAL SERVER ERROR”). Uses htmlHeading for semantic control.
  • errorCode: string — The error number displayed large (“404” or “500”). Hardcoded per usage, not from CMS.
  • body?: string — Descriptive text explaining the error.
  • cta?: CtaLinkWithElement — CTA button linking back to homepage or other page.
  • siteKey: string — Site identifier for theming.
  • locale: string — Current language.
  • moduleId?: string — Module ID for data attributes.

UI Components to Use:

  • CtaLink from @savoy/ui — For the CTA pill button
  • HtmlHeading pattern from @savoy/cms-client — For label rendering

Layout:

  • Full-width section with background color var(--color-bg-alt)
  • Content centered horizontally, max-width ~889px
  • Vertical padding: 80px
  • Stacked vertically: label → error code → body text → CTA button
  • Gap between text elements: 14px
  • Gap between text group and CTA: 34px
  • All text centered
  • Responsive: max-width adapts fluidly, padding reduces on mobile

CMS Integration (Special — NOT a Block List module)

Section titled “CMS Integration (Special — NOT a Block List module)”

This module does NOT follow the standard Block List pattern. Instead:

  1. siteRoot gets a new “Error Pages” tab (sortOrder 15) via migration
  2. Fields on siteRoot:
    • error404Label — Single Html Heading Block List (invariant BL, culture-variant text inside)
    • error404Body — Textarea (culture-variant)
    • error404Cta — Single URL Picker (culture-variant)
    • error500Label — Single Html Heading Block List (invariant BL, culture-variant text inside)
    • error500Body — Textarea (culture-variant)
    • error500Cta — Single URL Picker (culture-variant)
  3. Error code (404/500) is hardcoded in the frontend, not a CMS field
  4. The module is NOT registered in the Page Modules Block List — it’s used directly by Next.js error boundary files
  • not-found.tsx — Fetches siteRoot content from CMS API, extracts error404* fields, renders <ErrorPage>
  • error.tsx ('use client') — Uses pre-rendered static content. On CMS publish (webhook), a build step regenerates static error content per site. Fallback: embed default text if static content unavailable.

Follow a frontend-first approach, in this exact order:

  1. Define types in .types.ts first, aligned with expected content structure
  2. Scaffold test file — create .test.tsx with describe blocks for mapper + rendering → Static: write full mapper tests + rendering tests NOW (TDD — before implementation)
  3. Write Storybook stories with realistic mock data BEFORE implementing the component
  4. Create these story variants: Default, MinimalContent, EmptyState, LongContent, AllOptions, FigmaFidelity → FigmaFidelity must mirror Figma content exactly (Lorem Ipsum OK if Figma uses it) → All other variants use realistic hotel context data — never Lorem Ipsum
  5. Stories must include tags: ['autodocs', 'vitest'], full EN autodocs documentation with Figma desktop + mobile links, User Story link, and feature bullet list
  1. Implement the component — .tsx + .scss (BEM + CSS custom properties) → Static: Server Component (no ‘use client’)
  2. Write mapper — .mapper.ts transforming siteRoot properties → ErrorPage props → Two mapper functions: mapError404(siteRoot) and mapError500(siteRoot)
  3. Run unit tests — pnpm --filter modules test — all must pass before continuing
  4. Register in packages/modules/src/registry.ts with { component, mapper, moduleId }
  5. Export via index.ts with named exports
  6. Pre-gate checks — pnpm typecheck + pnpm lint — both must pass before visual testing

Phase B7 — Pixel Perfect Visual Testing (HARD GATE)

Section titled “Phase B7 — Pixel Perfect Visual Testing (HARD GATE)”

This phase is MANDATORY. Phase C cannot begin until both thresholds are met (or the fallback limit is reached).

Thresholds (both viewports must pass):

  • Desktop (1440×900): diff ≤ 5%
  • Mobile (375×812): diff ≤ 10%

Fallback: If after 10 fix iterations or 30 minutes thresholds are still not met, stop and notify the user explicitly with current diff values, then proceed to Phase C. Manual visual review is required before merge.

  1. Add “Error Pages” tab to siteRoot via migration — NOT a new Element Type
  2. No SVG thumbnail needed (not a Block List module)
  3. No Block List registration needed
  4. Create test content on siteRoot error fields
  5. Validate mapper against real API response
  6. Test in at least 2 sites
  7. Update not-found.tsx and error.tsx to use the module
  1. packages/modules/src/m46-error-page/index.ts
  2. packages/modules/src/m46-error-page/ErrorPage.tsx
  3. packages/modules/src/m46-error-page/ErrorPage.scss
  4. packages/modules/src/m46-error-page/ErrorPage.types.ts
  5. packages/modules/src/m46-error-page/ErrorPage.mapper.ts
  6. packages/modules/src/m46-error-page/ErrorPage.stories.tsx
  7. packages/modules/src/m46-error-page/ErrorPage.test.tsx
  8. apps/cms/Savoy.Cms/Migrations/AddErrorPagesMigration.cs (adds tab to siteRoot)
  • BEM class names with SASS (no CSS Modules, no Tailwind)
  • All colors, fonts, spacing via CSS custom properties (var(—token-name))
  • SCSS uses BEM nesting with &__element, &--modifier (max 3 levels)
  • Root element: <section data-module="errorPage" data-module-id={moduleId}>
  • Semantic HTML (section, h2-h4)
  • Responsive: mobile-first with min-width breakpoints
  • Fluid layout — no fixed pixel widths
  • Storybook stories use realistic hotel context data (FigmaFidelity is the only exception)
  • Stories include tags: ['autodocs', 'vitest'], Figma link in parameters.design
  • This module extends ModuleLayoutProps but layout spacing may not be used (error pages don’t go through Block List)