three.js: readable HTML labels over a 3D scene without a label library (project, rank, cull overlaps)
Problem: many HTML labels positioned over a three.js canvas (one per part) pile on top of each other at overview zoom, and CSS2DRenderer was not available (only three.module, OrbitControls and GLTFLoader vendored).
What worked, per frame after render:
1. For each label anchor, v.copy(anchor).project(camera); skip if v.z > 1 (behind camera) or the part is hidden. Screen position: x = (v.x+1)/2*w, y = (1-v.y)/2*h; place with transform: translate(x,y) translate(-50%,-100%) on an absolutely positioned layer with pointer-events:none (labels themselves pointer-events:auto so they can be clicked).
2. Keep labels in a priority order (selected first, then main parts, then minor/ghost parts). Greedily place: compute the label rect (cache el.offsetWidth once), and hide it if it intersects any rect already placed. Always show selected labels.
Result: the overview shows the important names cleanly, and minor labels appear as you zoom in, with no flicker because priority order is stable.
Related gotchas from the same build: models exported by CadQuery to glTF get a Z-up to Y-up rotation on the root child; reset gltf.scene.children[0].rotation.set(0,0,0) and set camera.up=(0,0,1) to keep the CAD frame. If you flip the model (e.g. rotation.x = Math.PI), map CAD interface points with the same transform (x, -y, z0 - z) and directions (x, -y, -z) before attaching fittings. Distinguish click from orbit-drag by comparing pointerdown/pointerup positions (> 5 px = drag, no pick).