Composants de base et sélecteurs

Stepper numérique du coin détente de la serre

Compléter un sélecteur numérique dans un contexte de terrasse de serre : contenu réel, contrôles actionnables, retour d’état clair et disposition adaptable aux différentes tailles.

Composants de base et sélecteursSélecteur numériqueHTMLCSSJavaScript
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>Stepper numérique du coin détente de la serre</title>
  <style>
    :root { --ink:#18332f; --paper:#f4efe4; --accent:#d45f3e; --secondary:#8fc4ad; --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:30px;border:0;border-radius:42px 12px;background:color-mix(in srgb,var(--secondary) 18%,var(--paper))}.row{display:flex;flex-wrap:wrap;gap:14px;align-items:center}.stack{display:flex;flex-direction:column;gap:20px}.action{min-height:48px;padding:12px 22px;border:0;border-radius:50% 50% 18px 18px;background:color-mix(in srgb,var(--secondary) 42%,transparent);font-weight:800}.action.primary,.action.is-active{color:var(--paper);background:var(--accent)}.muted{color:color-mix(in srgb,var(--ink) 64%,transparent);line-height:1.7}.status{min-height:25px;margin-top:16px;color:var(--accent)}.metric{font-size:clamp(32px,7vw,66px);font-weight:800}.meter{height:11px;border-radius:50%;background:color-mix(in srgb,var(--ink) 10%,transparent)}.meter>i{display:block;height:100%;border-radius:inherit;background:var(--accent)}.chip{padding:8px 12px;border-radius:50%;background:var(--paper)}
    .composition-split{display:grid;grid-template-columns:150px minmax(0,1fr);gap:18px;align-items:start}.split-note{position:sticky;top:20px;display:grid;gap:14px;padding:18px 12px;border-block:3px double var(--accent);font-size:13px;line-height:1.65}.split-note b{font:900 12px/1 ui-monospace,monospace;letter-spacing:.12em}.split-note span{writing-mode:vertical-rl;min-height:120px;color:color-mix(in srgb,var(--ink) 60%,transparent)}.composition-split .demo{min-width:0}@media(max-width:900px){.composition-split{grid-template-columns:1fr}.split-note{position:static;grid-template-columns:auto 1fr}.split-note span{writing-mode:horizontal-tb;min-height:0}}
    .stepper-studio{display:grid;gap:18px;max-width:920px;margin:auto}.stepper-studio header{display:flex;justify-content:space-between;gap:18px;align-items:end}.stepper-studio h2{margin:.2em 0;font-size:clamp(38px,8vw,82px)}.stepper-board{display:grid;grid-template-columns:minmax(220px,.42fr) minmax(0,1fr);gap:16px}.stepper-stage,.stepper-breakdown{display:grid;gap:14px;padding:22px;border:1px solid var(--line);border-radius:22px;background:var(--paper)}.stepper-stage strong{font-size:clamp(54px,10vw,104px);line-height:.9}.stepper-direct{display:grid;gap:6px;font-weight:800}.stepper-direct input{width:100%;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--paper);font-size:22px;font-weight:900}.stepper-controls{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}.stepper-controls button{min-height:56px;border:1px solid var(--ink);border-radius:16px;background:var(--ink);color:var(--paper);font-size:30px}.stepper-shortcuts{display:flex;flex-wrap:wrap;gap:8px}.stepper-shortcuts button{min-width:56px;padding:9px 12px;border:1px solid var(--line);border-radius:999px;background:transparent}.stepper-shortcuts button[aria-pressed="true"]{color:var(--paper);background:var(--accent)}#stepperSlots{display:grid;gap:12px;padding:0;margin:0;list-style:none}#stepperSlots li{display:grid;grid-template-columns:auto 52px 1fr;gap:12px;align-items:center}#stepperSlots strong{font-size:30px}#stepperSlots i{display:block;height:12px;overflow:hidden;border-radius:999px;background:var(--line)}#stepperSlots b{display:block;height:100%;width:0;background:var(--accent)}@media(max-width:700px){.stepper-studio header{align-items:stretch;flex-direction:column}.stepper-board{grid-template-columns:1fr}}.workflow-session{display:grid;gap:14px}.session-command{display:grid;grid-template-columns:1fr auto minmax(170px,.45fr) auto;gap:16px;align-items:end;padding:18px;border-block:4px double var(--ink)}.session-command h2{margin:.2em 0}.session-command output{font:900 clamp(28px,5vw,54px)/1 ui-monospace,monospace}.session-command label{display:grid;grid-template-columns:1fr auto;gap:7px;font-size:12px}.session-command input{grid-column:1/-1;width:100%}#workflowSessionProgress{height:12px;overflow:hidden;border-radius:2px;background:var(--line)}#workflowSessionProgress i{display:block;width:0;height:100%;background:var(--accent)}.session-stage{min-width:0;transition:filter .25s,transform .25s}.session-stage[data-session-phase="running"]{transform:translateY(-3px)}.session-stage[data-session-phase="paused"]{filter:grayscale(.65)}.session-stage[data-session-phase="complete"]{filter:contrast(.78);opacity:.72}.session-log{display:grid;grid-template-columns:auto 1fr;gap:18px;align-items:start}.session-log ol{display:flex;flex-wrap:wrap;gap:7px;padding:0;margin:0;list-style:none}.session-log li{padding:5px 8px;border:1px solid var(--line);font-size:12px}@media(max-width:900px){.session-stage{order:-1}.session-command{grid-template-columns:1fr auto}.session-command label{grid-column:1/-1}.session-log{grid-template-columns:1fr}}
  </style>
