Michael L. Lawson

Project case study

Product prototype / 2026

Waymark

One faithful step at a time.

An accessible React and TypeScript prototype for a daily devotional companion, with a daily reading and streak, a resource library and a concept shop with a working cart.

Explore live project
Project
Self-initiated concept
Role
Product Design + Front-End Development
Built with
React 19 · TypeScript · Vite · Vitest
Code
View on GitHub ↗
Waymark homepage: You don’t need the whole map. Just the next faithful step.
The live Waymark homepage.

01 / The brief

A daily habit without the pressure.

Waymark is a prototype for a quiet daily devotional companion: a short reading each day, a gentle streak, honest resources for hard seasons and a small concept shop.

The tone mattered as much as the features. Most habit apps push. Waymark is built to feel calm: warm paper tones, generous space, one clear next step and no notifications or badges.

Waymark devotional page with a verse, reflection, prayer and a Mark today as read button
The daily devotional: verse, reflection, prayer and one action.

02 / The key decision

Count days the way people live them.

A streak is only fair if “today” means the visitor’s today. JavaScript’s toISOString() returns UTC dates, so in Arizona an evening reading after 5 p.m. would be saved as tomorrow and could break a streak.

Waymark builds dates from the visitor’s local calendar. Streaks are counted backward from the most recent completed day, so filling in a missed earlier day never resets the current run. A stored streak only stays live if the last reading was today or yesterday.

src/services/devotionalProgress.ts ↗
export function getLocalDateISO(date = new Date()): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
Waymark homepage on a phone
Phone: homepage
Waymark devotional with a reveal-the-verse card on a phone
Phone: today’s devotional

03 / Accessibility

Built for keyboards and screen readers.

  • The cart drawer moves focus inside when it opens, keeps Tab inside, closes on Escape and returns focus to the button that opened it.
  • While the drawer is open, the page behind it is marked inert, so assistive technology can’t wander into it.
  • On every route change, focus moves to the main content and the page title updates.
  • A live region announces changes such as adding to the cart and completing a reading.
src/components/Drawer.tsx ↗
// On open: remember what had focus, then move focus into the dialog.
// On close: return focus to whatever triggered the drawer.
if (isOpen) {
  previouslyFocusedRef.current = document.activeElement as HTMLElement | null;
  closeButtonRef.current?.focus();
  if (backgroundRef?.current) backgroundRef.current.inert = true;
} else {
  if (backgroundRef?.current) backgroundRef.current.inert = false;
  previouslyFocusedRef.current?.focus();
}
Waymark concept shop with the cart drawer open
Concept shop with the cart drawer open. No payment is collected.

04 / Design system

One set of tokens, one style guide.

Color, type, spacing and motion live in design tokens. Buttons, cards, the drawer, verse cards and the streak badge are reusable components, documented on a living style guide at /style-guide.

05 / Verification

121 tests, zero type errors.

Vitest and Testing Library run 121 tests across 16 files, covering components, the cart, devotional progress and page behavior. The build runs TypeScript in strict mode, and oxlint checks the source.

This is a front-end prototype: cart contents and progress stay in the visitor’s browser, and the checkout never collects payment.

Next: run short usability sessions where people complete a reading and a demo checkout, then fix whatever trips them up.

EXPLORE THE WORK

See the experience
in motion.

Open Waymark