Business and Service Flows

Flower Shop Invoice Generator

Complete the invoice generator in a flower-shop scenario: real content, actionable controls, clear state feedback, and responsive layouts across sizes.

Business and Service FlowsBill GeneratorHTMLCSSJavaScript
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>Flower Shop Invoice Generator</title>
  <style>
    :root { --ink:#111111; --paper:#f8f8f4; --accent:#111111; --secondary:#8d8d88; --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:Helvetica,"PingFang SC",sans-serif; background:repeating-linear-gradient(0deg,transparent 0 31px,color-mix(in srgb,var(--ink) 7%,transparent) 32px),var(--paper); }
    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(1120px,calc(100% - 30px));margin:auto;padding:48px 0 80px}.mast{display:grid;grid-template-columns:1fr auto;border-top:10px solid var(--ink);padding-top:20px;gap:20px;align-items:end;margin-bottom:26px}.mast h1{font-family:Helvetica,"PingFang SC",sans-serif;font-weight:900;font-size:clamp(44px,10vw,106px);letter-spacing:-.075em;margin:0;line-height:.82}.eyebrow{display:inline-block;margin-bottom:12px;padding:5px 8px;color:var(--paper);background:var(--ink);font:900 10px/1 ui-monospace,monospace;letter-spacing:.14em}.intro{max-width:700px;line-height:1.55}.scenario{padding:9px 12px;border:3px solid var(--ink);font-weight:900}.demo{border:3px solid var(--ink);border-radius:0;box-shadow:12px 12px 0 var(--ink);position:relative;min-height:410px;overflow:hidden;padding:clamp(22px,4vw,46px);background:var(--paper)}@media(max-width:700px){.page{width:calc(100% - 18px);padding:20px 0}.mast{grid-template-columns:1fr}.demo{padding:15px}}
    .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}
    .invoice-demo{display:grid;grid-template-columns:1fr minmax(280px,.65fr);gap:18px}.invoice-demo>section{display:grid;gap:16px}.invoice-demo h2{margin:.3em 0}.invoice-rows{display:grid;gap:8px}.invoice-rows label{display:grid;grid-template-columns:1fr 80px 100px 90px;gap:8px;align-items:center}.invoice-rows input{min-width:0;padding:9px;border:1px solid var(--line);border-radius:8px;background:var(--paper)}.invoice-rows output{text-align:right;font-weight:900}.invoice-tax{display:grid;grid-template-columns:auto 1fr auto;gap:12px}.invoice-paper{display:grid;align-content:start;gap:14px;padding:28px;border:1px solid var(--ink);background:#fffdf5;color:#24211b;box-shadow:10px 10px 0 color-mix(in srgb,var(--ink) 14%,transparent)}.invoice-paper dl{display:grid;gap:7px}.invoice-paper dl div,.invoice-paper>div,.invoice-paper footer{display:flex;justify-content:space-between;gap:14px}.invoice-paper dd{margin:0}.invoice-paper footer{padding-top:14px;border-top:2px solid currentColor;font-size:22px}.invoice-paper button{padding:10px;border:1px solid currentColor;background:transparent}@media(max-width:960px){.invoice-demo{grid-template-columns:1fr}}@media(max-width:720px){.invoice-rows label{grid-template-columns:1fr 65px 80px}.invoice-rows output{grid-column:1/-1}}.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="monochrome" data-composition="studio">
  <main class="page">
    <header class="mast"><div><span class="eyebrow">monochrome / invoice generator / INTERMEDIATE</span><h1>Flower Shop Invoice Generator</h1><p class="intro">Complete the invoice generator in a flower-shop scenario: real content, actionable controls, clear state feedback, and responsive layouts across sizes.</p></div><span class="scenario">Flower shop</span></header>
    <div class="composition-studio"><div class="studio-ruler" aria-hidden="true"></div><section class="demo" aria-label="Flower shop invoice generator interaction area"><div class="workflow-dispatch"><aside class="dispatch-console"><span class="eyebrow">Flower Shop DISPATCH</span><h2>Bouquet order dispatch console</h2><form id="workflowDispatchForm"><label>Task Type<select id="workflowDispatchKind"><option>Daily Bouquet</option><option>Seasonal bouquet</option><option>Custom Bouquet</option></select></label><label>Floral quantity<input id="workflowDispatchAmount" type="number" min="1" max="9999" value="30"><span>Branch</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">Accept orders</button></form><label>Current task<select id="workflowDispatchActive" aria-label="Switch current task"><option value="1">Daily Bouquet</option><option value="2">Seasonal bouquet</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:commerce-service-flows-invoice-maker --><div class="invoice-demo"><section class="panel"><header><span class="eyebrow">INVOICE MAKER</span><h2>Florist Invoice</h2></header><div id="invoiceRows" class="invoice-rows"><label><span>Flower Shop Collaboration</span><input data-invoice-quantity type="number" min="0" value="1" aria-label="Flower shop collaboration count"><input data-invoice-price type="number" min="0" value="45" aria-label="Flower Shop Collaboration Unit Price"><output></output></label><label><span>Flower Shop Progress</span><input data-invoice-quantity type="number" min="0" value="2" aria-label="Flower Shop Progress Quantity"><input data-invoice-price type="number" min="0" value="75" aria-label="Florist progress unit price"><output></output></label><label><span>Flower shop reminder</span><input data-invoice-quantity type="number" min="0" value="3" aria-label="Flower Shop Reminder Count"><input data-invoice-price type="number" min="0" value="105" aria-label="Florist Unit Price Reminder"><output></output></label></div><label class="invoice-tax">Tax rate <input id="invoiceTax" type="range" min="0" max="20" value="6" data-core-bridge="commerce-service-flows-invoice-maker"><output id="invoiceTaxLabel">6%</output></label></section><aside class="invoice-paper"><span>INVOICE / LOCAL</span><h3>Flower shop</h3><dl id="invoiceSummary"></dl><div><span>Tax Amount</span><strong id="invoiceTaxValue"></strong></div><footer><span>Total Amount Due</span><strong id="invoiceTotal"></strong></footer><button id="invoicePrint" type="button">Print Preview</button></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:commerce-service-flows-invoice-maker */
const invoiceRows=[...document.querySelectorAll('.invoice-rows label')];function renderInvoice(){let subtotal=0;const summary=[];invoiceRows.forEach(row=>{const quantity=Number(row.querySelector('[data-invoice-quantity]').value)||0;const price=Number(row.querySelector('[data-invoice-price]').value)||0;const line=quantity*price;subtotal+=line;row.querySelector('output').textContent='¥'+line;summary.push([row.querySelector('span').textContent,line]);});const taxRate=Number(document.querySelector('#invoiceTax').value);const tax=Math.round(subtotal*taxRate)/100;document.querySelector('#invoiceTaxLabel').textContent=taxRate+'%';document.querySelector('#invoiceTaxValue').textContent='¥'+tax.toFixed(2);document.querySelector('#invoiceTotal').textContent='¥'+(subtotal+tax).toFixed(2);document.querySelector('#invoiceSummary').innerHTML=summary.map(([name,value])=>'<div><dt>'+name+'</dt><dd>¥'+value+'</dd></div>').join('');announce('Total bill '+(subtotal+tax).toFixed(2)+' yuan');}document.querySelectorAll('.invoice-demo input').forEach(input=>input.addEventListener('input',renderInvoice));document.querySelector('#invoicePrint').addEventListener('click',()=>announce('Print preview is ready; this demo will not call the system print dialog'));renderInvoice();
/* 11DZ_CORE_SCRIPT_END */
const workflowBridgeSpec={"name":"commerce-service-flows-invoice-maker","selector":"[data-core-bridge=\"commerce-service-flows-invoice-maker\"]","mode":"range","key":"ArrowRight"};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("input",workflowBridgeScheduleCommit);workflowBridgeControl.addEventListener('change',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();const step=Number(workflowBridgeControl.step)||1;const min=workflowBridgeControl.min===''?-Infinity:Number(workflowBridgeControl.min);const max=workflowBridgeControl.max===''?Infinity:Number(workflowBridgeControl.max);let next=Math.min(max,Math.max(min,(Number(workflowBridgeControl.value)||0)+step));if(next===Number(workflowBridgeControl.value))next=Math.max(min,Number(workflowBridgeControl.value)-step);workflowBridgeControl.value=String(next);workflowBridgeControl.dispatchEvent(new Event('input',{bubbles:true}));workflowBridgeControl.dispatchEvent(new Event('change',{bubbles:true}));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":"Daily Bouquet","amount":12,"priority":"normal","touches":0}],["2",{"label":"Seasonal bouquet","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+' Branch · '+bridge.summary;announce('Current flower order task:'+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+' Branch · '+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