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 ↗

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.

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.
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}`;
}

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.
// 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();
}
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