Mortgage Caculator

Description

09.26c14v1(v2) Mortgage Caculator

#1. Install Code

This is just a calculator using a sample formula, but you can easily send it to AI (ChatGPT or Claude. I recommend this AI chat bot) to customize it to your specific needs – and, notably, it can sync with your Squarespace style. You can also use this free calculator tool to create a calculator.

#1.1. You can edit Page where you want to place Calculator > Add a Code Block

09.26c14v1 Shipping Rates Calculator

#1.2. Add this code into Code Block

<!-- 09.26c14v1(v2) Mortgage Caculator -->
<div class="tp-calc" data-tp-calc></div>

<script>
  window.MortgageCalcConfig = {
    text: {
      title: "",
      amountLabel: "Loan Amount",
      amountHint: "Enter the total loan amount you want to borrow",
      rateLabel: "Interest Rate",
      rateUnit: "%",
      rateHint: "",
      periodLabel: "Loan Period",
      periodUnit: "years",
      periodHint: "",
      paymentHeading: "Monthly Payment",
      paymentHint: "This is an approximate monthly repayment amount for your mortgage based on the given inputs.",
      interestLabel: "Total Interest Paid",
      interestHint: "This is the total interest you would pay over the entire loan period.",
      ctaHeading: "Ready to get started?",
      ctaHint: "Get in touch with our mortgage advisors for personalized advice!",
      ctaButton: "Book a Meeting",
      ctaHref: "/contact-us"
    },
    tags: {
      title: "h2",
      amountLabel: "h3",
      rateLabel: "h3",
      periodLabel: "h3",
      paymentHeading: "h3",
      interestLabel: "h3",
      ctaHeading: "h2"
    },
    colors: {
      title: "",
      label: "",
      smallText: "",
      cardBg: "#f0f6fd",
      cardHeading: "",
      cardValue: "",
      cardMuted: "rgba(17,17,17,0.6)"
    },
    slider: {
      trackFill: "#197bff",
      trackEmpty: "rgba(17,17,17,0.05)",
      thumbBg: "#ffffff",
      thumbBorder: "rgba(17,17,17,0.1)",
      trackHeight: "6px",
      thumbSize: "24px"
    },
    layout: {
      inputBorder: "1px solid rgba(17,17,17,0.2)"
    },
    button: {
      text: "#ffffff",
      bg: "#197bff",
      border: "#197bff",
      hoverText: "#ffffff",
      hoverBg: "#1569e0",
      hoverBorder: "#1569e0"
    },
    ranges: {
      amount: { min: 100000, max: 5000000, step: 10000, value: 1000000 },
      rate: { min: 1, max: 15, step: 0.1, value: 5 },
      period: { min: 5, max: 40, step: 1, value: 30 }
    },
    formula: {
      currencySymbol: "$",
      moneyDecimals: 0,
      // paymentFn(amount, annualRatePct, years, formula) -> monthly payment
      // interestFn(amount, annualRatePct, years, formula) -> total interest
      paymentFn: null,
      interestFn: null
    }
  };
</script>

<script>
  (function () {
    var CFG = window.MortgageCalcConfig;

    var CSS =
      ".tp-calc,.tp-calc *{box-sizing:border-box}" +
      ".tp-calc{--tp-card-bg:#f0f6fd;--tp-track-fill:#197bff;--tp-track-empty:rgba(17,17,17,0.05);--tp-thumb-bg:#fff;--tp-thumb-border:rgba(17,17,17,0.1);--tp-track-h:6px;--tp-thumb-size:24px;--tp-input-border:1px solid rgba(17,17,17,0.2);--tp-pill-min-width:84px;--tp-card-radius:22px;color:inherit}" +
      ".tp-calc__wrap{max-width:1180px;margin:40px auto;padding:0 24px;display:grid;grid-template-columns:1.1fr 0.9fr;gap:48px;align-items:start}" +
      ".tp-calc__h{margin:0}" +
      ".tp-calc__title{margin:0 0 32px}" +
      ".tp-calc__field{padding:6px 0}" +
      ".tp-calc__labelRow{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:18px}" +
      ".tp-calc__sm{margin:14px 0 0}" +
      ".tp-calc__pill{min-width:var(--tp-pill-min-width);background:#fff;border:var(--tp-input-border);border-radius:12px;padding:12px 16px;display:inline-flex;gap:0;align-items:center;justify-content:center;font-size:17px;color:#111}" +
      ".tp-calc__num{width:auto;min-width:1ch;border:0;background:transparent;padding:0;margin:0;color:inherit}" +
      ".tp-calc__unit{margin-left:6px}" +
      ".tp-calc__unit--tight{margin-left:2px}" +
      ".tp-calc__range{width:100%;-webkit-appearance:none;appearance:none;height:var(--tp-track-h);border-radius:999px;margin:4px 0 0;cursor:pointer;background:linear-gradient(90deg,var(--tp-track-fill) 0%,var(--tp-track-fill) var(--tp-fill,0%),var(--tp-track-empty) var(--tp-fill,0%),var(--tp-track-empty) 100%)}" +
      ".tp-calc__range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:var(--tp-thumb-size);height:var(--tp-thumb-size);border-radius:50%;background:var(--tp-thumb-bg);border:1px solid var(--tp-thumb-border);box-shadow:0 2px 6px rgba(0,0,0,.15);cursor:pointer}" +
      ".tp-calc__range::-moz-range-thumb{width:var(--tp-thumb-size);height:var(--tp-thumb-size);border-radius:50%;background:var(--tp-thumb-bg);border:1px solid var(--tp-thumb-border);box-shadow:0 2px 6px rgba(0,0,0,.15);cursor:pointer}" +
      ".tp-calc__minmax{display:flex;justify-content:space-between;margin-top:12px}" +
      ".tp-calc__divider{height:1px;background:rgba(17,17,17,0.08);margin:30px 0}" +
      ".tp-calc__card{width:100%;background:var(--tp-card-bg);border-radius:var(--tp-card-radius);padding:44px}" +
      ".tp-calc__card .tp-calc__h{margin:0 0 4px}" +
      ".tp-calc__total{font-weight:800;font-size:60px;line-height:1;margin:10px 0 6px}" +
      ".tp-calc__cardDivider{height:1px;background:rgba(17,17,17,0.1);margin:28px 0}" +
      ".tp-calc__etaRow{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}" +
      ".tp-calc__etaVal{font-size:20px;font-weight:700;white-space:nowrap}" +
      ".tp-calc__spacer{height:26px}" +
      ".tp-calc__spacer--lg{height:40px}" +
      ".tp-calc a.tp-calc__btn{display:block !important;width:100% !important;text-align:center !important;border-radius:10px !important;margin-top:20px}" +
      "@media (max-width:980px){.tp-calc__wrap{grid-template-columns:1fr;gap:28px}.tp-calc__card{padding:34px}}" +
      "@media (max-width:520px){.tp-calc__card{padding:28px}.tp-calc__total{font-size:52px}}";

    function decl(prop, val) { return val ? prop + ":" + val + " !important;" : ""; }

    function buttonCSS() {
      var B = CFG.button;
      var base = decl("color", B.text) + decl("background-color", B.bg) + decl("border-color", B.border);
      var hover = decl("color", B.hoverText) + decl("background-color", B.hoverBg) + decl("border-color", B.hoverBorder);
      var out = "";
      if (base) out += ".tp-calc a.tp-calc__btn{" + base + "}";
      if (hover) out += ".tp-calc a.tp-calc__btn:hover{" + hover + "}";
      return out;
    }

    function injectCSS() {
      if (window.__mortCalcStyled) return;
      window.__mortCalcStyled = true;
      var st = document.createElement("style");
      st.type = "text/css";
      st.textContent = CSS + buttonCSS();
      document.head.appendChild(st);
    }

    function el(tag, cls, txt) {
      var e = document.createElement(tag);
      if (cls) e.className = cls;
      if (txt != null) e.textContent = txt;
      return e;
    }

    function heading(tag, txt, color) {
      var e = el(tag, "tp-calc__h", txt);
      if (color) e.style.color = color;
      return e;
    }

    function smallP(txt, color) {
      var e = el("p", "sqsrte-small tp-calc__sm", txt);
      if (color) e.style.color = color;
      return e;
    }

    function stepDecimals(step) {
      var s = String(step);
      var i = s.indexOf(".");
      return i < 0 ? 0 : (s.length - i - 1);
    }

    function fmtNumber(v, decimals) {
      return (+v).toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: decimals });
    }

    function formatMoney(n) {
      var F = CFG.formula;
      var d = Math.max(0, F.moneyDecimals | 0);
      return F.currencySymbol + (+n).toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });
    }

    function calcMonthly(P, ratePct, years) {
      var F = CFG.formula;
      if (typeof F.paymentFn === "function") return F.paymentFn(P, ratePct, years, F);
      var r = (ratePct / 100) / 12;
      var n = years * 12;
      if (!n) return 0;
      if (r === 0) return P / n;
      var pow = Math.pow(1 + r, n);
      return P * (r * pow) / (pow - 1);
    }

    function calcInterest(P, ratePct, years) {
      var F = CFG.formula;
      if (typeof F.interestFn === "function") return F.interestFn(P, ratePct, years, F);
      return calcMonthly(P, ratePct, years) * years * 12 - P;
    }

    function clampNum(v, min, max, decimals) {
      v = +v;
      if (!isFinite(v)) v = min;
      v = Math.min(max, Math.max(min, v));
      var f = Math.pow(10, decimals || 0);
      return Math.round(v * f) / f;
    }

    var measureSpan;
    function autosize(input) {
      if (!measureSpan) {
        measureSpan = document.createElement("span");
        measureSpan.style.cssText = "position:absolute;visibility:hidden;white-space:pre;left:-9999px;top:0";
        document.body.appendChild(measureSpan);
      }
      var cs = window.getComputedStyle(input);
      measureSpan.style.fontFamily = cs.fontFamily;
      measureSpan.style.fontSize = cs.fontSize;
      measureSpan.style.fontWeight = cs.fontWeight;
      measureSpan.style.fontStyle = cs.fontStyle;
      measureSpan.style.letterSpacing = cs.letterSpacing;
      measureSpan.textContent = input.value || "0";
      input.style.width = (measureSpan.offsetWidth + 2) + "px";
    }

    function numField(o) {
      var decimals = stepDecimals(o.r.step);
      var node = el("div", "tp-calc__field");
      var row = el("div", "tp-calc__labelRow");
      row.appendChild(heading(o.tag, o.label, CFG.colors.label));

      var pill = el("div", "tp-calc__pill");
      var input = el("input", "tp-calc__num");
      input.type = "text";
      input.setAttribute("inputmode", decimals ? "decimal" : "numeric");
      input.setAttribute("aria-label", o.label);
      pill.appendChild(input);
      if (o.suffix) {
        pill.appendChild(el("span", "tp-calc__unit" + (o.suffixTight ? " tp-calc__unit--tight" : ""), o.suffix));
      }
      row.appendChild(pill);
      node.appendChild(row);

      var range = el("input", "tp-calc__range");
      range.type = "range";
      range.min = o.r.min; range.max = o.r.max; range.step = o.r.step; range.value = o.r.value;
      range.setAttribute("aria-label", o.label);
      node.appendChild(range);

      var mm = el("div", "tp-calc__minmax");
      var s1 = el("span", "sqsrte-small", labelText(o.r.min));
      var s2 = el("span", "sqsrte-small", labelText(o.r.max));
      if (CFG.colors.smallText) { s1.style.color = CFG.colors.smallText; s2.style.color = CFG.colors.smallText; }
      mm.appendChild(s1); mm.appendChild(s2);
      node.appendChild(mm);

      if (o.hint) node.appendChild(smallP(o.hint, CFG.colors.smallText));

      function labelText(v) {
        var s = (o.prefix || "") + fmtNumber(v, decimals);
        if (o.suffix) s += (o.suffixTight ? "" : " ") + o.suffix;
        return s;
      }
      function disp(v) { return (o.prefix || "") + fmtNumber(v, decimals); }
      function get() {
        var cleaned = (input.value || "").replace(/[^0-9.]/g, "");
        var v = cleaned === "" ? o.r.min : parseFloat(cleaned);
        return clampNum(v, o.r.min, o.r.max, decimals);
      }
      function fill() {
        var v = +range.value, min = +range.min, max = +range.max;
        range.style.setProperty("--tp-fill", ((v - min) / (max - min)) * 100 + "%");
      }
      function sync(v) { range.value = v; fill(); autosize(input); }

      var cb = function () {};
      input.addEventListener("input", function () { autosize(input); cb(); });
      input.addEventListener("change", function () { input.value = disp(get()); autosize(input); cb(); });
      range.addEventListener("input", function () { input.value = disp(+range.value); autosize(input); cb(); });

      input.value = disp(o.r.value);
      fill();

      return { node: node, get: get, sync: sync, onChange: function (f) { cb = f; } };
    }

    function mount(root) {
      if (!root || root.dataset.tpDone === "1") return;
      root.dataset.tpDone = "1";

      var T = CFG.text, G = CFG.tags, K = CFG.colors, S = CFG.slider, R = CFG.ranges, F = CFG.formula;

      root.style.setProperty("--tp-card-bg", K.cardBg);
      root.style.setProperty("--tp-track-fill", S.trackFill);
      root.style.setProperty("--tp-track-empty", S.trackEmpty);
      root.style.setProperty("--tp-thumb-bg", S.thumbBg);
      root.style.setProperty("--tp-thumb-border", S.thumbBorder);
      root.style.setProperty("--tp-track-h", S.trackHeight);
      root.style.setProperty("--tp-thumb-size", S.thumbSize);
      root.style.setProperty("--tp-input-border", CFG.layout.inputBorder);

      var wrap = el("div", "tp-calc__wrap");
      var left = el("div", "tp-calc__left");
      var right = el("div", "tp-calc__right");

      if (T.title) {
        var title = heading(G.title, T.title, K.title);
        title.classList.add("tp-calc__title");
        left.appendChild(title);
      }

      var aField = numField({ label: T.amountLabel, tag: G.amountLabel, prefix: F.currencySymbol, hint: T.amountHint, r: R.amount });
      left.appendChild(aField.node);
      left.appendChild(el("div", "tp-calc__divider"));

      var rField = numField({ label: T.rateLabel, tag: G.rateLabel, suffix: T.rateUnit, suffixTight: true, hint: T.rateHint, r: R.rate });
      left.appendChild(rField.node);
      left.appendChild(el("div", "tp-calc__divider"));

      var pField = numField({ label: T.periodLabel, tag: G.periodLabel, suffix: T.periodUnit, hint: T.periodHint, r: R.period });
      left.appendChild(pField.node);

      var card = el("div", "tp-calc__card");
      card.appendChild(heading(G.paymentHeading, T.paymentHeading, K.cardHeading));
      var total = el("div", "tp-calc__total");
      if (K.cardValue) total.style.color = K.cardValue;
      card.appendChild(total);
      card.appendChild(smallP(T.paymentHint, K.cardMuted));

      card.appendChild(el("div", "tp-calc__cardDivider"));

      var intRow = el("div", "tp-calc__etaRow");
      var intLeft = el("div");
      intLeft.appendChild(heading(G.interestLabel, T.interestLabel, K.cardHeading));
      intLeft.appendChild(smallP(T.interestHint, K.cardMuted));
      var intVal = el("div", "tp-calc__etaVal");
      if (K.cardValue) intVal.style.color = K.cardValue;
      intRow.appendChild(intLeft); intRow.appendChild(intVal);
      card.appendChild(intRow);

      card.appendChild(el("div", "tp-calc__spacer tp-calc__spacer--lg"));
      card.appendChild(heading(G.ctaHeading, T.ctaHeading, K.cardHeading));
      card.appendChild(smallP(T.ctaHint, K.cardMuted));

      var btn = el("a", "btn btn--border theme-btn--primary-inverse sqs-button-element--primary tp-calc__btn", T.ctaButton);
      btn.setAttribute("href", T.ctaHref);
      btn.setAttribute("rel", "noopener");
      card.appendChild(btn);

      right.appendChild(card);
      wrap.appendChild(left); wrap.appendChild(right);
      root.appendChild(wrap);

      function recalc() {
        var P = aField.get(), rate = rField.get(), years = pField.get();
        aField.sync(P); rField.sync(rate); pField.sync(years);
        total.textContent = formatMoney(calcMonthly(P, rate, years));
        intVal.textContent = formatMoney(calcInterest(P, rate, years));
      }

      aField.onChange(recalc);
      rField.onChange(recalc);
      pField.onChange(recalc);
      recalc();
    }

    function init() {
      var nodes = document.querySelectorAll("[data-tp-calc]");
      for (var i = 0; i < nodes.length; i++) mount(nodes[i]);
    }

    function ready(fn) {
      if (document.readyState !== "loading") fn();
      else document.addEventListener("DOMContentLoaded", fn);
    }

    injectCSS();
    ready(init);

    if (!window.__mortCalcBound) {
      window.__mortCalcBound = true;
      window.addEventListener("mercury:load", init);
    }
  })();
