A contact action should not obstruct the other work on the screen. While someone uses a dialog’s close control or a viewer’s interface, fixed UI may need to step aside. I inspected the common contact bar code and organized how those states can be communicated.
The preservation contract includes avoiding VR controls, and the common bar provides selector-based pausing and manual APIs. I added a new coordinator for overlapping states and a synthetic executable example. The criterion considers whether the current task remains usable alongside the enquiry path.
Separate reading concealment from task pausing
The common bar separates temporary scroll concealment from pausing according to page context. Its current code supports visible configured selectors and manual pause/resume APIs. Stepping aside during movement and remaining aside until a task ends are different policies.
Treating an open dialog or viewer as ordinary scroll concealment can let a restoration timer bring the bar back over an ongoing task. I pass that task state separately so normal reading and enquiry behavior resumes after the task finishes.
Wait for the final reason when tasks overlap
If a dialog and viewer are open together, closing the dialog must not restore the bar over the remaining viewer. Two instances of the same dialog can also be lost by one overwritten boolean. The new implementation gives each pause reason an independent release function.
Releasing twice acts only once, and the bar resumes after all reasons finish. Repeated state events use a condition interface that does not accumulate leases. This is a new coordinator that can connect to the common bar API, not a claim that every existing installation already contains it.
Coordinate manual pausing through one owner
The existing resume API clears manual pausing. If another script calls pause independently, a coordinator that does not know that reason can clear it. Route manual pause requests for one bar through one coordinator. The new implementation also avoids an unconditional startup resume by leaving the adapter untouched initially.
Selector-based context pausing and any separate manual-hide policy still need their roles defined. This module expects a synchronous state adapter. It does not discover and merge every request made by unrelated operational code. Establishing the state owner is necessary to connect restoration correctly.
Read actual fullscreen and viewer states
Fullscreen can connect to the document’s actual fullscreenElement and change event. The binding reads the initial state and releases its own condition during cleanup. Use one fullscreen binding per document rather than duplicating ownership in several places.
A parent page must not assume arbitrary access to interaction state inside a third-party VR frame. Establish the viewer’s supported events or integration. A new message connection would also need origin, sending frame and message-shape review. The executable example below uses synthetic states without invoking actual VR or fullscreen.
Code that coordinates pause reasons
The code manages independent reasons and ongoing conditions. An adapter for a real bar can call its existing pause and resume APIs. This article’s example changes the visibility of a bar owned only by the example and never invokes a customer site’s enquiry function.
Cleanup releases managed reasons and rejects new requests. Adapter failures preserve a release reason or cancel a failed acquisition for retry. Calculating state and establishing that an element is actually unobstructed are separate facts, so rendered review still follows.
Explore the implementation
JavaScript · Coordinate overlapping pause reasons
export function createPauseGate(setPaused){
if(typeof setPaused!=='function')throw new TypeError('A synchronous pause adapter is required');
const leases=new Map(),conditions=new Map();let paused=false,closed=false;
function snapshot(){return {paused:leases.size>0,reasons:[...new Set(leases.values())],leases:leases.size};}
function apply(){const next=leases.size>0;if(next!==paused){setPaused(next,snapshot());paused=next;}}
function acquire(reason){
if(closed||typeof reason!=='string'||!reason.trim())throw new TypeError('An active gate and reason are required');
const token=Symbol(reason);leases.set(token,reason);try{apply();}catch(error){leases.delete(token);throw error;}
let released=false;
return ()=>{if(released||closed)return;leases.delete(token);try{apply();released=true;}catch(error){leases.set(token,reason);throw error;}};
}
function setCondition(reason,active){
if(typeof active!=='boolean')throw new TypeError('Condition must be a boolean');
if(active&&!conditions.has(reason))conditions.set(reason,acquire(reason));
else if(!active&&conditions.has(reason)){conditions.get(reason)();conditions.delete(reason);}
}
return {acquire,setCondition,snapshot,destroy(){if(closed)return;const previous=new Map(leases);leases.clear();try{apply();}catch(error){for(const [k,v]of previous)leases.set(k,v);throw error;}conditions.clear();closed=true;}};
}
export function bindFullscreenPause(document,gate){
const update=()=>gate.setCondition('fullscreen',Boolean(document.fullscreenElement));
document.addEventListener('fullscreenchange',update);update();
return ()=>{document.removeEventListener('fullscreenchange',update);gate.setCondition('fullscreen',false);};
}
Close overlapping example states one at a time
The example is implemented to let a reader enable a simulated viewer state and open a dialog. Closing the dialog retains pausing while the viewer state remains, and ending the last state restores the bar. It reproduces no actual place or customer material and uses the same new coordinator inside the public HTML frame.
Ten checks used synthetic states and fake document events: overlap, independent instances of one reason, repeated release and condition events, startup state, invalid options, fullscreen cleanup, adapter failure and retry, and coordinator cleanup. Actual popup, VR, fullscreen and rendered collision behavior were not newly reviewed in a browser.