Skip to content

feat(staging): rework section-tab + canonical-forms anchoring + label rendering on the plinth #255

Description

@bradleypmartin

Surfaced from #225 PR1 (#250 / #254) headset smoke (Brad, 2026-05-22). The vertical rack — three SectionTabs + the canonical-forms heading at the left edge of the plinth working surface — picks up the surface tilt via the slot model (orientation: 'surface'), but the result reads janky from two directions:

What headset smoke surfaced

  1. Anchoring is not "satisfyingly centered." Canonical-forms heading sits at PLINTH_SECTION_TAB_HEADING_Y = 0.55 slot-local, which is exactly the working-surface back edge (PLINTH_WORKING_HEIGHT_QUADRICS = 0.55). The button's vertical extent extends past the back edge in slot-Y, so the upper half is "in the air" behind the plinth surface rather than seated on it. The three section tabs below are within the surface but the rack as a whole reads as drifting off the back.

  2. Yaw-billboarded label text intersects the tilted plinth surface. TapButton.faceCamera rotates the child Text node toward the camera while leaving this.group (the button body) tilted with the plinth slot. At ~20° tilt the text plane and button-body plane diverge enough that the troika text mesh visibly clips into the plinth slab from typical viewing poses. Tightening the offsets won't fix this — it's the rendering-model mismatch (yaw-billboarded text on a non-billboarded surface).

Direction

This will probably need a new rendering / design model for plinth-mounted labels rather than a tuning pass:

  • Anchoring (1): straightforward — compress the rack vertically + shift the heading down so the full 4-element stack centers within [0, workingSurfaceHeight] with margin. May want a per-scene PLINTH_SECTION_TAB_* retune driven by smoke. Cheapest fix.
  • Label-vs-surface (2): open. Options to evaluate (non-exhaustive):
    • Drop the per-button faceCamera for plinth-mounted buttons and let labels surface-tilt with the body (legibility tradeoff at glancing angles).
    • Move labels off the button body — e.g. a single column of slot-anchored labels next to the button column, with the buttons reduced to glyph/color affordances.
    • Bake labels onto a textured plane sized to the button (drop troika for plinth-mounted buttons specifically).
    • Counter-rotate the label child inside the button so the text plane re-aligns with the surface (works but introduces a per-frame compose step).

Sister concern to #252 (panel-backed readout visual) — both bump into "yaw-billboarded text on a tilted surface looks wrong"; whatever design language we land on for readouts will probably inform this too.

Code anchors

  • Slot positions: src/exhibits/quadrics/index.tsPLINTH_SECTION_TAB_X = -0.42, PLINTH_SECTION_TAB_HEADING_Y = 0.55, PLINTH_SECTION_TAB_TOP_Y = 0.42, PLINTH_SECTION_TAB_PITCH = 0.13.
  • Working surface depth: PLINTH_WORKING_HEIGHT_QUADRICS = 0.55 (slot-Y range [0, 0.55]).
  • Per-button label rendering: src/scaffold/ui/TapButton.ts faceCamera (:194 per current main).
  • Plinth slot model: src/scaffold/staging/Plinth.ts (SlotOrientation + billboarded-primitive carve-out at the file-top doc-comment).

Refs

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions