Portfolio Builder

Your personal website · free

Links
Projects
'; } function shade(hex, f) { const h = hex.replace("#", ""); const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16); const m = (c) => Math.max(0, Math.min(255, Math.round(c + (f < 0 ? c * f : (255 - c) * f)))); return "#" + [m(r), m(g), m(b)].map((c) => c.toString(16).padStart(2, "0")).join(""); } function renderForm() { document.querySelectorAll("[data-f]").forEach((inp) => { inp.value = st[inp.dataset.f] || ""; }); $("#accent").innerHTML = ACCENTS.map((c) => '').join("") + ''; $("#links").innerHTML = st.links.map((l) => '
').join("") || '

No links yet.

'; $("#projects").innerHTML = st.projects.map((p) => '
').join("") || '

No projects yet.

'; } function renderPreview() { $("#frame").srcdoc = siteHtml(); } function renderAll() { renderForm(); renderPreview(); } document.body.addEventListener("input", (e) => { const f = e.target.closest("[data-f]"); if (f) { st[f.dataset.f] = f.value; save(); renderPreview(); return; } if (e.target.id === "accentPick") { st.accent = e.target.value; save(); document.querySelectorAll("[data-accent]").forEach((x) => x.classList.toggle("on", x.dataset.accent === st.accent)); renderPreview(); return; } const ln = e.target.closest("[data-link]"); if (ln) { const o = st.links.find((x) => x.id === ln.dataset.id); if (o) { o[ln.dataset.link] = e.target.value; save(); renderPreview(); } return; } const pr = e.target.closest("[data-proj]"); if (pr) { const o = st.projects.find((x) => x.id === pr.dataset.id); if (o) { o[pr.dataset.proj] = e.target.value; save(); renderPreview(); } return; } }); document.body.addEventListener("click", (e) => { const ac = e.target.closest("[data-accent]"); if (ac) { st.accent = ac.dataset.accent; save(); renderForm(); renderPreview(); return; } const dl = e.target.closest("[data-dellink]"); if (dl) { st.links = st.links.filter((x) => x.id !== dl.dataset.dellink); save(); renderForm(); renderPreview(); return; } const dp = e.target.closest("[data-delproj]"); if (dp) { st.projects = st.projects.filter((x) => x.id !== dp.dataset.delproj); save(); renderForm(); renderPreview(); return; } const a = e.target.closest("[data-act]"); if (!a) return; if (a.dataset.act === "addLink") { st.links.push({ id: uid(), label: "", url: "" }); save(); renderForm(); return; } if (a.dataset.act === "addProj") { st.projects.push({ id: uid(), title: "", desc: "", url: "" }); save(); renderForm(); return; } if (a.dataset.act === "download") { const blob = new Blob([siteHtml()], { type: "text/html" }); const x = document.createElement("a"); x.href = URL.createObjectURL(blob); x.download = "index.html"; x.click(); setTimeout(() => URL.revokeObjectURL(x.href), 500); toast("Downloaded index.html — upload it to any host"); return; } if (a.dataset.act === "preview") { const w = window.open(); if (w) { w.document.write(siteHtml()); w.document.close(); } return; } }); renderAll(); })();