:root{
/* Tema claro */
–bg:#f5f7fb; /* fundo página */
–tela:#ffffff; /* cartões */
–muted:#475569; /* texto secundário */
–text:#0b1222; /* texto principal */
–pri:#16a34a; /* verde-600 */
–pri2:#22c55e; /* verde-500 */
–err:#dc2626; /* vermelho-600 */
–shadow: 0 10px 30px rgba(2,6,23,.06);
–radius: 16px;
–input:#ffffff;
–border:#e5e7eb;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0; padding:24px;
background:linear-gradient(180deg,#f8fafc 0%, #eef2ff 100%);
color:var(–text);
font:500 16px/1.5 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,”Helvetica Neue”,Arial;
}
.wrap{max-width:980px; margin:0 auto}
h1{font-weight:800; letter-spacing:.3px; margin:0 0 4px; font-size:clamp(20px,3.5vw,34px)}
p.sub{margin:0 0 24px; color:var(–muted)}
.grid{display:grid; gap:16px; grid-template-columns:repeat(12,1fr)}
.tela{
grid-column:1/-1; background:var(–tela);
border:1px solid var(–border);
border-radius:var(–radius); padding:18px;
box-shadow:var(–shadow);
}
.row{display:grid; gap:12px; grid-template-columns:repeat(12,1fr)}
.col-6{grid-column:span 6}
.col-4{grid-column:span 4}
.col-3{grid-column:span 3}
.col-12{grid-column:span 12}
label{display:block; font-size:14px; color:var(–muted); margin:2px 0 6px}
input,select{
width:100%; background:var(–input); color:var(–text);
border:1px solid var(–border); border-radius:12px;
padding:12px 14px; outline:none; transition:.2s border,.2s transform,.2s box-shadow;
}
input:focus,select:focus{
border-color:var(–pri);
box-shadow:0 0 0 4px rgba(34,197,94,.12);
transform: translateY(-1px);
}
.hint{font-size:12px; color:var(–muted); margin-top:6px}
.actions{display:flex; gap:12px; margin-top:8px; flex-wrap:wrap}
/* Âncoras como botões */
.btn{
display:inline-block; text-decoration:none; user-select:none !important;
border:1px solid transparent; padding:12px 16px; border-radius:12px; cursor:pointer !important;
font-weight:800; letter-spacing:.2px !important;
background:var(–pri); color:#ffffff !important; transition:.2s transform,.2s background,.2s color,.2s border-color !important;
}
.btn:hover{background:#ffffff !important; color:var(–pri) !important; border-color:var(–pri) !important; transform:translateY(-1px) !important}
.btn:focus-visible{outline:2px solid var(–pri) !important; outline-offset:2px !important}
.btn.ghost{
background:transparent !important; color:var(–text) !important; border-color:var(–border) !important; font-weight:700 !important;
}
.btn.ghost:hover{background:#ffffff !important; border-color:var(–pri) !important; color:var(–pri) !important}
.error{color:var(–err); font-size:13px; margin-top:8px; display:none}
table{width:100%; border-collapse:collapse; margin-top:8px; font-size:15px; background:#fff}
th,td{padding:10px 8px; text-align:right; border-bottom:1px dashed #e5e7eb}
th:first-child, td:first-child{text-align:left}
tfoot td{font-weight:800; border-top:1px solid #e5e7eb}
.badge{
display:inline-block; padding:4px 8px; border-radius:999px; font-size:12px;
color:#064e3b; background:rgba(34,197,94,.25); margin-left:6px;
}
@media (max-width:760px){
.col-6,.col-4,.col-3{grid-column:span 12}
.actions{flex-direction:column}
.btn{width:100%; text-align:center}
}
.small{font-size:12px; color:var(–muted); margin-top:8px}
(function(){
const $ = (id)=>document.getElementById(id);
function parseMoney(str, locale){
if(!str) return NaN;
str = String(str).trim();
// Normaliza conforme locale selecionado
if(locale === ‘pt-BR’){
// remove pontos de milhar e troca vírgula por ponto
str = str.replace(/\./g,”).replace(‘,’, ‘.’);
}
// en-US já usa ponto como decimal
const val = Number(str);
return Number.isFinite(val) ? val : NaN;
}
function fmt(val, locale){
return new Intl.NumberFormat(locale,{style:’currency’,currency:’BRL’}).format(val || 0);
}
function fmtNum(val, locale){
return new Intl.NumberFormat(locale,{maximumFractionDigits:2}).format(val || 0);
}
function calcular(){
const locale = “pt-BR”;
const salario = parseMoney($(‘salario’).value, locale);
const jornada = Number($(‘jornada’).value);
const adNoturnoPct = Number($(‘adNoturno’).value || 0);
const h50 = Number($(‘h50’).value || 0);
const h100 = Number($(‘h100’).value || 0);
const hNoturnas = Number($(‘hNoturnas’).value || 0);
// Validacoes
let ok = true;
if(!(salario > 0) || !(jornada >= 10 && jornada (h50 + h100)) ok = false;
$(‘erro’).style.display = ok ? ‘none’ : ‘block’;
if(!ok){
$(‘tabela’).style.display = ‘none’;
$(‘resumo’).textContent = ‘Corrija os campos destacados e tente novamente.’;
return;
}
const horasMes = jornada * 5; // padrão brasileiro (44h → 220h/mês)
const valorHora = salario / horasMes;
const valor50 = valorHora * 1.5;
const valor100 = valorHora * 2.0;
// Subtotais de hora extra “cheia” (sem adicional noturno)
const sub50 = h50 * valor50;
const sub100 = h100 * valor100;
// Adicional noturno: aplicado sobre as horas extras noturnas informadas
const adNoturnoDec = adNoturnoPct > 0 ? (adNoturnoPct/100) : 0;
// Assumimos, por simplicidade, que a composição noturna é proporcional:
const prop50 = (h50 > 0) ? Math.min(hNoturnas, h50) : 0;
const prop100 = Math.max(0, hNoturnas – prop50);
const adNoturno50 = prop50 * (valor50 * adNoturnoDec);
const adNoturno100 = prop100 * (valor100 * adNoturnoDec);
const total = sub50 + sub100 + adNoturno50 + adNoturno100;
// Monta tabela
const rows = [];
rows.push({
desc:’Horas extras 50%’,
horas:h50,
vhora: valorHora,
adicional:’50%’,
subtotal: sub50
});
rows.push({
desc:’Horas extras 100%’,
horas:h100,
vhora: valorHora,
adicional:’100%’,
subtotal: sub100
});
if(adNoturnoDec > 0 && hNoturnas > 0){
rows.push({
desc:`Adicional noturno s/ 50% (${adNoturnoPct}%)`,
horas:prop50,
vhora: valor50,
adicional:`+${adNoturnoPct}%`,
subtotal: adNoturno50
});
rows.push({
desc:`Adicional noturno s/ 100% (${adNoturnoPct}%)`,
horas:prop100,
vhora: valor100,
adicional:`+${adNoturnoPct}%`,
subtotal: adNoturno100
});
}
// Render
const tbody = $(‘tbody’);
tbody.innerHTML = ”;
rows.forEach(r=>{
const tr = document.createElement(‘tr’);
tr.innerHTML = `
${r.desc}
${fmtNum(r.horas, locale)}
${fmt(r.vhora, locale)}
${r.adicional}
${fmt(r.subtotal, locale)}
`;
tbody.appendChild(tr);
});
$(‘total’).textContent = fmt(total, locale);
$(‘tabela’).style.display = ‘table’;
// Resumo
const txt = [
`Hora base: ${fmt(valorHora, locale)} (Salário ÷ ${fmtNum(horasMes, locale)} h/mês)`,
`Horas 50%: ${fmt(sub50, locale)}`,
`Horas 100%: ${fmt(sub100, locale)}`
];
if(adNoturnoDec > 0 && hNoturnas > 0){
txt.push(`Adic. noturno: ${fmt(adNoturno50 + adNoturno100, locale)}`);
}
txt.push(`Total de horas extras: ${fmt(total, locale)}`);
$(‘resumo’).innerHTML = txt.join(‘ · ‘);
}
function limpar(){
[‘salario’,’h50′,’h100′,’hNoturnas’].forEach(id=>$(id).value=”);
$(‘jornada’).value = 44;
$(‘adNoturno’).value = 0;
$(‘formato’).value = ‘pt-BR’;
$(‘erro’).style.display=’none’;
$(‘tabela’).style.display=’none’;
$(‘resumo’).textContent = ‘Informe os dados e clique em Calcular.’;
}
// Eventos (com âncoras)
$(‘btnCalc’).addEventListener(‘click’, (e)=>{ e.preventDefault(); calcular(); });
$(‘btnLimpar’).addEventListener(‘click’, (e)=>{ e.preventDefault(); limpar(); });
// UX: Enter para calcular
document.addEventListener(‘keydown’, (e)=>{
if(e.key === ‘Enter’) calcular();
});
})();
Use o salário mensal e a sua jornada semanal. A calculadora aplica a base horária padrão brasileira (jornada × 5; ex.: 44h → 220h/mês).
Resultado
Informe os dados e clique em Calcular.
| Descrição | Horas | Valor hora | Adicional | Subtotal |
|---|---|---|---|---|
| Total de horas extras | ||||
Como o cálculo funciona
- Hora base = Salário mensal ÷ (Jornada semanal × 5). Exemplos: 44h → 220h/mês; 40h → 200h/mês.
- 50%: valor hora × 1,5. 100%: valor hora × 2,0.
- Adicional noturno (opcional): aplica-se sobre as horas extras noturnas indicadas.
- Esta ferramenta foca no cálculo direto de horas extras. DSR e hora noturna reduzida não estão incluídos.