Skip to content

WIP: chore: park YouVersion brand-font implementation#272

Draft
jaredhightower-youversion wants to merge 4 commits into
mainfrom
feat/youversion-brand-fonts
Draft

WIP: chore: park YouVersion brand-font implementation#272
jaredhightower-youversion wants to merge 4 commits into
mainfrom
feat/youversion-brand-fonts

Conversation

@jaredhightower-youversion

@jaredhightower-youversion jaredhightower-youversion commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Parked snapshot of the YouVersion brand-font implementation (Aktiv Grotesk App + Untitled Serif), preserved at commit 31b8476 after the fonts were reverted on the main figma PR pending licensing.
  • Re-application target once legal clears: load Untitled Serif via the gated GET /v1/fonts/{font_id}/stylesheet endpoint (font_id 1, slug untitled-serif) instead of hardcoded CDN @font-face.
  • ⚠️ Do not merge — blocked on font licensing (see below). Also overlaps the figma PR (useOrganizations + abbreviation-tile redesign land there); rebase onto main after that merges so only the font diff remains.

PR Description

What changed

Brand fonts (the parked work):

  • packages/core/src/styles/theme.css--yv-font-sans: 'Aktiv Grotesk App', 'Inter', --yv-font-serif: 'Untitled Serif', 'Source Serif 4'.
  • packages/ui/src/styles/global.css — CDN @font-face for Aktiv Grotesk App (400/700) + Untitled Serif (400/700); @theme inline --font-sans/--font-serif brand defaults + --font-aktiv / --font-untitled-serif aliases.
  • packages/ui/src/lib/verse-html-utils.tsAKTIV_FONT / UNTITLED_SERIF_FONT constants + FontFamily union.
  • packages/ui/src/components/bible-reader.tsx — Reader font picker brand options; default UNTITLED_SERIF_FONT.
  • packages/ui/src/components/bible-version-picker.tsx — abbreviation tile renders Untitled Serif Bold.
  • i18n aktivFontName / untitledSerifFontName (en/es/fr); stories + tests updated.

Also present (lands via the figma PR, not unique to this branch):

  • OrganizationsClient + useOrganization / useOrganizations hooks; publisher names in BibleVersionPicker; VersionAbbreviationIcon Figma redesign.

Why

  • Preserve the brand-font work intact so it can be re-applied cleanly once licensing is resolved, rather than reconstructing it from history.

Licensing blocker

  • Aktiv Grotesk (Dalton Maag): breached the moment a third-party developer accesses the font file via their app key — no held licence tier covers SDK distribution to third parties.
  • Untitled Serif (Klim): depends on whether a Platform developer qualifies as a "partner" under the Enterprise licence — open legal question.
  • Detail: docs/adr/0001-revert-brand-fonts-pending-licensing.md (on main via the figma PR).

Greptile Summary

This PR parks two related workstreams together \u2014 brand font adoption (Aktiv Grotesk App + Untitled Serif, blocked on licensing) and a BibleVersionPicker redesign (publisher names, refreshed abbreviation tile, new OrganizationsClient / useOrganization / useOrganizations hooks). It is explicitly marked Do not merge and is intended as a snapshot for re-application once legal clears.

  • Brand fonts: --yv-font-sans and --yv-font-serif design tokens updated in theme.css; CDN @font-face blocks added in global.css; AKTIV_FONT / UNTITLED_SERIF_FONT constants exported from verse-html-utils.ts; reader and picker components updated to use them as defaults.
  • Organizations feature: New OrganizationsClient in core, useOrganization / useOrganizations / useOrganizationsClient hooks, and BibleVersionPicker updated to resolve and display publisher names at the list level (avoiding N+1 requests); organization_id is now persisted in RecentVersion so recent rows can also show publisher names.
  • useOrganizations cache gap: When every ID in a batch fails, the resolved.size === 0 guard causes an early return without adding those IDs to cacheRef; they are re-requested on every subsequent idsKey or client change. The hook also exposes no loading or error state, unlike useOrganization.

Confidence Score: 3/5

Not safe to merge — the PR is intentionally blocked on licensing, and the useOrganizations cache has a retry bug that should be fixed before the branch lands.

