BMLT Crumb Widget

BMLT Crumb Widget

An embeddable NA meeting finder widget for any website.

Live Demo

Overview

Crumb Widget is a self-contained JavaScript widget that queries a BMLT root server and renders a fully featured meeting finder — search, filters, list view, and an interactive map.

It ships as a single JavaScript file with all CSS injected at runtime. No stylesheets, no build steps, no framework required on the host page.

Quick Start

To get started, add a container element to your page, point it at your BMLT root server, and load the widget script. Here is a complete example:

HTML
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Meeting Finder</title>
  </head>
  <body>
    <div
      id="crumb-widget"
      data-root-server="https://myserver.com/main_server/"
      data-service-body="3"
    ></div>
    <script type="module" src="https://cdn.aws.bmlt.app/crumb-widget.js"></script>
  </body>
</html>

The widget initializes automatically when the script loads, so no additional JavaScript is needed.

Important: Be sure your page includes <meta name="viewport" content="width=device-width, initial-scale=1.0" /> in the <head>. This is important for proper rendering on mobile devices and small screens.

Data Attributes

All configuration is set as data-* attributes on the #crumb-widget div.

Attribute Description
data-root-server Required Full URL to your BMLT root server.
Example: https://example.org/main_server
data-service-body Optional Filter meetings to one or more service bodies. Accepts a single numeric ID or a comma-separated list. Omit to show all meetings on the server.
Single: "42"
Multiple: "42,57,103"
Note: recursive lookup is always enabled — child service bodies are automatically included and cannot be disabled.
data-view Optional Default view when the widget loads.
Values: list (default) or map
Can be overridden at runtime by the ?view= query parameter (see URL Query Parameters).

Global Config Object

For options not available as data attributes, define CrumbWidgetConfig as a global variable before loading the script.

HTML
<script>
  var CrumbWidgetConfig = {
    defaultView: 'map'
  };
</script>
<script src="app.js"></script>
PropertyTypeDescription
defaultView 'list' | 'map' Override the default view. Takes precedence over data-view.
language string Override the UI language (e.g. 'es', 'fr'). Defaults to navigator.language, falling back to English. Supported: en, es, fr, de, pt, it, sv, da.
columns string[] Columns to show in list view. Default: ['time', 'name', 'location', 'address']. Omit any column name to hide it. Available values: time, name, location (venue/building name), address (street address with in-person/online badges), service_body. service_body is hidden by default — add it explicitly to show the service body name.
geolocation boolean Show a Near Me button and auto-geolocate on page load to find meetings near the user. Requires HTTPS. Default: false. If geolocation fails or is denied, an error is shown — no fallback load occurs. Note: serviceBodyIds filters have no effect on geolocation searches; results are determined entirely by proximity.
geolocationRadius number Search radius in miles when using geolocation. Default: 10.
darkMode 'auto' | true | false Built-in dark color scheme. 'auto' follows the visitor's OS preference (prefers-color-scheme); true forces dark mode regardless of OS setting; false (default) disables it. Works alongside dark mode tiles.
map.tiles TilesConfig Custom map tile provider. Replaces the default OpenStreetMap tiles. See Tile Provider below.
map.tiles_dark TilesConfig Alternate tile provider used when prefers-color-scheme: dark. Swaps automatically on OS theme change. See Dark Mode Tiles below.
map.markers.location MarkerConfig Custom map marker for meeting locations. Replaces the default NA marker. See Marker Config below.

Language

The widget automatically detects the visitor's language from navigator.language and falls back to English. Override it with the language config property:

<script>
  var CrumbWidgetConfig = {
    language: 'es'
  };
</script>
<script src="app.js"></script>

Language codes are matched on the base tag, so 'fr-CA' uses fr. Supported languages:

CodeLanguage
enEnglish
esSpanish
frFrench
deGerman
ptPortuguese
itItalian
svSwedish
daDanish

Geolocation

Enable the Near Me button with geolocation: true. When enabled, the widget also attempts to geolocate the user automatically on page load and show meetings nearby. Requires a secure context (HTTPS).

If geolocation is denied or unavailable, an error message is displayed — there is no silent fallback to loading all meetings. This is intentional: on large servers without service body scoping, loading all meetings could return tens of thousands of results.

