Lehi 31st Ward landing page — hero with an image of Christ behind the title "Lehi 31st Ward," tagline "Come closer to Christ. Faith, service, and community." View Events and Sign In buttons. Cards below show Sunday Worship times (Sacrament Meeting 10:30 AM, Second Hour 11:30 AM), the chapel address (220 W 200 S Lehi UT 84043), and an "Everyone Welcome" invitation.
Projects / Live

Lehi 31st Ward

Launched

The website for my local LDS ward — Sunday service info, calendar, callings, member tools. The original build that grew into Ward Page.

SvelteKit Svelte 5 TypeScript Bootstrap 5 Sass Supabase Edge Functions GitHub Pages

What it doesLink to heading

The community website for Lehi 31st Ward — Sunday service info, sacrament program, calendars, events, callings, member tools, internal communications. Used by my actual ward.

It’s also the project that became the seed for Ward Page. I built lehi31 first, ran it for two years, then extracted the public-page-shaped subset into a multi-tenant SaaS while leaving lehi31 in active service for the ward.

StackLink to heading

  • Framework: SvelteKit 2 + Svelte 5 (runes) + TypeScript (strict)
  • UI: Bootstrap 5 + Sass
  • Data: Supabase Postgres + Supabase Auth + Deno-based Edge Functions for backend operations
  • Build: Vite + @sveltejs/adapter-static for a fully static export
  • Tests: Vitest + Playwright with integration tests against a real Supabase
  • Hosting: GitHub Pages with a custom domain — zero servers to keep alive

PermissionsLink to heading

A ward has many overlapping organizations — presidencies, groups, callings — and different people need different access to different subsystems. The permission system is the most complex piece of the codebase, and exactly the complexity I chose not to ship in Ward Page, which caps at three roles.

NotesLink to heading

Static export + Edge Functions on demand is a wildly capable shape. No Node server to keep alive, but full backend power when needed. For something with the longevity of a ward website, “nothing to babysit” is the right tradeoff.