The useOrganizations hook never records which IDs it has already attempted but failed to fetch. Any totally-failed batch leaves those IDs absent from cacheRef, so they are re-requested on every change to the ID set or client identity. In a version list where many org IDs return 404, this translates to repeated unnecessary network calls every time the user types in the search box. The changeset also documents unlicensed brand fonts as a released feature, which must be cleaned up before merge.

packages/hooks/src/useOrganizations.ts needs the failed-ID tracking fix; .changeset/bible-version-picker-figma-updates.md needs to be rewritten or split to remove brand-font references before merge.

Important Files Changed

Filename Overview
packages/hooks/src/useOrganizations.ts New batch-fetch hook with custom cache logic; failed IDs are not tracked, causing repeated retries when idsKey changes, and the hook exposes no loading/error state unlike its sibling useOrganization.
packages/hooks/src/useOrganization.ts New single-organization fetch hook; follows established useApiData pattern with enabled guard for empty IDs, consistent with other hooks.
packages/hooks/src/useOrganizationsClient.ts New hook that constructs an OrganizationsClient from context; correctly memoized on context fields, matches pattern of other client hooks.
packages/core/src/organizations.ts New OrganizationsClient with UUID validation and Zod schema parse on the response; clean and consistent with other core clients.
packages/ui/src/components/bible-version-picker.tsx Adds publisher name display and updates VersionAbbreviationIcon styling; useOrganizations is called at the list level (no N+1), organization_id is persisted in RecentVersion for publisher display on recently-used rows.
packages/ui/src/styles/global.css Adds CDN @font-face for Untitled Serif (400/700) and promotes brand fonts as @theme inline defaults; hardcoded storage.googleapis.com URLs are flagged as licensing-blocked in the PR description.
packages/core/src/styles/theme.css Updates --yv-font-sans and --yv-font-serif design tokens to use brand fonts with Inter/Source Serif 4 as fallbacks; change is SDK-wide and blocked pending licensing clearance.
.changeset/bible-version-picker-figma-updates.md Changeset documents brand fonts as a released feature despite the PR being licensing-blocked; will need to be rewritten or split before merging.
packages/ui/src/lib/verse-html-utils.ts Adds AKTIV_FONT and UNTITLED_SERIF_FONT constants; retains INTER_FONT/SOURCE_SERIF_FONT for backward compatibility; FontFamily union extended correctly.
packages/ui/src/components/bible-reader.tsx Swaps font defaults and picker labels to brand font constants; straightforward constant replacement, no logic changes.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant BVP as BibleVersionPicker
    participant UO as useOrganizations
    participant Cache as cacheRef (Map)
    participant OC as OrganizationsClient
    participant API as GET /v1/organizations/:id

    BVP->>UO: useOrganizations([org_a, org_b, org_a])
    UO->>UO: toUniqueIds to [org_a, org_b]
    UO->>Cache: filter missing (not in cache)
    Cache-->>UO: "missing = [org_a, org_b]"
    UO->>OC: Promise.allSettled([getOrg(org_a), getOrg(org_b)])
    OC->>API: GET /v1/organizations/org_a
    OC->>API: GET /v1/organizations/org_b
    API-->>OC: 200 org_a
    API-->>OC: 200 org_b
    OC-->>UO: resolved Map org_a, org_b
    UO->>Cache: set org_a, set org_b
    UO->>BVP: organizations Map with org_a and org_b

    Note over UO,Cache: If ALL fetches fail, resolved.size===0,<br/>early return, IDs NOT cached, re-fetched<br/>on next idsKey or client change
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant BVP as BibleVersionPicker
    participant UO as useOrganizations
    participant Cache as cacheRef (Map)
    participant OC as OrganizationsClient
    participant API as GET /v1/organizations/:id

    BVP->>UO: useOrganizations([org_a, org_b, org_a])
    UO->>UO: toUniqueIds to [org_a, org_b]
    UO->>Cache: filter missing (not in cache)
    Cache-->>UO: "missing = [org_a, org_b]"
    UO->>OC: Promise.allSettled([getOrg(org_a), getOrg(org_b)])
    OC->>API: GET /v1/organizations/org_a
    OC->>API: GET /v1/organizations/org_b
    API-->>OC: 200 org_a
    API-->>OC: 200 org_b
    OC-->>UO: resolved Map org_a, org_b
    UO->>Cache: set org_a, set org_b
    UO->>BVP: organizations Map with org_a and org_b

    Note over UO,Cache: If ALL fetches fail, resolved.size===0,<br/>early return, IDs NOT cached, re-fetched<br/>on next idsKey or client change
