حاسبة البقشيش
مثال بالفرنسية
الشيفرة
المعاينة
<!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>
تطبيق
حاسبة البقشيش
الطلب
«حاسبة بقشيش لكيبيك: الضريبة الفدرالية وضريبة المقاطعة، ونسبة يختارها المستخدم، وتقسيم الفاتورة على عدة أشخاص.»
ما الذي يعمل
ضريبتان بنسبة 5٪ و9.975٪، مقرّبتان إلى أقرب سنت. بقشيش يُحسب قبل الضرائب أو بعدها. تقسيم الفاتورة مع التقريب إلى السنت الأعلى.