</script>

09.26c14v1(v2) Mortgage Caculator

#2. Customize

#2.1. To change text + button url, change line 06 – 24

text: {
     title: "",
     amountLabel: "Loan Amount",
     amountHint: "Enter the total loan amount you want to borrow",
     rateLabel: "Interest Rate",
     rateUnit: "%",
     rateHint: "",
     periodLabel: "Loan Period",
     periodUnit: "years",
     periodHint: "",
     paymentHeading: "Monthly Payment",
     paymentHint: "This is an approximate monthly repayment amount for your mortgage based on the given inputs.",
     interestLabel: "Total Interest Paid",
     interestHint: "This is the total interest you would pay over the entire loan period.",
     ctaHeading: "Ready to get started?",
     ctaHint: "Get in touch with our mortgage advisors for personalized advice!",
     ctaButton: "Book a Meeting",
     ctaHref: "/contact-us"
   },

#2.2. Style will inherit from Tags. You can set tags for all elements (title, description, paragraph, button..) here (line 25 – 33)

tags: {
     title: "h2",
     amountLabel: "h3",
     rateLabel: "h3",
     periodLabel: "h3",
     paymentHeading: "h3",
     interestLabel: "h3",
     ctaHeading: "h2"
   },

#2.3. To change color, change line 34 – 42

  • Color will inherit from Tags. So if you use Heading 2 (h2), you can change Color in Site Styles