Loading

Comments Outside Diff (2)

  1. packages/hooks/src/useOrganizations.ts, line 573-577 (link)

    P1 Failed IDs never enter the cache — causes repeated retries

    When every ID in a missing batch fails (network error or 404), resolved.size === 0 and the function returns early without updating cacheRef.current. Those IDs are absent from the cache, so the next time idsKey or client changes (e.g., the user types in the search box and a new org ID appears in the list), all previously-attempted-but-failed IDs are included in missing again and re-requested.

    The fix is to track attempted IDs separately (e.g., an attemptedRef Set) and skip them in future missing computations, regardless of whether they resolved or rejected.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/hooks/src/useOrganizations.ts
    Line: 573-577
    
    Comment:
    **Failed IDs never enter the cache — causes repeated retries**
    
    When every ID in a `missing` batch fails (network error or 404), `resolved.size === 0` and the function returns early without updating `cacheRef.current`. Those IDs are absent from the cache, so the next time `idsKey` or `client` changes (e.g., the user types in the search box and a new org ID appears in the list), all previously-attempted-but-failed IDs are included in `missing` again and re-requested.
    
    The fix is to track attempted IDs separately (e.g., an `attemptedRef` Set) and skip them in future `missing` computations, regardless of whether they resolved or rejected.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Cursor Fix in Codex

  2. packages/hooks/src/useOrganizations.ts, line 548-584 (link)

    P2 No loading or error state exposed — inconsistent with useOrganization

    useOrganizations returns only { organizations }, while its sibling useOrganization returns { organization, loading, error, refetch }. Callers have no way to distinguish "still fetching" from "all fetches failed" — both cases yield an empty Map. This means the BibleVersionPicker silently renders without publisher names when the network is down, with no observable difference from the pre-fetch state.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/hooks/src/useOrganizations.ts
    Line: 548-584
    
    Comment:
    **No `loading` or `error` state exposed — inconsistent with `useOrganization`**
    
    `useOrganizations` returns only `{ organizations }`, while its sibling `useOrganization` returns `{ organization, loading, error, refetch }`. Callers have no way to distinguish "still fetching" from "all fetches failed" — both cases yield an empty `Map`. This means the `BibleVersionPicker` silently renders without publisher names when the network is down, with no observable difference from the pre-fetch state.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code Fix in Cursor Fix in Codex

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
packages/hooks/src/useOrganizations.ts:573-577
**Failed IDs never enter the cache — causes repeated retries**

When every ID in a `missing` batch fails (network error or 404), `resolved.size === 0` and the function returns early without updating `cacheRef.current`. Those IDs are absent from the cache, so the next time `idsKey` or `client` changes (e.g., the user types in the search box and a new org ID appears in the list), all previously-attempted-but-failed IDs are included in `missing` again and re-requested.

The fix is to track attempted IDs separately (e.g., an `attemptedRef` Set) and skip them in future `missing` computations, regardless of whether they resolved or rejected.

### Issue 2 of 3
packages/hooks/src/useOrganizations.ts:548-584
**No `loading` or `error` state exposed — inconsistent with `useOrganization`**

`useOrganizations` returns only `{ organizations }`, while its sibling `useOrganization` returns `{ organization, loading, error, refetch }`. Callers have no way to distinguish "still fetching" from "all fetches failed" — both cases yield an empty `Map`. This means the `BibleVersionPicker` silently renders without publisher names when the network is down, with no observable difference from the pre-fetch state.

### Issue 3 of 3
.changeset/bible-version-picker-figma-updates.md:1-11
**Changeset documents unlicensed fonts as a shipped feature**

The changeset describes brand fonts (`--yv-font-sans: 'Aktiv Grotesk App'`, `--yv-font-serif: 'Untitled Serif'`) as part of the release. Since the PR is explicitly blocked pending licensing, this entry will need to be rewritten before the branch can be merged — otherwise npm consumers will see documentation for fonts they legally cannot use via SDK distribution. The changeset should either be split (font-only vs. non-font work) or have the brand-font references stripped out.

Reviews (1): Last reviewed commit: "feat(ui): apply YouVersion brand fonts t..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Adds useOrganizations(organizationIds), which resolves many organizations
at once, deduplicated by id, so a list of versions sharing publishers only
fetches each organization once. Renames useOrganizationClient ->
useOrganizationsClient and exports the new hook.
… Figma

Default design tokens now use the brand fonts (--yv-font-sans: Aktiv Grotesk
App, --yv-font-serif: Untitled Serif) with Inter / Source Serif 4 as
fallbacks, applied SDK-wide. BibleVersionPicker labels, headings, version
titles, and publisher names render in Aktiv Grotesk App; the abbreviation
tile renders in Untitled Serif Bold. Publisher names are resolved once at
the list level via useOrganizations, avoiding N+1 requests when many
versions share a publisher.
@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 31b8476

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-core Minor
@youversion/platform-react-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment on lines +1 to +11
---
"@youversion/platform-core": minor
"@youversion/platform-react-hooks": minor
"@youversion/platform-react-ui": minor
---

Update the Bible Version picker to match the latest Reader SDK Figma design, adding publisher names and refreshing the abbreviation tile.

- `@youversion/platform-core`: New `OrganizationsClient` with `getOrganization(organizationId)` for fetching an organization by its UUID (`GET /v1/organizations/{id}`), validated against the existing `OrganizationSchema`. The default design tokens now use the YouVersion brand fonts — `--yv-font-sans` is `'Aktiv Grotesk App'` and `--yv-font-serif` is `'Untitled Serif'`, with Inter / Source Serif 4 retained as graceful fallbacks. This applies SDK-wide (all components, including the Bible reader's serif text).
- `@youversion/platform-react-hooks`: New `useOrganization(organizationId)` hook (plus `useOrganizationsClient`) following the standard `useApiData` pattern. Fetching is skipped when the id is empty. Also adds `useOrganizations(organizationIds)`, which resolves many organizations at once, deduplicated by id, so a list of versions sharing publishers only fetches each organization once.
- `@youversion/platform-react-ui`: The `BibleVersionPicker` now uses the YouVersion brand fonts to match Figma — labels, headings, version titles, and the publisher name render in Aktiv Grotesk App (`--font-aktiv`), and the abbreviation tile renders in Untitled Serif Bold (new `--font-untitled-serif` token, CDN `@font-face`, falling back to Source Serif 4). `BibleVersionPicker` now renders the publisher name above the version title for versions that have an `organization_id` (rows without an associated organization render the title only), and recently used versions persist `organization_id` so they display the publisher too. Publisher names are resolved once at the list level via `useOrganizations` instead of per row, avoiding N+1 requests when many versions share a publisher. The `VersionAbbreviationIcon` tile now renders as a 64px square with a 6px radius, warm-neutral (`secondary`) fill, themed border, and serif typography using the foreground text color; recent-version and all-version rows share the same tile styling, and long or trailing-digit abbreviations (e.g. `NASB1995` → `NASB` / `1995`) stay readable without overflowing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Changeset documents unlicensed fonts as a shipped feature

The changeset describes brand fonts (--yv-font-sans: 'Aktiv Grotesk App', --yv-font-serif: 'Untitled Serif') as part of the release. Since the PR is explicitly blocked pending licensing, this entry will need to be rewritten before the branch can be merged — otherwise npm consumers will see documentation for fonts they legally cannot use via SDK distribution. The changeset should either be split (font-only vs. non-font work) or have the brand-font references stripped out.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .changeset/bible-version-picker-figma-updates.md
Line: 1-11

Comment:
**Changeset documents unlicensed fonts as a shipped feature**

The changeset describes brand fonts (`--yv-font-sans: 'Aktiv Grotesk App'`, `--yv-font-serif: 'Untitled Serif'`) as part of the release. Since the PR is explicitly blocked pending licensing, this entry will need to be rewritten before the branch can be merged — otherwise npm consumers will see documentation for fonts they legally cannot use via SDK distribution. The changeset should either be split (font-only vs. non-font work) or have the brand-font references stripped out.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Cursor Fix in Codex

@cameronapak cameronapak changed the title chore: park YouVersion brand-font implementation WIP: chore: park YouVersion brand-font implementation Jun 26, 2026
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.

1 participant