Service body filters have no effect on geolocation searches. When using Near Me, results are determined entirely by the user's coordinates and geolocationRadius. If no serviceBodyIds are configured, the Near Me button cannot be toggled off — geolocation is the only data source.

While in map view with Near Me active, panning or zooming the map reveals a Search this area button at the top of the map. Clicking it reloads meetings centered on the current map viewport, using the same geolocationRadius. The map stays at the user's position — it does not jump to fit the new results.

<script>
  var CrumbWidgetConfig = {
    geolocation: true,
    geolocationRadius: 25  // miles, default 10
  };
</script>
<script src="app.js"></script>

URL Query Parameters

The ?view= query parameter sets the initial view and overrides data-view and defaultView:

ValueBehaviour
listForce list view on load
mapForce map view on load
auto Geolocate on load — success shows map with nearby results; failure falls back to list with all meetings

Example: https://example.org/meetings/?view=auto

?view=list and ?view=map disable auto-geolocation even if geolocation: true is set.

CSS Variables

The widget exposes CSS custom properties so you can theme it without touching JavaScript. Set any of these on the #crumb-widget element — only specify the ones you want to override.

VariableDefaultControls
--bmlt-font-familysystem-ui, -apple-system, sans-serifWidget font stack
--bmlt-font-size16pxBase font size
--bmlt-background#ffffffWidget and controls bar background
--bmlt-text#111827Primary text color
--bmlt-border#e5e7ebBorders and dividers
--bmlt-accent#2563ebActive buttons, links, focus indicators
--bmlt-accent-light#eff6ffRow hover, active filter panel background
--bmlt-border-radius8pxCard and button corner radius
--bmlt-row-alt#f9fafbAlternating (even) row background
--bmlt-in-person#15803dIn-person badge text
--bmlt-in-person-bg#dcfce7In-person badge background
--bmlt-virtual#1d4ed8Virtual badge text
--bmlt-virtual-bg#dbeafeVirtual badge background
--bmlt-surface#ffffffInput, button, and dropdown panel backgrounds
--bmlt-hover#f9fafbNeutral hover state (buttons and rows not using accent)
--bmlt-divider#f3f4f6Internal row and list divider lines
--bmlt-text-secondary#6b7280Secondary / muted text (table headers, time, location)
--bmlt-text-muted#9ca3afVery muted text (footer count, empty-state messages)
CSS — change accent color and remove rounded corners
#crumb-widget {
  --bmlt-accent: #dc2626;
  --bmlt-accent-light: #fef2f2;
  --bmlt-border-radius: 0px;
}

Dark Mode

The widget ships with a built-in dark palette. Enable it with the darkMode config option — no extra CSS required. Works alongside the dark mode tile option.

HTML — follow OS preference
<script>
  var CrumbWidgetConfig = {
    darkMode: 'auto'
  };
</script>
<script src="app.js"></script>
HTML — always dark
<script>
  var CrumbWidgetConfig = {
    darkMode: true
  };
</script>
<script src="app.js"></script>

To use a custom dark palette instead, override the CSS variables directly:

CSS — custom dark theme via media query
@media (prefers-color-scheme: dark) {
  #crumb-widget {
    --bmlt-background:     #1e293b;
    --bmlt-text:           #f1f5f9;
    --bmlt-border:         #334155;
    --bmlt-accent:         #60a5fa;
    --bmlt-accent-light:   #1a3460;
    --bmlt-row-alt:        #243044;
    --bmlt-in-person:      #86efac;
    --bmlt-in-person-bg:   #14532d;
    --bmlt-virtual:        #93c5fd;
    --bmlt-virtual-bg:     #1e3a5f;
    --bmlt-surface:        #2a3a50;
    --bmlt-hover:          #2d4060;
    --bmlt-divider:        #263244;
    --bmlt-text-secondary: #94a3b8;
    --bmlt-text-muted:     #64748b;
  }
}

CSS Helper Classes

These classes are applied internally and can also be targeted in your own CSS for further customization.

ClassUsed on
bmlt-btn-primaryFilled accent buttons — active filter chips, view toggle, Join Meeting
bmlt-btn-secondaryOutlined buttons — Get Directions
bmlt-linkLink-style buttons — Back, email contact
bmlt-badge-in-personIn-person venue badge
bmlt-badge-virtualVirtual venue badge
bmlt-cardDetail section cards
bmlt-rowMeeting list table rows
CSS — example: override the outlined button hover
#crumb-widget .bmlt-btn-secondary:hover {
  background-color: transparent;
  opacity: 0.8;
}

Tile Provider

By default the map uses OpenStreetMap tiles. Switch to any Leaflet-compatible tile provider by setting map.tiles.

PropertyTypeDescription
url string Tile URL template. Use {z}, {x}, {y} placeholders (and {r} for retina where supported).
attribution string Attribution HTML displayed in the map's attribution control. Required by most tile providers' terms of service.
HTML — Stadia Maps example
<script>
  var CrumbWidgetConfig = {
    map: {
      tiles: {
        url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png',
        attribution: '&copy; <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      }
    }
  };
</script>
<script src="app.js"></script>

Dark Mode Tiles

Set map.tiles_dark to use a different tile layer for visitors with prefers-color-scheme: dark. The layer swaps automatically when the OS theme changes — no page reload needed. If omitted, the same tiles are used in all color schemes.

HTML — light + dark tiles
<script>
  var CrumbWidgetConfig = {
    map: {
      tiles: {
        url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png',
        attribution: '&copy; <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      },
      tiles_dark: {
        url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
        attribution: '&copy; <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      },
    }
  };
</script>
<script src="app.js"></script>
HTML — Mapbox example
<script>
  var CrumbWidgetConfig = {
    map: {
      tiles: {
        url: 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=<pk.your.access.token>',
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>, Imagery &copy; <a href="https://www.mapbox.com/">Mapbox</a>',
      },
      tiles_dark: {
        url: 'https://api.mapbox.com/styles/v1/mapbox/dark-v10/tiles/{z}/{x}/{y}?access_token=<pk.your.access.token>',
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a>, Imagery &copy; <a href="https://www.mapbox.com/">Mapbox</a>',
      },
    }
  };
</script>
<script src="app.js"></script>
Google Maps tiles: Google's tile API requires a session token obtained server-side (or via a short-lived client-side fetch). Because the token must be fetched before CrumbWidgetConfig is set, you need to load app.js dynamically after the token is ready. The example below uses the Google Map Tiles API — you'll need a referer-restricted API key with the Map Tiles API enabled.
<script>
(async () => {
  const key = 'YOUR_REFERER_RESTRICTED_GOOGLE_API_KEY';
  const { session } = await fetch(
    `https://tile.googleapis.com/v1/createSession?key=${encodeURIComponent(key)}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ mapType: 'roadmap', language: 'en-US', region: 'US' })
    }
  ).then(r => r.json());

  window.CrumbWidgetConfig = {
    map: {
      tiles: {
        attribution: '&copy; Google',
        url: `https://tile.googleapis.com/v1/2dtiles/{z}/{x}/{y}?session=${session}&key=${key}`
      }
    }
  };

  const s = document.createElement('script');
  s.src = 'app.js';
  document.body.appendChild(s);
})();
</script>

Marker Config

The map.markers.location object lets you replace the default NA map pin with any image or inline SVG.

PropertyTypeDescription
html string HTML rendered inside the marker element. Typically an <img> tag or inline SVG.
width number Icon width in pixels.
height number Icon height in pixels. The anchor point is set to the bottom-center of the icon.
HTML — custom marker example
<script>
  var CrumbWidgetConfig = {
    map: {
      markers: {
        location: {
          html: '<img src="https://example.com/my-marker.png">',
          width: 23,
          height: 33
        }
      }
    }
  };
</script>
<script src="app.js"></script>

Example: Basic Embed

Show all meetings on a public BMLT server:

HTML
<div
  id="crumb-widget"
  data-root-server="https://latest.aws.bmlt.app/main_server"
></div>
<script src="https://cdn.aws.bmlt.app/crumb-widget.js"></script>

Example: Filter by Service Body

Show only meetings belonging to service body 42 (and its children):

HTML
<div
  id="crumb-widget"
  data-root-server="https://bmlt.sezf.org/main_server"
  data-service-body="42"
></div>
<script src="app.js"></script>

Example: Multiple Service Bodies

Pass a comma-separated list to include meetings from several service bodies:

HTML
<div
  id="crumb-widget"
  data-root-server="https://bmlt.sezf.org/main_server"
  data-service-body="42,57,103"
></div>
<script src="app.js"></script>

Example: Default to Map View

HTML
<div
  id="crumb-widget"
  data-root-server="https://bmlt.sezf.org/main_server"
  data-service-body="42"
  data-view="map"
></div>
<script src="app.js"></script>

Example: WordPress

The easiest way to add Crumb Widget to a WordPress site is the official Crumb plugin (GitHub). It wraps this widget in a shortcode with a settings screen — no code required.

Installation

  1. Search for Crumb in the WordPress plugin directory, or upload the plugin ZIP manually.
  2. Activate the plugin.
  3. Go to Settings → Crumb and enter your root server URL.
  4. Add the shortcode to any page or post.

Shortcode

WordPress shortcode
[bmlt_client]

The root server URL set in the plugin settings is used by default. You can override it — or filter by service body — directly in the shortcode:

WordPress shortcode — with overrides
[bmlt_client root_server="https://your-server/main_server" service_body="42"]

Advanced configuration

All global config options (language, geolocation, columns, map tiles, etc.) can be set from your theme's functions.php via the bmltclient_config filter:

PHP — functions.php
add_filter( 'bmltclient_config', function( $config ) {
    return array_merge( $config, [
        'language'          => 'en',
        'geolocation'       => true,
        'geolocationRadius' => 20,
        'height'            => 800,
        'columns'           => [ 'time', 'name', 'location', 'address', 'service_body' ],
    ] );
} );

Features

Website Builders

Crumb Widget can also be embedded on common website builders, but the setup varies by platform.

Google Sites

Google Sites supports embedding HTML, CSS, and JavaScript code using Insert → Embed → Embed code. You can also use a full-page embed if you want the meeting finder to live on its own page.

  1. Open your site in Google Sites.
  2. Click InsertEmbed.
  3. Choose Embed code.
  4. Paste your widget markup and script.
  5. Click Insert, then publish your site.
Google Sites embed
<div
  id="crumb-widget"
  data-root-server="https://myserver.com/main_server/"
  data-service-body="3"
></div>
<script type="module" src="https://cdn.aws.bmlt.app/crumb-widget.js"></script>

Note: In Google Sites, JavaScript must be included inside <script> tags when using the embed code option.

Squarespace

Squarespace supports custom code, but JavaScript support depends on your plan. Their documentation says advanced code blocks and code injection are available on Core, Plus, Advanced, Business, Commerce Basic, and Commerce Advanced plans.

  1. Edit the page where you want the widget.
  2. Add a Code Block.
  3. Paste the widget container and script.
  4. Save and publish.
Squarespace code block
<div
  id="crumb-widget"
  data-root-server="https://myserver.com/main_server/"
  data-service-body="3"
></div>
<script type="module" src="https://cdn.aws.bmlt.app/crumb-widget.js"></script>

Important: If JavaScript is disabled in your Squarespace code block or unavailable on your plan, the widget will not load. You may need a higher-tier Squarespace plan for JavaScript-based embeds.

Wix

Wix supports custom code through Settings → Custom Code. Wix says this requires your site to be published and to have a connected domain. Since connecting your own domain is a Premium or Studio feature, you may need a paid plan to use the JavaScript embed method.

  1. Publish your site.
  2. Make sure the site has a connected domain.
  3. Go to SettingsCustom Code.
  4. Add the widget script to the page where you want it to load.
  5. Add a container element in the page content where the widget should render.
Wix container
<div
  id="crumb-widget"
  data-root-server="https://myserver.com/main_server/"
  data-service-body="3"
></div>
Wix custom code
<script type="module" src="https://cdn.aws.bmlt.app/crumb-widget.js"></script>

Important: Wix’s custom code feature is intended for third-party snippets and depends on Wix platform restrictions. If you do not see the Custom Code option, check whether your site is published and whether it has a connected domain.

Browser Support

All modern browsers. Internet Explorer is not supported.