Text offset array expressions
Shared symbol text-offset accepts the existing x,y constant and expressions returning two numbers: literal/semiliteral, array assertions, get or global-state. Units are em; each component must lie within-256..256 and is multiplied by that feature's actual text-size in logical pixels.
map.setLayoutProperty('labels','text-offset',
['semiliteral', [['get','offsetX'],['get','offsetY']]]);
map.setGlobalStateProperty('labelOffset',[2,0]);
map.setLayoutProperty('labels','text-offset',
['array','number',2,['global-state','labelOffset']]);
await map.sqs.flush({render:true});
Attribute/global-state updates, text size, rotation and anchor share the actual glyph footprint. Drawing, collision and query hit testing move together, preserving source/layer identity. Constant x,y is not parsed as an expression; literal can explicitly wrap array data. Wrong output length/type/range rejects the candidate. Layout continues to reject feature-state.
Existing256-character/4096-byte, atlas and expression budgets remain. Line placement, formatted mixed text/complex shaping and independent URL-domain and other unsupported layouts remain separate work. Independent URL basemaps have their own expression scope. Portable script. Actual native QuickJS and browser main-thread/Worker query/blue glyph evidence is recorded; mobile devices and formal release remain separate.
08.39 aligns shared symbol integer layout zoom and fractional paint zoom; see the layout zoom rule.