Action contextuelle de la photothèque
Dans le scénario de la photothèque, achevez une fiche d’actions contextuelles : elle comprend du contenu réel, des contrôles actionnables, un retour d’état clair et une mise en page adaptative selon la taille.
Après activation de JavaScript, ce cas peut être modifié, exécuté et enregistré. Code source fixe ci-dessous :
<!doctype html>
<html lang="fr" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Action contextuelle de la photothèque</title>
<style>
:root { --ink:#ecf7f3; --paper:#071310; --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:ui-monospace,"SFMono-Regular",Consolas,monospace; background:linear-gradient(color-mix(in srgb,var(--secondary) 5%,transparent) 1px,transparent 1px),linear-gradient(90deg,color-mix(in srgb,var(--secondary) 5%,transparent) 1px,transparent 1px),var(--paper);background-size:24px 24px; }
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(1080px,calc(100% - 30px));margin:auto;padding:48px 0}.eyebrow{color:var(--secondary);font:700 12px/1.3 ui-monospace,monospace}.eyebrow::before{content:"> ";color:var(--accent)}.mast{display:grid;grid-template-columns:1fr auto;border:1px solid var(--secondary);padding:18px;gap:24px;align-items:center;margin-bottom:16px}.mast h1{font-family:ui-monospace,"SFMono-Regular",monospace;font-weight:700;font-size:clamp(34px,7vw,72px);letter-spacing:-.055em;margin:12px 0;line-height:1.05}.intro{margin:0;color:color-mix(in srgb,var(--ink) 72%,transparent);font:400 14px/1.7 ui-monospace,monospace}.scenario{padding:7px 10px;border:1px dashed var(--secondary);font:700 11px/1 ui-monospace,monospace}.demo{border-color:var(--secondary);border-radius:8px;box-shadow:0 0 50px color-mix(in srgb,var(--secondary) 15%,transparent);position:relative;min-height:390px;padding:26px;background:color-mix(in srgb,var(--paper) 94%,#000)}@media(max-width:700px){.page{width:calc(100% - 18px);padding:20px 0}.mast{grid-template-columns:1fr}.demo{padding:14px}}
.panel{padding:20px;border:1px solid color-mix(in srgb,var(--accent) 50%,transparent);border-radius:18px;background:color-mix(in srgb,var(--paper) 88%,transparent);box-shadow:inset 0 0 24px color-mix(in srgb,var(--accent) 7%,transparent)}.stack{display:grid;gap:16px}.row{display:flex;flex-wrap:wrap;gap:9px}.action{padding:11px 16px;border:1px solid var(--accent);border-radius:6px;color:var(--accent);background:transparent;box-shadow:0 0 14px color-mix(in srgb,var(--accent) 10%,transparent)}.action.primary,.action.is-active{color:var(--paper);background:var(--accent)}.status{min-height:24px;color:var(--secondary);text-shadow:0 0 10px currentColor}.metric{font-size:clamp(30px,7vw,64px);font-weight:900;text-shadow:0 0 18px color-mix(in srgb,var(--accent) 40%,transparent)}.muted{opacity:.7}.chip{padding:6px 9px;border:1px solid var(--secondary);border-radius:4px}.meter{height:7px;background:color-mix(in srgb,var(--accent) 15%,transparent)}.meter>i{display:block;height:100%;box-shadow:0 0 12px var(--accent);background:var(--accent)}
.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}
.context-actions-demo{display:grid;gap:18px}.context-actions-demo header{max-width:680px}.context-actions-demo h2{margin:.2em 0}.context-surface{position:relative;display:grid;align-content:center;justify-items:start;min-height:320px;padding:clamp(28px,7vw,76px);border:2px dashed var(--line);border-radius:28px;background:color-mix(in srgb,var(--secondary) 10%,var(--paper))}.context-surface>span{font:800 10px/1 ui-monospace,monospace;letter-spacing:.18em}.context-surface>strong{margin-top:12px;font-size:clamp(34px,7vw,70px)}.context-surface>button{padding:11px 15px;border:1px solid var(--ink);border-radius:10px;background:var(--paper)}.context-menu{position:absolute;z-index:5;display:grid;width:210px;padding:7px;border:1px solid var(--line);border-radius:14px;background:var(--paper);box-shadow:0 18px 48px #0004}.context-menu button{display:grid;grid-template-columns:28px 1fr;gap:8px;padding:11px;border:0;border-radius:8px;background:transparent;text-align:left}.context-menu button:hover,.context-menu button:focus-visible{background:color-mix(in srgb,var(--secondary) 22%,transparent)}.context-menu hr{width:100%;margin:5px 0;border:0;border-top:1px solid var(--line)}.context-menu .context-danger{color:#a8322a}.context-actions-demo>output{padding:12px 16px;border-left:5px solid var(--accent);font-weight:900}.workflow-compare{display:grid;grid-template-columns:minmax(230px,.55fr) minmax(0,1.45fr);gap:18px}.compare-stage{display:grid;gap:12px;min-width:0}.compare-stage>header{display:flex;justify-content:space-between;gap:16px;align-items:end}.compare-stage h2{margin:.2em 0}.compare-core{--compare-split:50%;min-width:0;padding:14px;clip-path:inset(0 calc(100% - var(--compare-split)) 0 0);outline:1px dashed var(--accent);outline-offset:-8px;transition:clip-path .25s,filter .25s}.compare-core[data-compare-channel="B"]{filter:hue-rotate(32deg) saturate(1.15)}.compare-controls{display:grid;align-content:start;gap:16px;padding:20px;border:1px solid var(--line);border-radius:24px 4px 24px 4px;background:var(--paper)}.compare-controls fieldset,.compare-controls>label{display:grid;gap:8px}.compare-controls input[type="range"]{width:100%}#workflowCompareShelf{display:grid;gap:6px;padding:0;margin:0;list-style:none}#workflowCompareShelf li{display:grid;grid-template-columns:auto 1fr auto;gap:8px;padding:9px;border-top:1px solid var(--line);font-size:12px}@media(max-width:900px){.workflow-compare{grid-template-columns:1fr}.compare-stage{order:-1}.compare-core{clip-path:none;border-left:8px solid var(--accent)}}
</style>
</head>
<body data-aesthetic="terminal" data-composition="studio">
<main class="page">
<header class="mast"><div><span class="eyebrow">terminal / fiche d’actions contextuelles / BEGINNER</span><h1>Action contextuelle de la photothèque</h1><p class="intro">Dans le scénario de la photothèque, achevez une fiche d’actions contextuelles : elle comprend du contenu réel, des contrôles actionnables, un retour d’état clair et une mise en page adaptative selon la taille.</p></div><span class="scenario">Bibliothèque de photos</span></header>
<div class="composition-studio"><div class="studio-ruler" aria-hidden="true"></div><section class="demo" aria-label="Zone d’interaction des actions contextuelles de la photothèque"><div class="workflow-compare"><form id="workflowCompareControls" class="compare-controls"><fieldset><legend>Canal de comparaison</legend><label><input type="radio" name="workflowChannel" value="A" checked>A · Version originale</label><label><input type="radio" name="workflowChannel" value="B">B · version révisée</label></fieldset><label>Poids de l’intensité des filtres<input id="workflowCompareWeight" type="range" min="10" max="90" value="50"><output id="workflowCompareWeightValue">50 %</output></label><label><input id="workflowCompareFreeze" type="checkbox">Geler la version actuelle</label><button id="workflowCompareCapture" class="action primary" type="button">Version d’échantillonnage et d’enregistrement</button><ol id="workflowCompareShelf" aria-label="Enregistrement de version"></ol></form><section class="compare-stage"><header><span class="eyebrow">Bibliothèque photo COMPARE</span><h2>Comparaison double version de la version photo</h2><output id="workflowCompareLive">Version A en attente d’échantillonnage</output></header><section id="workflowCore" class="workflow-core compare-core" data-compare-channel="A" style="--compare-split:50%"><!-- 11DZ_CORE_START:overlays-feedback-context-actions --><section class="context-actions-demo"><header><span class="eyebrow">CONTEXT ACTIONS</span><h2>Actions rapides de la bibliothèque de photos</h2><p>Clic droit, Shift + F10 ou le bouton peuvent tous ouvrir le même ensemble d’actions contextuelles.</p></header><div id="contextSurface" class="context-surface" tabindex="0" aria-label="Zone d’actions pour les enregistrements de la photothèque"><span>SELECTED ITEM</span><strong>Enregistrements de la photothèque</strong><p>Dans cette zone, cliquez avec le bouton droit ou appuyez sur Shift + F10</p><button id="contextOpen" type="button" aria-haspopup="menu" aria-expanded="false" data-core-bridge="overlays-feedback-context-actions">Ouvrir le menu d’actions</button><div id="contextMenu" class="context-menu" role="menu" hidden><button type="button" role="menuitem"><span aria-hidden="true">✎</span><span class="context-action-copy">Renommer</span></button><button type="button" role="menuitem"><span aria-hidden="true">□</span><span class="context-action-copy">Créer une copie</span></button><button type="button" role="menuitem"><span aria-hidden="true">→</span><span class="context-action-copy">Déplacer vers…</span></button><hr><button type="button" role="menuitem" class="context-danger"><span aria-hidden="true">×</span><span class="context-action-copy">Archiver</span></button></div></div><output id="contextResult">Aucune action effectuée</output></section><!-- 11DZ_CORE_END --></section></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:overlays-feedback-context-actions */
const contextSurface=document.querySelector('#contextSurface');const contextOpen=document.querySelector('#contextOpen');const contextMenu=document.querySelector('#contextMenu');const contextItems=[...contextMenu.querySelectorAll('[role="menuitem"]')];function contextActionLabel(item){return item.querySelector('.context-action-copy').textContent;}function openContextMenu(x,y){const maximumX=Math.max(8,contextSurface.clientWidth-225);const maximumY=Math.max(8,contextSurface.clientHeight-230);contextMenu.style.left=Math.min(Math.max(8,x),maximumX)+'px';contextMenu.style.top=Math.min(Math.max(8,y),maximumY)+'px';contextMenu.hidden=false;contextOpen.setAttribute('aria-expanded','true');contextItems[0].focus();announce('Le menu d’actions contextuelles est ouvert');}function closeContextMenu(restore=false){contextMenu.hidden=true;contextOpen.setAttribute('aria-expanded','false');if(restore)contextOpen.focus();}contextOpen.addEventListener('click',event=>{event.stopPropagation();openContextMenu(contextOpen.offsetLeft,contextOpen.offsetTop+contextOpen.offsetHeight+6);});contextSurface.addEventListener('contextmenu',event=>{event.preventDefault();const rect=contextSurface.getBoundingClientRect();openContextMenu(event.clientX-rect.left,event.clientY-rect.top);});contextSurface.addEventListener('keydown',event=>{if(event.shiftKey&&event.key==='F10'){event.preventDefault();openContextMenu(24,80);}});contextMenu.addEventListener('keydown',event=>{const index=contextItems.indexOf(document.activeElement);if(event.key==='ArrowDown'||event.key==='ArrowUp'){event.preventDefault();const offset=event.key==='ArrowDown'?1:-1;contextItems[(index+offset+contextItems.length)%contextItems.length].focus();}if(event.key==='Escape'){event.preventDefault();closeContextMenu(true);}});contextItems.forEach(item=>item.addEventListener('click',()=>{const label=contextActionLabel(item);document.querySelector('#contextResult').textContent='Exécuté :'+label;announce('Exécuté '+label);closeContextMenu(true);}));document.addEventListener('click',event=>{if(!contextMenu.hidden&&!contextMenu.contains(event.target))closeContextMenu(false);});
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"overlays-feedback-context-actions","selector":"[data-core-bridge=\"overlays-feedback-context-actions\"]","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+' · Version de base '+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 workflowCompareCore=document.querySelector('#workflowCore');const workflowCompareWeight=document.querySelector('#workflowCompareWeight');const workflowCompareState={channel:'A',weight:50,frozen:false,snapshots:[]};function workflowCompareFingerprint(){const controls=[...workflowCompareCore.querySelectorAll('input,select,textarea')];const outputs=[...workflowCompareCore.querySelectorAll('output,[role="status"],strong')].map(node=>node.textContent.trim()).filter(Boolean);return {controlValues:controls.map(control=>control.type==='checkbox'||control.type==='radio'?control.checked:control.value),summary:outputs.slice(0,4).join(' / '),nodes:workflowCompareCore.querySelectorAll('*').length};}function workflowCompareDrive(){workflowCompareCore.dataset.compareChannel=workflowCompareState.channel;workflowCompareCore.style.setProperty('--compare-split',workflowCompareState.weight+'%');document.querySelector('#workflowCompareWeightValue').textContent=workflowCompareState.weight+' %';document.querySelector('#workflowCompareLive').textContent=workflowCompareState.channel+' Version · '+workflowCompareState.weight+' %'+(workflowCompareState.frozen?' · figé':'');announce('Version photo en cours de consultation '+workflowCompareState.channel+' Version');}function workflowCompareCapture(source){const bridge=workflowBridgeActivate();const snapshot={...workflowCompareFingerprint(),channel:workflowCompareState.channel,weight:workflowCompareState.weight,source,bridge:bridge.summary};workflowCompareState.snapshots.push(snapshot);const item=document.createElement('li');item.innerHTML='<b>'+snapshot.channel+'</b><span>'+(snapshot.summary||snapshot.bridge)+'</span><small>'+snapshot.weight+' % · '+snapshot.bridge+'</small>';document.querySelector('#workflowCompareShelf').prepend(item);announce('échantillonné '+snapshot.channel+' Versionner et enregistrer la version · '+bridge.summary);}document.querySelectorAll('[name="workflowChannel"]').forEach(input=>input.addEventListener('change',()=>{workflowCompareState.channel=input.value;workflowCompareDrive();}));workflowCompareWeight.addEventListener('input',()=>{workflowCompareState.weight=Number(workflowCompareWeight.value);workflowCompareDrive();});document.querySelector('#workflowCompareFreeze').addEventListener('change',event=>{workflowCompareState.frozen=event.currentTarget.checked;workflowCompareDrive();});document.querySelector('#workflowCompareCapture').addEventListener('click',()=>workflowCompareCapture('Échantillonnage du bouton'));workflowCompareCore.addEventListener('dblclick',()=>workflowCompareCapture('Double-clic sur la vue principale'));workflowCompareDrive();
workflowBridgeReset();
</script>
</body>
</html>