Skip to content

36 — Interaction & Navigation Patterns (envisioning)

Envisioning doc — needs confirmation. Governed by 00-ROADMAP.md. Created 2026-06-20 (owner-raised, off the doc-33 roll-up). The cross-cutting "how does touch behave" layer: how you navigate to a thing from a reference to it, how text responds to touch (select / copy / edit), and what "I saw this" means. Grounded in the actors (12), the Portals/lenses model (26), the design system (22), and the professional-calm brand (31). Nothing built from this doc yet beyond the backend provenance it enables.

What triggered this

Reviewing the doc-33 roll-up: a rolled-up comment didn't say where it came from ("Project / Track / V#"), and the owner expected to tap it and be taken to its place — but with a confirmation (a button / bubble / message), not a blind jump. Plus: text should be touch-sensitive (select, copy, edit in place), and the system should capture a "view" event, not only status changes. These are one family: references become destinations, and touch is the verb.

Principle: a reference carries its provenance and is a portal to its source

Every time we show a reference to something that lives elsewhere — a rolled-up comment, a note, a search hit, an activity row, a shared item — it must (a) say what it is and where it lives (provenance) and (b) be navigable to that exact place (a portal to the source). One pattern, reused everywhere.

Provenance — the source path is always shown

A rolled-up annotation reads, e.g.: 💬 LOVEREDemo PlaceTrack 3V2 · 2d ago · "fix the low end". The kind chip (📝 note vs 💬 feedback) + the breadcrumb (roster › artist › project › track › version, trimmed to what's relevant at this altitude) answer "what / where" before you tap. (Backend now serves this: the roll-up items populate track/project names + file.version — doc 33.)

A tap must not blind-jump and lose the user's place. Recommended pattern — peek then go:

  1. Tap a reference → it expands in place (a "peek": full content + the provenance breadcrumb + the timecode for feedback) with an explicit "Open in {location} →" affordance. The expand is the confirmation — cheap, reversible, shows context.
  2. Tap "Open →" → navigate to the exact anchor:
    • feedback (timeline comment) → the track's Wave, seeking to timeStart, the comment focused.
    • note → the anchor node's Portal, the note focused.
  3. Long-press a reference → context menu ("Open location", "Copy", "Edit" if yours, "Resolve") — the iOS-native secondary path; pairs with touch-text below.

Don't auto-navigate on a single tap from a digest surface — the digest's job is overview; navigation is a deliberate second act (professional calm: the app doesn't yank you around). On a focused surface (the node's own thread) a single tap-to-seek is fine — there's no context to lose.

The hierarchy is symmetric (the owner's "which way around?")

A comment is authored on a version (File) in the Wave; it then rolls up for visibility — version → track → project → artist → roster — and you navigate down from any of those levels back to the exact version. Up = aggregation (the eagle view); down = location (the tap). Same edge, two directions.

Roster needs a Portal. Roster is today only a label+color grouping (doc 26 Layer 1). To host the roster-level roll-up band (and roster metadata / its own identity), Roster joins "Portals everywhere" (doc 26 Layer 4) — a lightweight Portal whose body is the cross-artist digest. Flag for the Portals phase.

Moving between hierarchy levels (artist → project → track → version, and back) currently pops — a stock push — which fights the identity gradient/halo painted at the top of every screen. The owner wants the transition to zoom / fade so the gradient feels continuous, not reset. This is not new vision — it's the unbuilt navigation half of two owner-locked principles:

  • doc 22 §4 — "nothing pops; transitions interpolate." Continuous, driven motion over boolean cuts.
  • doc 22 §6 — halo continuity down the chain. The halo already crossfades from the parent's identity color → the child's cover color (Artist → Project → Track → Version; doc 24 backlog). The screen transition should ride the same continuity.

The model — the cover is the through-line. Tapping an artist/project/track tile should feel like zooming into that cover: the tapped artwork expands into the destination's hero, and the ScreenBackground halo carries the gradient through the push (parent color already present → settles to the child's). Back = the reverse zoom-out. Net: one continuous gradient across the whole browse stack, the artwork as the spatial anchor — Liquid-Glass-native, on-brand calm.

  • Best-practice (iOS 18+): NavigationLink/navigationDestination with **.navigationTransition(.zoom)
    • .matchedTransitionSource** on the tapped cover → the destination hero. The halo is already identity- keyed (doc 22 §6), so it interpolates for free across the push instead of re-fading. Graceful fallback on older OS = a quick cross-fade (still no pop).
  • Scope/where: the browse hierarchy pushes (Home→Artist→Project→Track→Player) — a design-heavy, layout-critical slice (Claude Code per the routing rule), shared via a small reusable transition modifier so every level uses one mechanism (not per-screen invention). Roadmap it as its own polish slice; it touches every browse navigation and wants careful tuning against the halo timing (don't double-animate).

Decision flagged below (#5). Until built, pushes stay stock; nothing here blocks other work.

Touch-text sensitivity — select / copy / edit in place

Text is not inert. The documented contract:

  • Any annotation/metadata text is selectable + copyable (.textSelection(.enabled)) — a reviewer copies a lyric line, a manager copies an ISRC. Zero-cost, expected.
  • Owned text is editable in place — your own note/comment/metadata shows a subtle edit affordance (tap to enter an inline field, or long-press → "Edit"); not-owned text is read-only (roles gate). Edits are the existing PUT /comment/:id / metadata PATCH — no new backend.
  • Consistent affordance, not per-screen invention — the same "tap-to-edit / long-press-for-actions" verb everywhere, so the user learns it once (design-system level — belongs in 22).

"View" events — seen-state, not only status

The owner: "we will require to get the event of 'view', not only action taken of status." Distinct from approvals/resolves (an action), a view is a passive seen signal — who has actually looked at this version / comment / note. Value (B2B, doc 31): "the producer has seen my revision request," "3 of 4 approvers have opened the master." This is read-receipts for the catalog.

  • Model (envision): a per-(user, node) seen record — { user, nodeType, nodeId, lastSeenAt } — set when a node is actually viewed (debounced, on meaningful dwell, not on every scroll). Drives "unseen" dots, "seen by N", and an unseen filter on the roll-up (a strong default lens: "new since I last looked").
  • Distinct from doc-35 events (which broadcast mutations) and from notifications (which alert on events). A view is neither — it's accumulated state. New surface; its own slice.
  • Privacy/quiet: seen-state is calm by default (a dot, not a notification); "seen by" detail is opt-in per the fair-credit/professional-calm posture (doc 31). Decide visibility rules at build time.

How this sequences

  • Shipped now (backend): roll-up item provenance (track/project names + file.version on the roll-up items) — the data the breadcrumb + navigation need.
  • Next (iOS): the peek-then-go navigation + breadcrumb on roll-up items; touch-text select/copy + edit-in-place where owned. Rides the doc-33 badge+band slice.
  • Later (envision): the Roster Portal; the "view"/seen subsystem (its own slice — per-user seen store + unseen lens).

Decisions — to confirm (flagged)

  1. Navigation gesturepeek-then-go (tap expands → "Open →"; recommend) vs single-tap-navigates vs long-press-only. Different per surface (digest = peek; focused thread = direct)?
  2. Edit affordance — inline tap-to-edit vs long-press → "Edit" vs a visible pencil. Pick one, apply everywhere.
  3. "View"/seen — build it? If so: granularity (per version? per comment?), what "counts" as a view (dwell threshold), and whether "seen by {who}" is visible or just an aggregate.
  4. Roster Portal — confirm Roster becomes a Portal node (doc 26 Layer 4) to host its roll-up + identity.
  5. Navigation transition — ✅ BUILT 2026-06-21 (zoom-into-cover, .navigationTransition(.zoom) + matchedTransitionSource across all browse hops; halo carried through via doc-22 §6). xcodebuild green; ⏳ visual QA + halo-timing tuning pending. (Cross-fade fallback happens automatically when no source matches.)

Cross-references

Ctrl-Audio Platform Documentation