colors: {
      title: "",
      label: "",
      smallText: "",
      cardBg: "#f0f6fd",
      cardHeading: "",
      cardValue: "",
      cardMuted: "rgba(17,17,17,0.6)"
    },

#2.4. To change scroll/track bar style, change line 43 – 50

slider: {
      trackFill: "#197bff",
      trackEmpty: "rgba(17,17,17,0.05)",
      thumbBg: "#ffffff",
      thumbBorder: "rgba(17,17,17,0.1)",
      trackHeight: "6px",
      thumbSize: "24px"
    },

#2.5. To change button style (it will inherit from Primary Button Style), you can change line 54 to line 61

button: {
      text: "#ffffff",
      bg: "#197bff",
      border: "#197bff",
      hoverText: "#ffffff",
      hoverBg: "#1569e0",
      hoverBorder: "#1569e0"
    },

#2.6. To set Amount, Rate, Period, change line 62 – 66

ranges: {
     amount: { min: 100000, max: 5000000, step: 10000, value: 1000000 },
     rate: { min: 1, max: 15, step: 0.1, value: 5 },
     period: { min: 5, max: 40, step: 1, value: 30 }
   },

#2.7. To change formula, change line 67 – 74

formula: {
     currencySymbol: "$",
     moneyDecimals: 0,
     // paymentFn(amount, annualRatePct, years, formula) -> monthly payment
     // interestFn(amount, annualRatePct, years, formula) -> total interest
     paymentFn: null,
     interestFn: null
   }

An example (If you don’t understand it, describe formula then send it to AI, AI will build it for you)

formula: {
  currencySymbol: "$",
  moneyDecimals: 0,
  paymentFn: function (amount, rate, years, F) {
    var totalInterest = amount * (rate / 100) * years;
    return (amount + totalInterest) / (years * 12);
  },
  interestFn: function (amount, rate, years, F) {
    return amount * (rate / 100) * years;
  }
},

 

 

 

 

 

 

Buy me a coffee