# Site JS — what a team dev can do

Everything available to a developer on your Bynefit-hosted team site — Bootstrap, Font Awesome, twenty-five fonts, modals, toasts, confirms, forms, and the public API — wired together in one real page.

> **Prereq:** set up [config.json](https://bynefit.com/docs/help-site-config) first so Bootstrap, icons, and a font are loaded site-wide. The example below assumes you have `ui` in your `ui.load` array.

## What's on the page

A community-center landing page with a hero, an events grid, a newsletter signup, and a donate flow that uses every part of the kit:

- `data-bynli-ui` auto-loads Bootstrap + Font Awesome + the Bynefit fonts
- Bootstrap navbar + responsive grid
- Instrument Serif headlines, Inter body
- A Bynefit form, Bootstrap-styled, embedded inline
- A donate button that opens a custom modal
- A "remove from mailing list" link with a branded confirm dialog
- Toasts on every success

## The full page

Save this as `index.html` in your site. The `config.json` at the root is doing the Bootstrap/fonts loading; nothing else needed.

```
<!DOCTYPE html>
<html lang="en" data-bynli-ui="ui,font:inter">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ team.name }} — Community Hub</title>
  <style>
    body  { font-family: "Inter", system-ui, sans-serif; }
    h1, h2, .display-1, .display-4 { font-family: "Instrument Serif", Georgia, serif; font-weight: 400; }
    .hero { background: #0e0c0a; color: #f5ede0; padding: 6rem 0; }
    .hero h1 { font-size: clamp(2.5rem, 6vw, 5rem); line-height: 1.05; }
  </style>
</head>
<body>

  <!-- ── Bootstrap navbar ─────────────────────────────────── -->
  <nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom">
    <div class="container">
      <a class="navbar-brand fw-semibold" href="/">{{ team.name }}</a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#nav">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="nav">
        <ul class="navbar-nav ms-auto">
          <li class="nav-item"><a class="nav-link" href="#events">Events</a></li>
          <li class="nav-item"><a class="nav-link" href="#newsletter">Newsletter</a></li>
          <li class="nav-item">
            <button id="donateBtn" class="btn btn-primary ms-2">
              <i class="fa-solid fa-heart me-1"></i> Donate
            </button>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <!-- ── Hero ─────────────────────────────────────────────── -->
  <section class="hero text-center">
    <div class="container">
      <h1>Welcome to {{ team.name }}</h1>
      <p class="lead opacity-75 col-md-7 mx-auto">{{ team.description }}</p>
      <a href="#newsletter" class="btn btn-light btn-lg mt-3">
        <i class="fa-solid fa-envelope me-2"></i> Join our newsletter
      </a>
    </div>
  </section>

  <!-- ── Events grid (Bootstrap cards) ────────────────────── -->
  <section id="events" class="container py-5">
    <h2 class="mb-4">Upcoming events</h2>
    <div class="row g-4">
      <div class="col-md-4">
        <div class="card h-100 shadow-sm">
          <div class="card-body">
            <i class="fa-solid fa-calendar-day fs-3 text-primary mb-2"></i>
            <h5 class="card-title">Spring Cleanup</h5>
            <p class="card-text text-muted">Saturday, 9am — bring gloves.</p>
            <a href="#" class="btn btn-outline-primary btn-sm">RSVP</a>
          </div>
        </div>
      </div>
      <!-- repeat ... -->
    </div>
  </section>

  <!-- ── Newsletter form (Bynefit form, Bootstrap-styled) ───── -->
  <section id="newsletter" class="bg-body-tertiary py-5">
    <div class="container col-md-6">
      <h2 class="mb-3">Stay in the loop</h2>
      <p class="text-muted">One email a week, max. Unsubscribe any time.</p>
      <div data-bynli="form"
           data-form-id="frm_xxxxxxxxxxxx"
           data-form-style="default"
           data-form-success="You're on the list — talk soon."
           data-form-success-mode="toast"></div>
    </div>
  </section>

  <!-- ── Footer with unsubscribe link ─────────────────────── -->
  <footer class="container py-4 text-muted small text-center">
    Already on our list?
    <a href="#" id="unsubLink">Remove me from the mailing list</a>
  </footer>

  <script>
    // Donate button → custom modal built on the fly
    document.getElementById('donateBtn').addEventListener('click', function () {
      Bynli.use('modal', 'toast').then(function () {
        var m = Bynli.Modal.create({
          title: 'Support our work',
          body:
            '<p class="text-muted">Every dollar funds programming for the kids in our community.</p>' +
            '<div class="d-grid gap-2">' +
              '<button class="btn btn-primary" data-amt="10"><i class="fa-solid fa-heart me-2"></i>$10</button>' +
              '<button class="btn btn-primary" data-amt="50"><i class="fa-solid fa-heart me-2"></i>$50</button>' +
              '<button class="btn btn-primary" data-amt="100"><i class="fa-solid fa-heart me-2"></i>$100</button>' +
            '</div>',
          size: 'sm'
        });
        m.el.querySelectorAll('[data-amt]').forEach(function (b) {
          b.addEventListener('click', function () {
            m.close();
            Bynli.Toast.success('Thanks! Opening secure checkout for $' + b.dataset.amt + '…');
            // Redirect to your real donation endpoint:
            // window.location.href = '/donate?amt=' + b.dataset.amt;
          });
        });
        m.open();
      });
    });

    // Unsubscribe link → branded confirm + toast
    document.getElementById('unsubLink').addEventListener('click', function (e) {
      e.preventDefault();
      Bynli.use('confirm', 'toast').then(function () {
        Bynli.Confirm.ask(
          "We'll remove your email from every future send. You can re-join any time.",
          { title: 'Unsubscribe?', confirmLabel: 'Yes, unsubscribe', danger: true }
        ).then(function () {
          // Hit your unsubscribe endpoint here.
          Bynli.Toast.success("You're off the list. Sorry to see you go.");
        }).catch(function () { /* cancelled */ });
      });
    });

    // Form success → toast (already requested via data-form-success-mode)
    document.addEventListener('bynli:form:submit:success', function (e) {
      console.log('Newsletter signup:', e.detail);
    });
  </script>

</body>
</html>
```

## What's doing what

| Line | What it does |
| --- | --- |
| `data-bynli-ui="ui,font:inter"` | runtime.js sees this on `<html>` and loads Bootstrap + Font Awesome + DM Sans + Instrument Serif + Inter — no `<script>` or `<link>` tags needed. Kept for backward compatibility — for new pages prefer `<meta name="bynli-ui-load" content="ui,font:inter">` in the `<head>` (same behaviour, more idiomatic), or set `ui.load` in `config.json` if every page on the site needs it. |
| `{{ team.name }}` | Bynefit Blocks server-side variable. Renders before the page leaves the server — see [Bynefit Blocks](https://bynefit.com/docs/help-bynefit-blocks). |
| `data-bynli="form" data-form-style="default"` | Fetches the form schema by ID, renders inputs with Bootstrap classes (`form-control`, `btn btn-primary`), and POSTs to the public submit endpoint when sent. |
| `data-form-success-mode="toast"` | On success, show a Bynefit toast instead of swapping the form for a success card. |
| `Bynli.use('modal', 'toast')` | Lazy-loads the two modules just before they're needed. If they're already loaded (because `data-bynli="form"` already pulled in the toast module), this is a no-op. |
| `Bynli.Modal.create({...})` | Builds a branded modal entirely from JS. Returns `{ el, open, close }` so you can wire events to the inner DOM. |
| `Bynli.Confirm.ask(msg, opts)` | Branded confirm dialog. Resolves on confirm, rejects on cancel. Use `danger: true` for destructive actions. |
| `document.addEventListener('bynli:form:submit:success', ...)` | Every form embed dispatches this custom event after a successful submit. `e.detail` carries the response body — useful for analytics or downstream logic. |

## What you don't have to write

The runtime takes care of, automatically:

- Loading `bynli.js` only when the page actually uses a kit attribute or asset
- Page-view ping to your dashboard analytics
- Suspension / maintenance overlay if your site is in either state
- "Powered by Bynefit" badge (unless your `config.json` turns it off and you have the entitlement)

> **Tip:** Open the Network tab on your live site after a fresh reload. You should see `runtime.js`, then a single `config.json` fetch, then on-demand requests for each builder module and asset as the page references them. Nothing more.

---

Source: https://bynefit.com/docs/help-site-js
