Ladda upp PDF:en till valfri tjänst nedan. Båda parter signerar med sitt BankID — helt gratis eller till låg kostnad.
Ladda upp PDF:en till valfri tjänst nedan. Båda parter signerar med sitt BankID — helt gratis eller till låg kostnad.
Hyresvärden upplåter till hyresgästen följande objekt för bostadsändamål (om annat ej anges):
Hyresavtalet gäller ${kontraktstyp} och löper från ${dateFrom} till ${tillText}.
Uppsägningstid är ${v('uppsagningstid')}. Uppsägning ska ske skriftligen.
Hyresgästen ska betala en månadshyra om ${v('hyra')} kr.
Hyran ska vara hyresvärden tillhanda senast ${v('bet_dag')} varje månad.
Betalning sker till: ${bgText}.
Depositionsbelopp: ${depText}. Depositionen återbetalas inom 30 dagar efter hyrestidens slut, förutsatt att hyresrätten är i godtagbart skick och inga obetalda hyror föreligger.
Hyresgästen förbinder sig att väl vårda hyresobjektet och dess inventarier. Skador som uppstår till följd av hyresgästens vållande ska ersättas av hyresgästen. ${v('husdjur')}. ${v('rokning')}. ${v('andrahand')}.
${v('ovrigt') !== '—' ? v('ovrigt') : 'Inga övriga villkor har angivits.'}
I övrigt gäller hyreslagen (12 kap. jordabalken) samt Hyresnämndens riktlinjer.
Tvister med anledning av detta avtal ska i första hand lösas via förhandling. I andra hand kan ärendet hänskjutas till Hyresnämnden eller allmän domstol.
Avtalet har upprättats i två exemplar, varav parterna tagit var sitt. Underskrifterna bekräftar att parterna tagit del av och godkänner samtliga villkor i detta avtal.
Upprättat: ${today} | Genererat med Hyresavtalet.se
`; } // ─── GENERATE & DOWNLOAD PDF ───────────────────────────────────────── function downloadPDF() { const { jsPDF } = window.jspdf; const doc = new jsPDF({ unit: 'mm', format: 'a4' }); const navy = [27, 43, 75]; const gold = [201, 168, 76]; const gray = [74, 82, 104]; const lgray = [238, 240, 244]; const black = [26, 30, 46]; const W = 210, margin = 18; const contentW = W - margin * 2; let y = 0; // ── Header bar ── doc.setFillColor(...navy); doc.rect(0, 0, W, 36, 'F'); doc.setFillColor(...gold); doc.rect(0, 36, W, 1.2, 'F'); doc.setTextColor(255, 255, 255); doc.setFont('helvetica', 'bold'); doc.setFontSize(20); doc.text('HYRESAVTAL', W / 2, 18, { align: 'center' }); doc.setFont('helvetica', 'normal'); doc.setFontSize(8); doc.setTextColor(180, 190, 210); doc.text('Avseende uthyrning av bostad / lokal · Genererat med Hyresavtalet.se', W / 2, 27, { align: 'center' }); y = 46; // ── Helper functions ── const newPage = () => { doc.addPage(); doc.setFillColor(...navy); doc.rect(0, 0, W, 12, 'F'); doc.setTextColor(180, 190, 210); doc.setFont('helvetica', 'normal'); doc.setFontSize(7); doc.text('Hyresavtalet.se · Fortsättning', W / 2, 8, { align: 'center' }); y = 20; }; const checkY = (needed = 12) => { if (y + needed > 275) newPage(); }; const sectionTitle = (title) => { checkY(14); doc.setFillColor(...navy); doc.rect(margin, y, contentW, 7, 'F'); doc.setTextColor(...gold); doc.setFont('helvetica', 'bold'); doc.setFontSize(7.5); doc.text(title, margin + 3, y + 4.8); y += 11; }; const infoBox = (rows) => { const rowH = 7; const totalH = rows.length * rowH + 4; checkY(totalH + 2); doc.setFillColor(...lgray); doc.roundedRect(margin, y, contentW, totalH, 2, 2, 'F'); const col1 = margin + 3; const col2 = margin + contentW / 2 + 2; rows.forEach((row, i) => { const ry = y + 4 + i * rowH; doc.setFont('helvetica', 'bold'); doc.setFontSize(7.5); doc.setTextColor(...gray); doc.text(row[0], col1, ry); doc.setFont('helvetica', 'normal'); doc.setTextColor(...black); doc.text(row[1] || '—', col2, ry); }); y += totalH + 4; }; const bodyText = (text) => { checkY(10); doc.setFont('helvetica', 'normal'); doc.setFontSize(8.5); doc.setTextColor(...black); const lines = doc.splitTextToSize(text, contentW); lines.forEach(line => { checkY(6); doc.text(line, margin, y); y += 5.5; }); y += 2; }; const highlight = (label, value) => { doc.setFont('helvetica', 'normal'); doc.setFontSize(8.5); doc.setTextColor(...black); return `${label} ${value}`; }; // ── § 1 – PARTER ── sectionTitle('§ 1 – AVTALSPARTER'); infoBox([ ['Hyresvärd:', v('hv_namn')], ['Personnr / Org.nr:', v('hv_pnr')], ['E-post:', v('hv_email')], ['Telefon:', v('hv_telefon')], ['Adress:', v('hv_adress')], ]); infoBox([ ['Hyresgäst:', v('hg_namn')], ['Personnr:', v('hg_pnr')], ['E-post:', v('hg_email')], ['Telefon:', v('hg_telefon')], ['Adress:', v('hg_adress')], ]); // ── § 2 – OBJEKT ── sectionTitle('§ 2 – HYRESOBJEKT'); infoBox([ ['Adress:', v('obj_adress')], ['Typ:', v('obj_typ')], ['Storlek:', `${v('obj_rum')}, ${v('obj_yta')} m²`], ['Våning:', v('obj_vaning')], ['Ingår i hyran:', v('obj_ingar')], ]); // ── § 3 – PERIOD ── sectionTitle('§ 3 – HYRESPERIOD'); const tillText = (v('datum_till') !== '—') ? v('datum_till') : 'tillsvidare'; bodyText(`Hyresavtalet gäller ${v('kontraktstyp')} och löper från ${v('datum_fran')} till ${tillText}. Uppsägningstid är ${v('uppsagningstid')}. Uppsägning ska ske skriftligen.`); // ── § 4 – HYRA ── sectionTitle('§ 4 – HYRA OCH BETALNING'); const bgText = v('bankgiro') !== '—' ? v('bankgiro') : 'Meddelas separat'; const depText = v('deposition') !== '—' ? v('deposition') + ' kr' : 'Ingen deposition avtalad'; bodyText(`Hyresgästen ska betala en månadshyra om ${v('hyra')} kr. Hyran ska vara hyresvärden tillhanda senast ${v('bet_dag')} varje månad. Betalning sker till: ${bgText}.`); bodyText(`Depositionsbelopp: ${depText}. Depositionen återbetalas inom 30 dagar efter hyrestidens slut, förutsatt att hyresrätten är i godtagbart skick och inga obetalda hyror föreligger.`); // ── § 5 – ORDNING ── sectionTitle('§ 5 – ORDNINGSREGLER OCH SKICK'); bodyText(`Hyresgästen förbinder sig att väl vårda hyresobjektet och dess inventarier. Skador som uppstår till följd av hyresgästens vållande ska ersättas av hyresgästen. ${v('husdjur')}. ${v('rokning')}. ${v('andrahand')}.`); // ── § 6 – ÖVRIGT ── sectionTitle('§ 6 – ÖVRIGA VILLKOR'); bodyText(v('ovrigt') !== '—' ? v('ovrigt') : 'Inga övriga villkor har angivits.'); bodyText('I övrigt gäller hyreslagen (12 kap. jordabalken) samt Hyresnämndens riktlinjer.'); // ── § 7 – TVIST ── sectionTitle('§ 7 – TVISTELÖSNING'); bodyText('Tvister med anledning av detta avtal ska i första hand lösas via förhandling. I andra hand kan ärendet hänskjutas till Hyresnämnden eller allmän domstol.'); // ── § 8 – UNDERSKRIFTER ── checkY(70); sectionTitle('§ 8 – UNDERSKRIFTER (BankID)'); bodyText('Avtalet har upprättats i två exemplar, varav parterna tagit var sitt. Underskrift sker digitalt med BankID via separat signeringstjänst. Underskrifterna bekräftar att parterna tagit del av och godkänner samtliga villkor i detta avtal.'); checkY(55); y += 4; // Signature boxes const boxW = (contentW - 8) / 2; const boxH = 36; // Left box – Hyresvärd doc.setDrawColor(...navy); doc.setLineWidth(0.5); doc.roundedRect(margin, y, boxW, boxH, 2, 2); doc.setFillColor(...navy); doc.roundedRect(margin, y, boxW, 7, 2, 2, 'F'); doc.rect(margin, y + 4, boxW, 3, 'F'); doc.setTextColor(255,255,255); doc.setFont('helvetica', 'bold'); doc.setFontSize(7); doc.text('HYRESVÄRD · UNDERSKRIFT', margin + 3, y + 5); doc.setTextColor(...gray); doc.setFont('helvetica', 'normal'); doc.setFontSize(7.5); doc.text(v('hv_namn'), margin + 3, y + 17); doc.text('Personnr: ' + v('hv_pnr'), margin + 3, y + 23); doc.setDrawColor(...lgray); doc.setLineWidth(0.4); doc.line(margin + 3, y + 31, margin + boxW - 3, y + 31); doc.setFontSize(6.5); doc.setTextColor(...gray); doc.text('Signatur via BankID', margin + 3, y + 34.5); // Right box – Hyresgäst const rx = margin + boxW + 8; doc.setDrawColor(...navy); doc.setLineWidth(0.5); doc.roundedRect(rx, y, boxW, boxH, 2, 2); doc.setFillColor(...navy); doc.roundedRect(rx, y, boxW, 7, 2, 2, 'F'); doc.rect(rx, y + 4, boxW, 3, 'F'); doc.setTextColor(255,255,255); doc.setFont('helvetica', 'bold'); doc.setFontSize(7); doc.text('HYRESGÄST · UNDERSKRIFT', rx + 3, y + 5); doc.setTextColor(...gray); doc.setFont('helvetica', 'normal'); doc.setFontSize(7.5); doc.text(v('hg_namn'), rx + 3, y + 17); doc.text('Personnr: ' + v('hg_pnr'), rx + 3, y + 23); doc.setDrawColor(...lgray); doc.setLineWidth(0.4); doc.line(rx + 3, y + 31, rx + boxW - 3, y + 31); doc.setFontSize(6.5); doc.setTextColor(...gray); doc.text('Signatur via BankID', rx + 3, y + 34.5); y += boxH + 8; // ── BankID notice ── checkY(18); doc.setFillColor(232, 241, 255); doc.roundedRect(margin, y, contentW, 14, 2, 2, 'F'); doc.setTextColor(27, 60, 130); doc.setFont('helvetica', 'bold'); doc.setFontSize(7.5); doc.text('Information om BankID-signering', margin + 3, y + 5); doc.setFont('helvetica', 'normal'); doc.setFontSize(7); doc.text('Detta dokument är upprättat för signering med BankID via godkänd e-signeringstjänst (t.ex. Scrive, Assently eller Egreement).', margin + 3, y + 9.5); doc.text('Det signerade avtalet distribueras till båda parter via signeringstjänsten och utgör det juridiskt giltiga originalet.', margin + 3, y + 13); y += 18; // ── Footer ── const today = new Date().toLocaleDateString('sv-SE'); const contractId = 'HYR-' + Date.now(); doc.setFillColor(...navy); doc.rect(0, 287, W, 10, 'F'); doc.setTextColor(150, 165, 200); doc.setFont('helvetica', 'normal'); doc.setFontSize(6.5); doc.text(`Hyresavtalet.se · Upprättat: ${today} · Avtal-ID: ${contractId} · Väntar på BankID-signering`, W / 2, 293, { align: 'center' }); // ── Save ── const fname = 'hyresavtal_' + v('obj_adress').replace(/[^a-zA-Z0-9]/g,'_').substring(0,25) + '.pdf'; doc.save(fname); // Show confirmation document.getElementById('pdf_downloaded_msg').style.display = 'flex'; document.getElementById('pdf_filename').textContent = fname; } // ─── MODALS ────────────────────────────────────────────────────────── function showModal(id) { const tpl = document.getElementById('tpl_' + id); if (!tpl) return; document.getElementById('modal_content').innerHTML = tpl.innerHTML; document.getElementById('modal_overlay').classList.add('open'); document.body.style.overflow = 'hidden'; } function closeModal() { document.getElementById('modal_overlay').classList.remove('open'); document.body.style.overflow = ''; } document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); }); function toggleFaq(el) { const answer = el.nextElementSibling; const isOpen = answer.classList.contains('open'); // Close all document.querySelectorAll('.faq-a').forEach(a => a.classList.remove('open')); document.querySelectorAll('.faq-q').forEach(q => q.classList.remove('open')); if (!isOpen) { answer.classList.add('open'); el.classList.add('open'); } } function startNew() { location.reload(); }