Business and Service Flows

Bike Rental Seat Selector

Complete the seat selector in a bike rental scenario: including real content, interactive controls, clear state feedback, and responsive layouts across sizes.

Business and Service FlowsSeat PickerHTMLCSSJavaScript
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>Bike Rental Seat Selector</title>
  <style>
    :root { --ink:#30231f; --paper:#f9efe5; --accent:#8b6fb0; --secondary:#d7a971; --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:26px;border:1px solid var(--line);border-radius:2px;background:var(--paper)}.stack{display:grid;gap:28px}.row{display:flex;gap:20px;flex-wrap:wrap}.action{padding:9px 2px;border:0;border-bottom:2px solid var(--line);background:transparent}.action.primary,.action.is-active{border-color:var(--accent);color:var(--accent)}.muted{font-size:14px;line-height:1.9}.status{min-height:20px;margin-top:28px;font-size:13px}.metric{font-size:clamp(40px,9vw,88px);font-weight:500;letter-spacing:-.06em}.meter{height:2px;background:var(--line)}.meter>i{display:block;height:100%;background:var(--accent)}.chip{font-size:11px;letter-spacing:.12em;text-transform:uppercase}
    .composition-index{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px}.index-mark{display:grid;align-content:space-between;width:76px;padding:16px 10px;border:1px solid var(--line);color:var(--accent);background:color-mix(in srgb,var(--paper) 82%,transparent);text-align:center}.index-mark strong{font:900 34px/1 ui-monospace,monospace}.index-mark span{writing-mode:vertical-rl;font:800 10px/1.2 ui-monospace,monospace;letter-spacing:.18em}.composition-index .demo{grid-column:1;grid-row:1}.composition-index::before{content:"";position:absolute;left:-12px;top:38px;width:5px;height:45%;background:linear-gradient(var(--accent),var(--secondary))}@media(max-width:700px){.composition-index{grid-template-columns:1fr}.index-mark{grid-row:1;width:auto;grid-template-columns:auto 1fr}.index-mark span{writing-mode:horizontal-tb}.composition-index .demo{grid-row:2}}
    .seat-selection-demo{display:grid;grid-template-columns:1fr minmax(220px,.5fr);gap:18px}.seat-selection-demo>section{display:grid;gap:14px}.seat-selection-demo h2{margin:.3em 0}.seat-screen{padding:10px;border-radius:50% 50% 10px 10px;text-align:center;background:var(--ink);color:var(--paper)}.seat-map{display:grid;grid-template-columns:repeat(5,1fr);gap:9px}.seat-map button{aspect-ratio:1;border:1px solid var(--line);border-radius:10px;background:var(--paper)}.seat-map button[aria-pressed="true"]{color:var(--paper);background:var(--accent)}.seat-map button:disabled{opacity:.3;background:var(--line)}.seat-legend{display:flex;gap:14px;justify-content:center}.seat-legend i{display:inline-block;width:12px;height:12px;margin-right:5px;border:1px solid var(--line);background:var(--paper)}.seat-legend span:nth-child(2) i{background:var(--accent)}.seat-legend span:nth-child(3) i{background:var(--line)}.seat-selection-demo aside{display:grid;align-content:center;gap:14px}.seat-selection-demo aside output{font-size:26px;font-weight:900}@media(max-width:650px){.seat-selection-demo{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="field-index">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">magazine / Seat Selector / ADVANCED</span><h1>Bike Rental Seat Selector</h1><p class="intro">Complete the seat selector in a bike rental scenario: including real content, interactive controls, clear state feedback, and responsive layouts across sizes.</p></div><span class="scenario">Bike rental</span></header>
    <div class="composition-index"><section class="demo" aria-label="Bike Rental Seat Selector Interactive Area"><div class="workflow-session"><header class="session-command"><div><span class="eyebrow">Bike Rental SESSION</span><h2>Rental time slot timer</h2></div><output id="workflowSessionClock">00:45</output><label>Round Duration<input id="workflowSessionLength" type="range" min="15" max="90" step="15" value="45"><span>sec</span></label><button id="workflowSessionToggle" class="action primary" type="button">Start rental</button></header><div id="workflowSessionProgress" role="progressbar" aria-label="Rental period progress" 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:commerce-service-flows-seat-selection --><div class="seat-selection-demo"><section><span class="eyebrow">SEAT MAP</span><h2>Bike rental seat</h2><div class="seat-screen">Front / Service Desk</div><div id="seatMap" class="seat-map" role="grid" aria-label="Seating chart"><button type="button" role="gridcell" data-seat="1" aria-pressed="false" aria-label="Seat 1 available" data-core-bridge="commerce-service-flows-seat-selection">1</button><button type="button" role="gridcell" data-seat="2" aria-pressed="false" aria-label="Seat 2, optional">2</button><button type="button" role="gridcell" data-seat="3" aria-pressed="false" disabled aria-label="Seat 3, occupied">3</button><button type="button" role="gridcell" data-seat="4" aria-pressed="false" aria-label="Seat 4, optional">4</button><button type="button" role="gridcell" data-seat="5" aria-pressed="false" aria-label="Seat 5, optional">5</button><button type="button" role="gridcell" data-seat="6" aria-pressed="false" aria-label="Seat 6, optional">6</button><button type="button" role="gridcell" data-seat="7" aria-pressed="false" aria-label="Seat 7, optional">7</button><button type="button" role="gridcell" data-seat="8" aria-pressed="false" disabled aria-label="Seat 8, occupied">8</button><button type="button" role="gridcell" data-seat="9" aria-pressed="false" aria-label="Seat 9, optional">9</button><button type="button" role="gridcell" data-seat="10" aria-pressed="false" aria-label="Seat 10 available">10</button><button type="button" role="gridcell" data-seat="11" aria-pressed="false" aria-label="Seat 11, selectable">11</button><button type="button" role="gridcell" data-seat="12" aria-pressed="false" aria-label="Seat 12, optional">12</button><button type="button" role="gridcell" data-seat="13" aria-pressed="false" disabled aria-label="Seat 13, occupied">13</button><button type="button" role="gridcell" data-seat="14" aria-pressed="false" aria-label="Seats 14 optional">14</button><button type="button" role="gridcell" data-seat="15" aria-pressed="false" aria-label="Seat 15, optional">15</button><button type="button" role="gridcell" data-seat="16" aria-pressed="false" aria-label="Seat 16, optional">16</button><button type="button" role="gridcell" data-seat="17" aria-pressed="false" aria-label="Seat 17, available">17</button><button type="button" role="gridcell" data-seat="18" aria-pressed="false" disabled aria-label="Seat 18, occupied">18</button><button type="button" role="gridcell" data-seat="19" aria-pressed="false" aria-label="Seat 19, optional">19</button><button type="button" role="gridcell" data-seat="20" aria-pressed="false" aria-label="Seat 20, optional">20</button></div><div class="seat-legend"><span><i></i>Optional</span><span><i></i>Selected</span><span><i></i>Occupied</span></div></section><aside class="panel"><span>Selected Seat</span><strong id="seatCount" class="metric">0</strong><p id="seatNames">Not selected yet</p><output id="seatTotal">¥0</output><button id="confirmSeats" class="action primary" type="button">Confirm seat</button></aside></div><!-- 11DZ_CORE_END --></section><footer class="session-log"><strong id="workflowSessionScore">0 interaction checkpoints</strong><ol id="workflowSessionLaps" aria-label="Round Log"></ol></footer></div><p id="status" class="status" role="status" aria-live="polite"></p></section><aside class="index-mark" aria-hidden="true"><strong>05</strong><span>Bike Rental FIELD INDEX</span></aside></div>
  </main>
  <script>
    'use strict';
    const statusOutput = document.querySelector('#status');
    const announce = message => { statusOutput.textContent = message; };
    /* 11DZ_CORE_SCRIPT_START:commerce-service-flows-seat-selection */
const seatButtons=[...document.querySelectorAll('.seat-map button:not(:disabled)')];function renderSeats(){const selected=seatButtons.filter(button=>button.getAttribute('aria-pressed')==='true');document.querySelector('#seatCount').textContent=selected.length;document.querySelector('#seatNames').textContent=selected.length?'Seat '+selected.map(button=>button.dataset.seat).join('、'):'Not selected yet';document.querySelector('#seatTotal').textContent='¥'+selected.length*38;announce('Selected '+selected.length+' seats');}seatButtons.forEach(button=>button.addEventListener('click',()=>{button.setAttribute('aria-pressed',String(button.getAttribute('aria-pressed')!=='true'));renderSeats();}));document.querySelector('#confirmSeats').addEventListener('click',()=>announce(seatButtons.some(button=>button.getAttribute('aria-pressed')==='true')?'Seat confirmed locally':'Please select a seat first'));renderSeats();
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"commerce-service-flows-seat-selection","selector":"[data-core-bridge=\"commerce-service-flows-seat-selection\"]","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 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+' interactive checkpoints';}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?'Restart':'Continue round';workflowSessionToggle.setAttribute('aria-pressed','false');workflowSessionDrive();announce(complete?'Rental period round completed':'Round paused');}function workflowSessionStart(){if(workflowSessionState.phase==='complete'){workflowSessionState.elapsed=0;workflowSessionState.checkpoints=[];document.querySelector('#workflowSessionLaps').replaceChildren();}workflowSessionState.phase='running';workflowSessionToggle.textContent='Pause round';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('Rental period round has started · '+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('Keyboard Interaction');}});workflowSessionCore.addEventListener('click',event=>{if(event.target.closest('[data-core-bridge]'))workflowSessionLap('Core action · '+workflowBridgeSummary());});document.addEventListener('visibilitychange',()=>{if(document.hidden&&workflowSessionState.phase==='running')workflowSessionStop();});workflowSessionDrive();
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