Skip to content

Instantly share code, notes, and snippets.

@roening
Last active March 1, 2026 23:01
Show Gist options
  • Select an option

  • Save roening/222c903922f5d0cb06d23d79d5cad06a to your computer and use it in GitHub Desktop.

Select an option

Save roening/222c903922f5d0cb06d23d79d5cad06a to your computer and use it in GitHub Desktop.
coffee-calculator-ios
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Brew Master</title>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="BrewMaster">
<link rel="apple-touch-icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>☕</text></svg>">
<style>
/* CSS Variables for easy customization */
:root {
--bg-body: #0a0a0a;
--bg-card: #1a1a1a;
--text-main: #efefef;
--text-dim: #999;
--accent-primary: #d4a373; /* Sandy Orange from your screenshot */
--accent-secondary: #2a2a2a;
--border: #333;
--radius: 16px;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
body {
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
margin: 0;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: env(safe-area-inset-top) 20px 20px 20px;
}
.container {
width: 100%;
max-width: 400px;
background: var(--bg-card);
padding: 30px 25px;
border-radius: var(--radius);
box-shadow: 0 20px 40px rgba(0,0,0,0.5);
margin-top: 40px;
}
h2 {
text-align: center;
color: var(--accent-primary);
margin: 0 0 25px 0;
font-size: 1.4rem;
letter-spacing: 0.5px;
}
.form-group { margin-bottom: 20px; }
label {
display: block;
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 8px;
color: var(--text-main);
}
select, input {
width: 100%;
padding: 14px;
border: 1px solid var(--border);
border-radius: 12px;
background: #222;
color: #fff;
font-size: 1rem;
outline: none;
appearance: none;
}
select:focus, input:focus { border-color: var(--accent-primary); }
.button-group {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 10px;
}
button {
padding: 15px;
border: none;
border-radius: 12px;
font-size: 0.95rem;
font-weight: bold;
cursor: pointer;
transition: opacity 0.2s;
}
.btn-calc { background-color: var(--accent-primary); color: #000; }
.btn-clear { background-color: var(--accent-secondary); color: var(--text-dim); border: 1px solid var(--border); }
button:active { opacity: 0.7; }
/* RESULTS SECTION */
#results { margin-top: 30px; display: none; border-top: 1px solid var(--border); padding-top: 25px; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 25px; }
.item { background: #222; padding: 12px; border-radius: 10px; text-align: center; }
.item span { display: block; font-size: 0.7rem; color: var(--text-dim); text-transform: uppercase; margin-bottom: 4px; }
.item strong { font-size: 1.1rem; color: var(--accent-primary); }
table { width: 100%; border-collapse: collapse; margin-top: 15px; }
th { text-align: left; font-size: 0.75rem; color: var(--text-dim); padding-bottom: 10px; border-bottom: 1px solid var(--border); }
td { padding: 12px 0; border-bottom: 1px solid #2a2a2a; font-size: 0.9rem; }
.val { font-weight: bold; color: var(--accent-primary); text-align: right; }
</style>
</head>
<body>
<div class="container">
<h2>Brew Master</h2>
<div class="form-group">
<label>Método de Preparo</label>
<select id="method">
<option value="v60">V60 (1:16)</option>
<option value="french">Prensa Francesa (1:13)</option>
</select>
</div>
<div class="form-group">
<label>Volume Final Desejado (ml)</label>
<input type="number" id="volume" placeholder="Ex: 300" inputmode="numeric">
</div>
<div class="button-group">
<button class="btn-calc" onclick="calculate()">Calcular</button>
<button class="btn-clear" onclick="clearAll()">Limpar</button>
</div>
<div id="results">
<div class="grid">
<div class="item"><span>Pó</span><strong id="r-coffee">-</strong></div>
<div class="item"><span>Moagem</span><strong id="r-grind">-</strong></div>
<div class="item"><span>Água</span><strong id="r-temp">-</strong></div>
<div class="item"><span>Tempo</span><strong id="r-time">-</strong></div>
</div>
<label style="margin-bottom: 15px;">Roteiro de Vertidas</label>
<table>
<thead><tr><th>Tempo</th><th style="text-align: right;">Total Acumulado</th></tr></thead>
<tbody id="r-table"></tbody>
</table>
</div>
</div>
<script>
const DATA = {
v60: { ratio: 16, grind: "16 clks", temp: "93°C", time: "4:45", steps: [
{ t: "0:00 (Bloom)", p: 0.20 }, { t: "0:30", p: 0.45 }, { t: "1:30", p: 0.75 }, { t: "2:25", p: 1.00 }
]},
french: { ratio: 13, grind: "23 clks", temp: "91°C", time: "4:00", steps: [
{ t: "0:00 (Início)", p: 0.50 }, { t: "0:30 (Fim)", p: 1.00 }
]}
};
// Load saved volume on start
window.onload = () => {
const saved = localStorage.getItem('lastVolume');
if(saved) {
document.getElementById('volume').value = saved;
calculate();
}
};
function calculate() {
const m = document.getElementById('method').value;
const v = parseFloat(document.getElementById('volume').value);
if(!v) return;
localStorage.setItem('lastVolume', v);
const conf = DATA[m];
document.getElementById('r-coffee').innerText = (v / conf.ratio).toFixed(1) + "g";
document.getElementById('r-grind').innerText = conf.grind;
document.getElementById('r-temp').innerText = conf.temp;
document.getElementById('r-time').innerText = conf.time;
const tbody = document.getElementById('r-table');
tbody.innerHTML = conf.steps.map(s => `
<tr><td>${s.t}</td><td class="val">${Math.round(v * s.p)} ml</td></tr>
`).join('');
document.getElementById('results').style.display = 'block';
}
function clearAll() {
document.getElementById('volume').value = "";
document.getElementById('results').style.display = 'none';
localStorage.removeItem('lastVolume');
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment