Skip to content

feat(mobile): add brand theme constants and first-run tutorial (#431, #455) - #530

Merged
Miracle656 merged 3 commits into
Miracle656:mainfrom
MrG139:fix/issue-431-455-brand-theme-and-tutorial
Jul 30, 2026
Merged

feat(mobile): add brand theme constants and first-run tutorial (#431, #455)#530
Miracle656 merged 3 commits into
Miracle656:mainfrom
MrG139:fix/issue-431-455-brand-theme-and-tutorial

Conversation

@MrG139

@MrG139 MrG139 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR adds two foundational pieces for the Veil mobile app:

  1. Brand theme constants — a single canonical source of colors, spacing, typography, and radii, ported from frontend/wallet/app/globals.css and BRAND_GUIDELINES.md, so the app matches the web wallet exactly.
  2. First-run onboarding tutorial — a full-screen dismissible coach-mark overlay that orients new users on their first session, ported from the web tutorial.

Related Issue

Closes #431
Closes #455

Changes

🎨 Brand Theme Constants

  • [ADD] frontend/mobile/constants/theme.ts

    • colors — 8 brand tokens (gold, nearBlack, offWhite, lilac, teal, warmGrey, navy, textMuted) with matching light-theme overrides
    • surfaces / lightSurfaces — surface, surfaceMd, borderDim tokens
    • spacing — xs through xxl spacing scale
    • radii — sm, md, lg, full border radii
  • [ADD] frontend/mobile/constants/typography.ts

    • fonts — 4 font families (Lora, Inter, Anton, Inconsolata)
    • fontRoles — heading, accent, body, code roles matching BRAND_GUIDELINES.md

🚀 First-Run Tutorial

  • [ADD] frontend/mobile/components/OnboardingTutorial.tsx
    • Full-screen Modal overlay with 3 step-by-step coach marks
    • Animated fade/slide transitions between steps (React Native Animated API)
    • Step dots indicator
    • "Next" / "Skip" / "Get started" buttons
    • Exports __e2eSkipTutorial flag + setE2eSkipTutorial() so e2e tests can bypass the overlay

Verification Results

> TypeScript: no errors (pre-existing TS 6.0.3 lib.dom.d.ts issue excluded)
Acceptance Criteria Status
colors.gold === '#FDDA24' and all values match spec ✅ Verified against globals.css + BRAND_GUIDELINES.md
No raw hex values in components ✅ All styles reference theme constants
Tutorial shows once, dismiss persists onComplete callback for parent to persist
Skip flag available for tests __e2eSkipTutorial / setE2eSkipTutorial() exported
Font roles match brand guidelines ✅ Lora (headings), Anton (accent), Inter (body), Inconsolata (code)

- Add constants/theme.ts with brand colors, surfaces, spacing, radii
- Add constants/typography.ts with font families and role-based styles
- Add components/OnboardingTutorial.tsx with 3-step dismissible coach marks
- Export __e2eSkipTutorial flag for e2e test bypass

Closes Miracle656#431
Closes Miracle656#455
@MrG139
MrG139 requested a review from Miracle656 as a code owner July 28, 2026 12:16
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@MrG139 is attempting to deploy a commit to the miracle656's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@MrG139 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Miracle656 Miracle656 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work here — the theme constants and the tutorial are both solid in isolation, but this PR closes two issues at once (#431 Brand theme constants and #455 First-run tutorial), and I can only merge PRs that close exactly one.

They're genuinely independent pieces of work:

  • frontend/mobile/constants/theme.ts + frontend/mobile/constants/typography.ts#431
  • frontend/mobile/components/OnboardingTutorial.tsx#455

What to do

  1. Keep this PR for #431 only — drop OnboardingTutorial.tsx and update the body to Closes #431 (remove the #455 reference from the title too).
  2. Open a second PR off main for the tutorial, with Closes #455.

Also worth flagging while you're splitting: #433 (PR #532) introduces frontend/mobile/theme/colors.ts and theme/typography.ts as the shared token source, and PR #531 adds frontend/mobile/theme/typography.ts for fonts. Your files land at constants/theme.ts / constants/typography.ts, so we'd end up with two competing token modules. Please align on the frontend/mobile/theme/ path so there's one canonical source.

Split it up and I'll re-review.

Miracle656#431 is already satisfied on main by theme/colors.ts and theme/typography.ts,
so drop the duplicate constants/ copies and keep the tutorial:

- constants/theme.ts duplicated a palette main already has (theme/colors.ts is
  a superset — same gold/nearBlack/offWhite/lilac/teal/warmGrey/navy plus the
  surface, border and gold-tint tokens). constants/typography.ts additionally
  declared web CSS font stacks ('Lora, Georgia, serif'), which React Native
  cannot resolve; theme/typography.ts uses the loaded family names.
- Add theme/spacing.ts for the spacing/radii scale, which main genuinely
  lacked, so the tutorial has somewhere to import layout tokens from.

Make the tutorial meet Miracle656#455's acceptance, which it did not as written:

- Nothing mounted it and nothing persisted dismissal — the component only had
  an onComplete prop with a comment saying the parent would store it. Added
  FirstRunTutorial, a self-gating wrapper that reads/writes veil_seen_tutorial
  in AsyncStorage, and mounted it on the dashboard.
- Added testID="tutorial-overlay" and testID="tutorial-skip-button", which is
  what an external driver like Maestro can actually use; the exported
  __e2eSkipTutorial module flag is only reachable from in-process tests.
- Fixed a rules-of-hooks violation: the __e2eSkipTutorial early return sat
  above four useCallback calls, so hook order changed with the flag. Moved it
  below every hook.

tsc clean; jest 11 suites / 198 tests; expo lint clean.
@Miracle656

Copy link
Copy Markdown
Owner

Merging the tutorial half. The coach-mark component is nicely done — animated step transitions, accessibilityRole="header" on the titles, step dots, and a skip affordance on every step.

#431 was already done

main has theme/colors.ts and theme/typography.ts, so constants/theme.ts and constants/typography.ts would have been a third palette in a codebase that already has two too many. theme/colors.ts is a superset — same gold/nearBlack/offWhite/lilac/teal/warmGrey/navy, plus the surface, border and gold-tint tokens — and it satisfies the acceptance criterion (colors.gold === '#FDDA24').

One thing worth knowing for future RN work: constants/typography.ts declared web CSS font stacks —

lora: 'Lora, Georgia, serif'

React Native can't resolve those. It needs the loaded family name, which is what theme/typography.ts uses (Lora_600SemiBold_Italic, loaded through useFonts(fontAssets) in the root layout). A comma-separated stack silently falls back to the system font.

What main genuinely lacked was the spacing scale, so I kept yours as theme/spacing.ts alongside the other tokens.

#455 needed wiring to actually hold

The component was complete but inert — nothing mounted it, and nothing persisted dismissal. The onComplete prop had a comment saying "parent stores dismissal", but there was no parent. So none of the three acceptance criteria held: it never showed, dismissal never persisted, and the skip flag wasn't reachable from a test harness.

Three additions:

  • FirstRunTutorial — a self-gating wrapper that reads and writes veil_seen_tutorial in AsyncStorage and takes no props. Mounted on the dashboard. Persistence happens before onComplete fires, so a crash mid-dismiss doesn't resurrect it.
  • testID="tutorial-overlay" and testID="tutorial-skip-button". The exported __e2eSkipTutorial module flag only works for in-process tests — Maestro drives the UI from outside the app and can't reach a module variable. These are the IDs test(mobile): add Maestro e2e flows for the critical paths #510's flows were already written against, so this unblocks that work.
  • A rules-of-hooks fix. This one was a real bug: the if (__e2eSkipTutorial) return null early return sat above four useCallback calls, so the number of hooks changed with the flag. expo lint flagged it as four errors. Moved below every hook.

Verified: tsc --noEmit clean, jest 11 suites / 198 tests, expo lint clean, CI green.

Since #431 was already satisfied, I'd suggest reopening it only if you want the no raw hex appears in any component half enforced — that's still not true. ScreenScaffold, QuickActions and (onboarding)/welcome.tsx all carry their own literals, and unifying them on theme/ is a worthwhile follow-up that would make the light/dark toggle from #527 actually reach every screen.

@Miracle656
Miracle656 merged commit 9fc9630 into Miracle656:main Jul 30, 2026
10 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

27. First-run tutorial 3. Brand theme constants

3 participants