Calculateur de pourboire
Exemple en français
Code
Aperçu
<!doctype html>
<html lang="fr-CA">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Calculateur de pourboire</title>
<style>
:root{--ink:#0f1f1a;--muted:#5b6b65;--teal:#0f766e;--teal-soft:#e6f4f1;--line:#dfe7e4;--bg:#f6f8f7}
*{box-sizing:border-box}
body{margin:0;min-height:100vh;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif;color:var(--ink);background:var(--bg);display:grid;place-items:center;padding:28px 16px}
.card{width:100%;max-width:820px;background:#fff;border:1px solid var(--line);border-radius:24px;display:grid;grid-template-columns:1.1fr 1fr;overflow:hidden;box-shadow:0 30px 60px -40px rgba(15,31,26,.35)}
.form{padding:28px;display:grid;gap:20px;align-content:start}
h1{margin:0;font-size:22px;letter-spacing:-.02em}
.hint{margin:4px 0 0;color:var(--muted);font-size:14px}
label,.label{display:block;font-size:13px;font-weight:600;color:var(--muted);margin-bottom:8px;text-transform:uppercase;letter-spacing:.06em}
.money{position:relative}
.money input{width:100%;font:600 28px/1 system-ui,sans-serif;padding:14px 44px 14px 16px;border:1px solid var(--line);border-radius:14px;background:var(--bg);color:var(--ink);font-variant-numeric:tabular-nums}
.money span{position:absolute;right:16px;top:50%;transform:translateY(-50%);font-size:22px;color:var(--muted)}
input:focus,button:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.chips{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.chips button{border:1px solid var(--line);background:#fff;border-radius:12px;padding:11px 0;font:600 15px system-ui,sans-serif;color:var(--ink);cursor:pointer}
.chips button[aria-pressed="true"]{background:var(--teal);border-color:var(--teal);color:#fff}
.custom{display:none;margin-top:8px}
.custom.on{display:flex;align-items:center;gap:8px}
.custom input{width:90px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;font:600 16px system-ui,sans-serif}
.switch{display:flex;gap:6px;background:var(--bg);border:1px solid var(--line);border-radius:12px;padding:4px}
.switch button{flex:1;border:0;background:none;border-radius:9px;padding:9px 6px;font:600 14px system-ui,sans-serif;color:var(--muted);cursor:pointer}
.switch button[aria-pressed="true"]{background:#fff;color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.08)}
.people{display:flex;align-items:center;gap:14px}
.people button{width:40px;height:40px;border-radius:12px;border:1px solid var(--line);background:#fff;font:600 20px/1 system-ui,sans-serif;cursor:pointer;color:var(--ink)}
.people output{font:700 22px system-ui,sans-serif;min-width:2ch;text-align:center;font-variant-numeric:tabular-nums}
.result{background:var(--ink);color:#e8f1ee;padding:28px;display:flex;flex-direction:column;gap:6px}
.line{display:flex;justify-content:space-between;font-size:15px;color:#a9bbb4;padding:7px 0;border-bottom:1px solid rgba(255,255,255,.08);font-variant-numeric:tabular-nums}
.line b{color:#e8f1ee;font-weight:600}
.line.total{color:#fff;font-size:17px;border:0;padding-top:12px}
.line.total b{font-size:20px}
.each{margin-top:auto;padding-top:22px}
.each small{display:block;color:#7fd1c4;font-size:13px;text-transform:uppercase;letter-spacing:.08em;font-weight:600}
.each strong{display:block;font-size:46px;letter-spacing:-.03em;line-height:1.1;font-variant-numeric:tabular-nums}
.each p{margin:6px 0 0;font-size:13px;color:#a9bbb4}
@media (max-width:680px){.card{grid-template-columns:1fr}.each strong{font-size:38px}}
</style>
</head>
<body>
<main class="card">
<div class="form">
<div>
<h1>Calculateur de pourboire</h1>
<p class="hint">Avec la TPS (5 %) et la TVQ (9,975 %) du Québec.</p>
</div>
<div>
<label for="amount">Montant avant taxes</label>
<div class="money"><input id="amount" type="text" inputmode="decimal" value="84,50" autocomplete="off"><span>$</span></div>
</div>
<div>
<span class="label" id="tip-label">Pourboire</span>
<div class="chips" role="group" aria-labelledby="tip-label">
<button type="button" data-tip="15" aria-pressed="false">15 %</button>
<button type="button" data-tip="18" aria-pressed="true">18 %</button>
<button type="button" data-tip="20" aria-pressed="false">20 %</button>
<button type="button" data-tip="autre" aria-pressed="false">Autre</button>
</div>
<div class="custom" id="custom">
<input id="customTip" type="number" min="0" max="100" step="0.5" value="22" aria-label="Pourboire personnalisé en pourcentage"> <span>%</span>
</div>
</div>
<div>
<span class="label" id="base-label">Pourboire calculé sur</span>
<div class="switch" role="group" aria-labelledby="base-label">
<button type="button" data-base="avant" aria-pressed="true">Avant taxes</button>
<button type="button" data-base="apres" aria-pressed="false">Après taxes</button>
</div>
</div>
<div>
<span class="label">Nombre de personnes</span>
<div class="people">
<button type="button" id="less" aria-label="Une personne de moins">−</button>
<output id="people" aria-live="polite">2</output>
<button type="button" id="more" aria-label="Une personne de plus">+</button>
</div>
</div>
</div>
<div class="result" aria-live="polite">
<div class="line"><span>Sous-total</span><b id="sub">0,00 $</b></div>
<div class="line"><span>TPS (5 %)</span><b id="tps">0,00 $</b></div>
<div class="line"><span>TVQ (9,975 %)</span><b id="tvq">0,00 $</b></div>
<div class="line"><span>Pourboire (<span id="pct">18</span> %)</span><b id="tip">0,00 $</b></div>
<div class="line total"><span>Total</span><b id="total">0,00 $</b></div>
<div class="each">
<small>Par personne</small>
<strong id="each">0,00 $</strong>
<p id="split">Partagé entre 2 personnes.</p>
</div>
</div>
</main>
<script>
const money = new Intl.NumberFormat('fr-CA', { style: 'currency', currency: 'CAD' });
const cents = n => Math.round(n * 100) / 100;
const state = { tip: 18, custom: false, base: 'avant', people: 2 };
const $ = id => document.getElementById(id);
function amount() {
const n = parseFloat($('amount').value.replace(/\s/g, '').replace(',', '.'));
return Number.isFinite(n) && n > 0 ? n : 0;
}
function update() {
const sub = cents(amount());
const tps = cents(sub * 0.05);
const tvq = cents(sub * 0.09975);
const pct = state.custom ? Math.max(0, parseFloat($('customTip').value) || 0) : state.tip;
const base = state.base === 'avant' ? sub : sub + tps + tvq;
const tip = cents(base * pct / 100);
const total = cents(sub + tps + tvq + tip);
$('sub').textContent = money.format(sub);
$('tps').textContent = money.format(tps);
$('tvq').textContent = money.format(tvq);
$('pct').textContent = String(pct).replace('.', ',');
$('tip').textContent = money.format(tip);
$('total').textContent = money.format(total);
$('each').textContent = money.format(Math.ceil(total / state.people * 100) / 100);
$('people').textContent = state.people;
$('split').textContent = state.people === 1
? 'Une seule personne paie.'
: 'Partagé entre ' + state.people + ' personnes, arrondi au cent supérieur.';
}
document.querySelectorAll('[data-tip]').forEach(b => b.addEventListener('click', () => {
document.querySelectorAll('[data-tip]').forEach(x => x.setAttribute('aria-pressed', String(x === b)));
state.custom = b.dataset.tip === 'autre';
if (!state.custom) state.tip = Number(b.dataset.tip);
$('custom').classList.toggle('on', state.custom);
if (state.custom) $('customTip').focus();
update();
}));
document.querySelectorAll('[data-base]').forEach(b => b.addEventListener('click', () => {
document.querySelectorAll('[data-base]').forEach(x => x.setAttribute('aria-pressed', String(x === b)));
state.base = b.dataset.base;
update();
}));
$('less').addEventListener('click', () => { state.people = Math.max(1, state.people - 1); update(); });
$('more').addEventListener('click', () => { state.people = Math.min(20, state.people + 1); update(); });
$('amount').addEventListener('input', update);
$('customTip').addEventListener('input', update);
update();
</script>
</body>
</html>
Application
Calculateur de pourboire
La demande
« Un calculateur de pourboire pour le Québec : TPS, TVQ, pourcentage au choix et partage entre plusieurs personnes. »
Ce qui fonctionne
TPS de 5 % et TVQ de 9,975 %, arrondies au cent. Pourboire calculé avant ou après taxes. Partage de la facture, arrondi au cent supérieur.