</head>
<body data-aesthetic="magazine" data-composition="split-note">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">magazine / sélecteur numérique / ADVANCED</span><h1>Stepper numérique du coin détente de la serre</h1><p class="intro">Compléter un sélecteur numérique dans un contexte de terrasse de serre : contenu réel, contrôles actionnables, retour d’état clair et disposition adaptable aux différentes tailles.</p></div><span class="scenario">Salon de pause de la serre</span></header>
    <div class="composition-split"><aside class="split-note"><b>CASE 02</b><span>Maîtriser la structure sémantique, l’agencement adaptatif, les interactions au clavier et la synchronisation d’état d’un sélecteur numérique.</span></aside><section class="demo" aria-label="Zone interactive du sélecteur numérique du banc de serre"><div class="workflow-session"><header class="session-command"><div><span class="eyebrow">Salle de détente de serre SESSION</span><h2>Comptoir de pause en serre</h2></div><output id="workflowSessionClock">00:45</output><label>Durée du tour<input id="workflowSessionLength" type="range" min="15" max="90" step="15" value="45"><span>s</span></label><button id="workflowSessionToggle" class="action primary" type="button">Début du scénario d’utilisation</button></header><div id="workflowSessionProgress" role="progressbar" aria-label="Progression du poste de repos de la serre" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><i></i></div><section id="workflowCore" class="workflow-core session-stage" data-session-phase="ready" tabindex="-1"><!-- 11DZ_CORE_START:controls-selectors-numeric-stepper --><section class="stepper-studio"><header><div><span class="eyebrow">NUMBER STEPPER</span><h2>Table d’allocation du lounge de la serre</h2><p>Utilisez des boutons pour répartir les quantités, puis ventilez le résultat dans trois zones de traitement.</p></div><output id="stepperCapacity">Il reste 8 places</output></header><div class="stepper-board"><aside class="stepper-stage"><strong id="stepperValue">4</strong><p>quantité actuelle</p><label class="stepper-direct"><span>Saisie directe de la quantité</span><input id="stepperInput" type="number" min="1" max="12" step="1" value="4" inputmode="numeric"></label><div class="stepper-controls"><button id="stepperMinus" type="button" aria-label="Réduire d’une place">−</button><button id="stepperPlus" type="button" aria-label="Ajouter une place" data-core-bridge="controls-selectors-numeric-stepper">+</button></div><div class="stepper-shortcuts"><button type="button" data-stepper-value="2">2</button><button type="button" data-stepper-value="4">4</button><button type="button" data-stepper-value="8">8</button><button type="button" data-stepper-value="12">12</button></div></aside><section class="stepper-breakdown"><ol id="stepperSlots"><li><span>zone de préparation</span><strong id="stepperSlot0">0</strong><i><b id="stepperFill0"></b></i></li><li><span>Zone de distribution</span><strong id="stepperSlot1">0</strong><i><b id="stepperFill1"></b></i></li><li><span>Zone de confirmation</span><strong id="stepperSlot2">0</strong><i><b id="stepperFill2"></b></i></li></ol><p id="stepperSummary">Zone de préparation 2 pièces, zone de distribution 1 pièces, zone de validation 1 pièces.</p></section></div></section><!-- 11DZ_CORE_END --></section><footer class="session-log"><strong id="workflowSessionScore">0 points de contrôle interactifs</strong><ol id="workflowSessionLaps" aria-label="Journal de ronde"></ol></footer></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:controls-selectors-numeric-stepper */
const stepperValue=document.querySelector('#stepperValue');const stepperInput=document.querySelector('#stepperInput');const stepperCapacity=document.querySelector('#stepperCapacity');const stepperSummary=document.querySelector('#stepperSummary');const stepperShortcuts=[...document.querySelectorAll('[data-stepper-value]')];function splitStepper(value){const head=Math.ceil(value/2);const tail=value-head;const middle=Math.ceil(tail/2);return [head,middle,Math.max(0,value-head-middle)];}function renderStepper(next){const value=Math.min(12,Math.max(1,Number(next)||1));const slots=splitStepper(value);stepperValue.textContent=String(value);stepperInput.value=String(value);stepperCapacity.textContent='Restant '+(12-value)+' places';document.querySelector('#stepperMinus').disabled=value===1;document.querySelector('#stepperPlus').disabled=value===12;slots.forEach((count,index)=>{document.querySelector('#stepperSlot'+index).textContent=String(count);document.querySelector('#stepperFill'+index).style.width=Math.round(count/Math.max(1,value)*100)+'%';});stepperSummary.textContent='zone de préparation '+slots[0]+' pièce, zone de distribution '+slots[1]+' pièce, zone de confirmation '+slots[2]+' unité.';stepperShortcuts.forEach(button=>button.setAttribute('aria-pressed',String(Number(button.dataset.stepperValue)===value)));announce('quantité actuelle '+value);}document.querySelector('#stepperMinus').addEventListener('click',()=>renderStepper(Number(stepperInput.value)-1));document.querySelector('#stepperPlus').addEventListener('click',()=>renderStepper(Number(stepperInput.value)+1));stepperInput.addEventListener('input',()=>renderStepper(stepperInput.value));stepperShortcuts.forEach(button=>button.addEventListener('click',()=>renderStepper(button.dataset.stepperValue)));renderStepper(4);
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"controls-selectors-numeric-stepper","selector":"[data-core-bridge=\"controls-selectors-numeric-stepper\"]","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 workflowSessionCore=document.querySelector('#workflowCore');const workflowSessionToggle=document.querySelector('#workflowSessionToggle');const workflowSessionLength=document.querySelector('#workflowSessionLength');const workflowSessionState={phase:'ready',elapsed:0,limit:Number(workflowSessionLength.value),checkpoints:[],timer:null};function workflowSessionFormat(seconds){return String(Math.floor(seconds/60)).padStart(2,'0')+':'+String(seconds%60).padStart(2,'0');}function workflowSessionDrive(){const percent=Math.min(100,Math.round(workflowSessionState.elapsed/workflowSessionState.limit*100));document.querySelector('#workflowSessionClock').textContent=workflowSessionFormat(Math.max(0,workflowSessionState.limit-workflowSessionState.elapsed));const progress=document.querySelector('#workflowSessionProgress');progress.setAttribute('aria-valuenow',String(percent));progress.querySelector('i').style.width=percent+'%';workflowSessionCore.dataset.sessionPhase=workflowSessionState.phase;document.querySelector('#workflowSessionScore').textContent=workflowSessionState.checkpoints.length+' points de contrôle d’interaction';}function workflowSessionLap(label){workflowSessionState.checkpoints.push({label,at:workflowSessionState.elapsed});const item=document.createElement('li');item.textContent=label+' · '+workflowSessionFormat(workflowSessionState.elapsed);document.querySelector('#workflowSessionLaps').append(item);workflowSessionDrive();}function workflowSessionStop(complete=false){clearInterval(workflowSessionState.timer);workflowSessionState.timer=null;workflowSessionState.phase=complete?'complete':'paused';workflowSessionToggle.textContent=complete?'Recommencer':'Tour suivant';workflowSessionToggle.setAttribute('aria-pressed','false');workflowSessionDrive();announce(complete?'Tour terminée dans la serre du lounge':'La ronde est suspendue');}function workflowSessionStart(){if(workflowSessionState.phase==='complete'){workflowSessionState.elapsed=0;workflowSessionState.checkpoints=[];document.querySelector('#workflowSessionLaps').replaceChildren();}workflowSessionState.phase='running';workflowSessionToggle.textContent='Tour de pause';workflowSessionToggle.setAttribute('aria-pressed','true');const bridge=workflowBridgeActivate();workflowSessionLap(bridge.summary);clearInterval(workflowSessionState.timer);workflowSessionState.timer=setInterval(()=>{workflowSessionState.elapsed+=1;if(workflowSessionState.elapsed>=workflowSessionState.limit){workflowSessionStop(true);return;}workflowSessionDrive();},1000);workflowSessionDrive();announce('Le tour de l’espace lounge de la serre a commencé · '+bridge.summary);}workflowSessionToggle.addEventListener('click',()=>workflowSessionState.phase==='running'?workflowSessionStop():workflowSessionStart());workflowSessionLength.addEventListener('input',()=>{workflowSessionState.limit=Number(workflowSessionLength.value);workflowSessionState.elapsed=Math.min(workflowSessionState.elapsed,workflowSessionState.limit);workflowSessionDrive();});workflowSessionCore.addEventListener('keydown',event=>{if(event.key==='Enter'||event.key===' '){workflowSessionLap('Opérations au clavier');}});workflowSessionCore.addEventListener('click',event=>{if(event.target.closest('[data-core-bridge]'))workflowSessionLap('Opération principale · '+workflowBridgeSummary());});document.addEventListener('visibilitychange',()=>{if(document.hidden&&workflowSessionState.phase==='running')workflowSessionStop();});workflowSessionDrive();
workflowBridgeReset();
  </script>
</body>
</html>
Commentaires sur la pageVous avez découvert un problème ou avez une suggestion d’amélioration ?
Les fonctions de retour nécessitent l’utilisation de la version en ligne

Veuillez activer JavaScript dans la version en ligne avant de soumettre ; les fonctionnalités locales de l’outil ou du cas ne sont pas affectées.

Aller vers la version en ligne pour envoyer des commentaires