Browser Media and Device Capabilities

Device Lab Full-screen Stage Controls

Complete fullscreen stage controls in the Device Lab scenario: includes realistic content, actionable controls, clear status feedback, and responsive layouts across sizes.

Browser Media and Device CapabilitiesFullscreen Stage ControlsHTMLCSSBrowser API
This example requires browser capability permissions. Keep ‘isolation mode’ turned off and trigger it with a click; a fallback state will be shown if permission is denied or unsupported.
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Device Lab Full-screen Stage Controls</title>
  <style>
    :root { --ink:#3a2419; --paper:#fbf3df; --accent:#c66a2d; --secondary:#b5c88d; --line:color-mix(in srgb, var(--ink) 18%, transparent); --shadow:0 24px 60px color-mix(in srgb, var(--ink) 16%, transparent); color-scheme:light; }
    * { box-sizing:border-box; }
    html { min-width:320px; background:var(--paper); }
    body { min-height:100vh; margin:0; overflow-x:hidden; color:var(--ink); font-family:Baskerville,"Songti SC",serif; background:linear-gradient(90deg,var(--paper) 0 72%,color-mix(in srgb,var(--secondary) 18%,var(--paper)) 72%); }
    button,input,select,textarea { color:inherit; font:inherit; }
    button,[role="button"],input,select,textarea { accent-color:var(--accent); }
    button { cursor:pointer; }
    button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible { outline:3px solid var(--accent); outline-offset:3px; }
    [hidden] { display:none !important; }
    @media (max-width:700px) { .mast h1 { line-height:1.08 !important; letter-spacing:-.035em !important; overflow-wrap:anywhere; } }
    @media (prefers-reduced-motion:reduce) { *,*::before,*::after { scroll-behavior:auto !important; animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } }
    .page{width:min(1200px,calc(100% - 38px));margin:auto;padding:76px 0}.mast{display:grid;grid-template-columns:2fr 1fr;gap:7vw;align-items:center;margin-bottom:44px}.mast h1{font-family:Baskerville,"Songti SC",serif;font-weight:800;font-size:clamp(48px,11vw,112px);line-height:.86;letter-spacing:-.06em;margin:0}.eyebrow{display:block;margin-bottom:22px;color:var(--accent);font:800 11px/1 ui-monospace,monospace;letter-spacing:.26em}.intro{max-width:46ch;margin-top:26px;font-size:17px;line-height:1.85}.scenario{align-self:end;padding-bottom:14px;border-bottom:5px solid var(--accent);font-weight:800}.demo{border-radius:0 60px 0 0;box-shadow:-16px 18px 0 color-mix(in srgb,var(--accent) 22%,transparent);position:relative;min-height:460px;overflow:hidden;padding:clamp(30px,6vw,72px);background:color-mix(in srgb,var(--paper) 92%,#fff)}@media(max-width:700px){.page{width:calc(100% - 22px);padding:30px 0}.mast{grid-template-columns:1fr}.demo{padding:20px}}
    .panel{padding:20px;border:1px dashed var(--secondary);border-radius:8px;background:color-mix(in srgb,var(--paper) 92%,#000)}.stack{display:grid;gap:10px}.row{display:flex;flex-wrap:wrap;gap:7px}.action{min-height:40px;padding:8px 12px;border:1px solid var(--secondary);border-radius:5px;color:var(--ink);background:transparent;font:700 12px/1 ui-monospace,monospace}.action.primary,.action.is-active{color:var(--paper);background:var(--accent)}.status{min-height:22px;color:var(--secondary);font:700 12px/1.5 ui-monospace,monospace}.status::before{content:"status: "}.metric{font:800 clamp(28px,6vw,56px)/1 ui-monospace,monospace}.muted{opacity:.64;font-family:ui-monospace,monospace}.meter{height:8px;background:repeating-linear-gradient(90deg,var(--line) 0 8px,transparent 8px 10px)}.meter>i{display:block;height:100%;background:var(--secondary)}.chip{padding:4px 7px;border:1px solid var(--secondary);font:700 10px/1 ui-monospace,monospace}
    .composition-studio{display:grid;gap:14px}.studio-ruler{display:flex;justify-content:space-between;padding:0 4px;color:color-mix(in srgb,var(--ink) 55%,transparent);font:700 11px/1 ui-monospace,monospace;letter-spacing:.14em}.studio-ruler::before{content:"01 / WORKING PROTOTYPE"}.studio-ruler::after{content:"360 · 768 · 1440"}.composition-studio .demo::after{content:"";position:absolute;right:18px;bottom:18px;width:24px;height:24px;border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);pointer-events:none}
    .fullscreen-studio{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(200px,.55fr);min-height:390px;border:1px solid var(--line)}#fullscreenStage{display:grid;align-content:space-between;min-height:390px;padding:clamp(26px,7vw,72px);color:#fff;background:radial-gradient(circle at 80% 20%,#7c3aed,#172554 58%)}#fullscreenStage:fullscreen{min-height:100vh}#fullscreenSlides{padding:0;margin:0;list-style:none}#fullscreenSlides li b{font-size:14px;letter-spacing:.2em}#fullscreenSlides h2{max-width:700px;margin:.3em 0;font-size:clamp(40px,9vw,92px);line-height:.95}#fullscreenProgress{height:8px;background:#ffffff33}#fullscreenProgress i{display:block;width:33.333%;height:100%;background:#fff;transition:width .25s}.fullscreen-studio>aside{display:grid;align-content:center;gap:18px;padding:24px;background:var(--paper)}.fullscreen-studio>aside output{font-size:28px;font-weight:900}.fullscreen-studio>aside div{display:grid;gap:8px}@media(max-width:700px){.fullscreen-studio{grid-template-columns:1fr}}.workflow-dispatch{display:grid;grid-template-columns:minmax(210px,.48fr) minmax(0,1.52fr);gap:18px}.dispatch-console{display:grid;align-content:start;gap:16px;padding:22px;border:1px solid var(--line);border-radius:18px;background:color-mix(in srgb,var(--secondary) 12%,var(--paper))}.dispatch-console h2{margin:.2em 0}.dispatch-console form,.dispatch-console label{display:grid;gap:7px}.dispatch-console input,.dispatch-console select{min-width:0;width:100%;padding:10px;border:1px solid var(--line);border-radius:8px;background:var(--paper)}.dispatch-console fieldset{display:flex;gap:12px;border:1px solid var(--line);border-radius:10px}.dispatch-console fieldset label{display:flex;align-items:center}.dispatch-workbench{--dispatch-load:.22;min-width:0;padding:14px;border-left:8px solid color-mix(in srgb,var(--accent) calc(var(--dispatch-load)*100%),var(--line));background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 8%,transparent),transparent 28%)}.dispatch-workbench[data-dispatch-priority="urgent"]{box-shadow:inset 0 0 0 2px var(--accent)}@media(max-width:900px){.workflow-dispatch{grid-template-columns:1fr}.dispatch-console{order:2}}
  </style>
</head>
<body data-aesthetic="magazine" data-composition="studio">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">magazine / Full-Screen Stage Controls / ADVANCED</span><h1>Device Lab Full-screen Stage Controls</h1><p class="intro">Complete fullscreen stage controls in the Device Lab scenario: includes realistic content, actionable controls, clear status feedback, and responsive layouts across sizes.</p></div><span class="scenario">Device Lab</span></header>
    <div class="composition-studio"><div class="studio-ruler" aria-hidden="true"></div><section class="demo" aria-label="Control interaction area for the Device Lab full-screen stage"><div class="workflow-dispatch"><aside class="dispatch-console"><span class="eyebrow">Device Lab DISPATCH</span><h2>Device check dashboard</h2><form id="workflowDispatchForm"><label>Task Type<select id="workflowDispatchKind"><option>Screen Capability</option><option>Input capability</option><option>Storage Capability</option></select></label><label>Check Items<input id="workflowDispatchAmount" type="number" min="1" max="9999" value="30"><span>Item</span></label><fieldset><legend>Processing priority</legend><label><input type="radio" name="workflowPriority" value="normal" checked>Regular</label><label><input type="radio" name="workflowPriority" value="urgent">Urgent</label></fieldset><button class="action primary" type="submit">Start checking</button></form><label>Current task<select id="workflowDispatchActive" aria-label="Switch current task"><option value="1">Screen Capability</option><option value="2">Input capability</option></select></label><output id="workflowDispatchSummary">Waiting for Dispatch</output></aside><section id="workflowCore" class="workflow-core dispatch-workbench" data-dispatch-priority="normal"><!-- 11DZ_CORE_START:browser-media-device-fullscreen-stage --><div class="fullscreen-studio"><section id="fullscreenStage" tabindex="0"><span class="eyebrow">FULLSCREEN STAGE</span><ol id="fullscreenSlides"><li data-slide="0"><b>01</b><h2>Preparing environment</h2><p>Device Lab · Use the left and right arrow keys to switch screens.</p></li><li data-slide="1" hidden><b>02</b><h2>Main demo flow</h2><p>Device Lab · Use the left and right arrow keys to switch screens.</p></li><li data-slide="2" hidden><b>03</b><h2>Check fallback state</h2><p>Device Lab · Use the left and right arrow keys to switch screens.</p></li></ol><div id="fullscreenProgress" role="progressbar" aria-label="Presentation Page Progress" aria-valuemin="1" aria-valuemax="3" aria-valuenow="1"><i></i></div></section><aside><output id="fullscreenState">Normal window</output><p>Fullscreen can only be triggered by the user; even if denied, you can still continue paging.</p><div><button id="fullscreenBack" class="action" type="button">Previous page</button><button id="fullscreenNext" class="action" type="button" data-core-bridge="browser-media-device-fullscreen-stage">Next page</button><button id="fullscreenToggle" class="action primary" type="button">Enter Full Screen</button></div></aside></div><!-- 11DZ_CORE_END --></section></div><p id="status" class="status" role="status" aria-live="polite"></p></section></div>
  </main>
  <script>
    'use strict';
    const statusOutput = document.querySelector('#status');
    const announce = message => { statusOutput.textContent = message; };
    /* 11DZ_CORE_SCRIPT_START:browser-media-device-fullscreen-stage */
const fullscreenStage=document.querySelector('#fullscreenStage');const fullscreenSlides=[...document.querySelectorAll('[data-slide]')];const fullscreenState={index:0};const fullscreenStateOutput=document.querySelector('#fullscreenState');const fullscreenToggle=document.querySelector('#fullscreenToggle');let fullscreenFallback=false;function renderFullscreen(){fullscreenSlides.forEach((slide,index)=>{slide.hidden=index!==fullscreenState.index;});const progress=document.querySelector('#fullscreenProgress');progress.setAttribute('aria-valuenow',String(fullscreenState.index+1));progress.querySelector('i').style.width=(fullscreenState.index+1)/fullscreenSlides.length*100+'%';document.querySelector('#fullscreenBack').disabled=fullscreenState.index===0;document.querySelector('#fullscreenNext').disabled=fullscreenState.index===fullscreenSlides.length-1;}function moveFullscreen(step){fullscreenState.index=Math.max(0,Math.min(fullscreenSlides.length-1,fullscreenState.index+step));renderFullscreen();announce('Demo No. '+(fullscreenState.index+1)+' page');}document.querySelector('#fullscreenBack').addEventListener('click',()=>moveFullscreen(-1));document.querySelector('#fullscreenNext').addEventListener('click',()=>moveFullscreen(1));fullscreenToggle.addEventListener('click',async()=>{try{if(document.fullscreenElement){fullscreenFallback=false;await document.exitFullscreen();}else if(fullscreenStage.requestFullscreen){fullscreenFallback=false;await fullscreenStage.requestFullscreen();}else{throw new Error('unsupported');}}catch{fullscreenFallback=true;fullscreenStateOutput.textContent='The current environment does not support fullscreen or has denied access';fullscreenToggle.textContent='Enter Full Screen';announce('Unable to enter fullscreen; presentation navigation still available');}});document.addEventListener('fullscreenchange',()=>{const active=document.fullscreenElement===fullscreenStage;if(active){fullscreenFallback=false;fullscreenStateOutput.textContent='Stage fullscreen active';}else if(!fullscreenFallback){fullscreenStateOutput.textContent='Normal window';}fullscreenToggle.textContent=active?'Exit full screen':'Enter Full Screen';});fullscreenStage.addEventListener('keydown',event=>{if(event.key==='ArrowRight')moveFullscreen(1);if(event.key==='ArrowLeft')moveFullscreen(-1);});renderFullscreen();
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"browser-media-device-fullscreen-stage","selector":"[data-core-bridge=\"browser-media-device-fullscreen-stage\"]","mode":"click","key":"Enter"};const workflowBridgeCore=document.querySelector('#workflowCore');const workflowBridgeControl=workflowBridgeCore.querySelector(workflowBridgeSpec.selector);if(!workflowBridgeControl)throw new Error('Missing core bridge: '+workflowBridgeSpec.name);const workflowBridgeControlledId=workflowBridgeControl.getAttribute('aria-controls');const workflowBridgeControlled=workflowBridgeControlledId?document.getElementById(workflowBridgeControlledId):null;workflowBridgeCore.dataset.workflowBridge=workflowBridgeSpec.name;workflowBridgeCore.dataset.workflowVersion='0';function workflowBridgeSerialize(){const visualNode=workflowBridgeCore.firstElementChild||workflowBridgeCore;const visual=getComputedStyle(visualNode);const outputs=[...workflowBridgeCore.querySelectorAll('output,[role="status"],[aria-live],meter,progress')].map(node=>node.textContent.trim()+'|'+node.getAttribute('aria-valuenow')+'|'+node.getAttribute('value'));const canvas=[...workflowBridgeCore.querySelectorAll('canvas')].map(node=>{try{return node.toDataURL().slice(-160);}catch{return 'canvas-unavailable';}});return JSON.stringify({html:workflowBridgeCore.innerHTML,status:statusOutput.textContent,outputs,canvas,visual:[visual.display,visual.visibility,visual.opacity,visual.transform,visual.filter,visual.backgroundColor,visual.width,visual.height],scroll:workflowBridgeSpec.mode==='scroll'?workflowBridgeControl.scrollTop:null});}let workflowBridgeLastEffect=workflowBridgeSerialize();function workflowBridgeSummary(){const label=(workflowBridgeControl.getAttribute('aria-label')||workflowBridgeControl.textContent||workflowBridgeControl.value||workflowBridgeSpec.name).trim().replace(/\s+/g,' ').slice(0,72);return label+' · Core Version '+workflowBridgeCore.dataset.workflowVersion;}function workflowBridgeCommit(){const effect=workflowBridgeSerialize();if(effect===workflowBridgeLastEffect)return {changed:false,summary:workflowBridgeSummary()};workflowBridgeLastEffect=effect;workflowBridgeCore.dataset.workflowVersion=String(Number(workflowBridgeCore.dataset.workflowVersion||0)+1);return {changed:true,summary:workflowBridgeSummary()};}function workflowBridgeScheduleCommit(){queueMicrotask(workflowBridgeCommit);setTimeout(workflowBridgeCommit,0);}workflowBridgeControl.addEventListener("click",workflowBridgeScheduleCommit);const workflowBridgeStateTargets=new Set(workflowBridgeCore.querySelectorAll('dialog'));if(workflowBridgeControlled&&workflowBridgeCore.contains(workflowBridgeControlled))workflowBridgeStateTargets.add(workflowBridgeControlled);workflowBridgeStateTargets.forEach(target=>['close','cancel','toggle'].forEach(eventName=>target.addEventListener(eventName,workflowBridgeScheduleCommit)));function workflowBridgeActivate(){workflowBridgeControl.focus();workflowBridgeControl.click();const result=workflowBridgeCommit();setTimeout(workflowBridgeCommit,0);return result;}function workflowBridgeReset(){workflowBridgeCore.dataset.workflowVersion='0';workflowBridgeLastEffect=workflowBridgeSerialize();}
const workflowDispatchCore=document.querySelector('#workflowCore');const workflowDispatchForm=document.querySelector('#workflowDispatchForm');const workflowDispatchSelect=document.querySelector('#workflowDispatchActive');const workflowDispatchJobs=new Map([["1",{"label":"Screen Capability","amount":12,"priority":"normal","touches":0}],["2",{"label":"Input capability","amount":21,"priority":"urgent","touches":0}]]);let workflowDispatchSerial=workflowDispatchJobs.size;function workflowDispatchDrive(job,activate=false){workflowDispatchCore.dataset.dispatchPriority=job.priority;workflowDispatchCore.style.setProperty('--dispatch-load',Math.min(.95,.16+job.amount/120));const bridge=activate?workflowBridgeActivate():{changed:false,summary:workflowBridgeSummary()};document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' Item · '+bridge.summary;announce('Current task on the device check:'+job.label);}function workflowDispatchRender(activeId,activate=false){workflowDispatchSelect.replaceChildren(...[...workflowDispatchJobs].map(([id,job])=>{const option=document.createElement('option');option.value=id;option.textContent=job.label+(job.priority==='urgent'?' · Urgent':'');return option;}));workflowDispatchSelect.value=activeId;workflowDispatchDrive(workflowDispatchJobs.get(activeId),activate);}workflowDispatchForm.addEventListener('submit',event=>{event.preventDefault();const data=new FormData(workflowDispatchForm);const id=String(++workflowDispatchSerial);workflowDispatchJobs.set(id,{label:document.querySelector('#workflowDispatchKind').value,amount:Math.max(1,Number(document.querySelector('#workflowDispatchAmount').value)||1),priority:data.get('workflowPriority'),touches:0});workflowDispatchRender(id,true);});workflowDispatchSelect.addEventListener('change',()=>workflowDispatchDrive(workflowDispatchJobs.get(workflowDispatchSelect.value),true));workflowDispatchCore.addEventListener('focusin',()=>{const job=workflowDispatchJobs.get(workflowDispatchSelect.value);if(!job)return;job.touches+=1;document.querySelector('#workflowDispatchSummary').textContent=job.label+' · '+job.amount+' Item · '+workflowBridgeSummary();});workflowDispatchRender('1',false);
workflowBridgeReset();
  </script>
</body>
</html>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback