Basics
The URL of an OTE feed.json document. Enter several,
comma-separated, to combine them (uses the feeds
attribute instead of feed).
Paste an OTE feed object, an event array, or one OTE event object.
Optional. Renders only the event with this OTE id — for an
event's own page. Ignores show-past, since an id is an
explicit request for that event. No match shows "Event not found".
Optional maximum number of events to show. Default: no limit.
Appearance
Optional. Cards use a built-in placeholder when an event has no image.
Optional CSS font-family for the widget.
Optional base font-size for the widget.
Cards layout only. Minimum card width in the responsive grid — also accepts any raw CSS length via the attribute (e.g. card-width="280px"), or set --ote-card-min-width directly in CSS.
Behavior
Which optional pieces of each event show on the card (Preview) versus the detail modal / list layout's expanded row (Detail). Ignored entirely by the calendar layout.
| Field | Preview | Detail |
|---|---|---|
| image | ||
| when | ||
| location | ||
| attendance | ||
| description | ||
| price | ||
| tags | ||
| organizer | ||
| eligibility | ||
| cfp |
Cards layout only. Collapses events that share the same partOf.id into one stacked card with a badge; the detail modal gains prev/next between occurrences. Off by default. To try it, switch to JSON source mode above and paste events whose partOf field shares an id, e.g. "partOf": {"id": "https://example.org/series/monthly", "type": "series"}.
Language of the widget's own UI text (loading/empty/error messages). "auto" follows the visitor's browser language.
Include events whose start date has already passed. Default: on.
Actions
What happens when a visitor selects an event. Default: modal.
Native actions. The default placement is the event detail view.
Adds a custom action with an icon that shows an alert for the selected event.
Live preview
Copy-paste snippet
This is the exact markup for the configuration above.
The snippet uses a fixed widget version. Use
/embed/latest/ only when you explicitly want automatic
updates.
Basics
Enables the Calendar group (Google Calendar, webcal://, ICS download). Leave blank to omit it.
Enables the RSS group (Feedly, feed://, RSS download). Leave blank to omit it.
Enables the OTE feed group (OTE Reader, OTE Tools preview, JSON download). Leave blank to omit it.
Used only for the trigger's accessible label ("Subscribe to <name>").
Appearance
menu: one trigger, one popover listing every group. badges: one small trigger per group (Calendar / RSS / OTE feed), each with its own popover.
Behavior
Which subscribe links to render. Defaults to everything valid for the feed URLs above.
Live preview
Copy-paste snippet
This is the exact markup for the <ote-subscribe> configuration above.
Versioning
Production embeds should use a fixed version URL so future widget changes do not alter existing sites without an explicit upgrade.
- Recommended
https://tools.opentechevents.org/embed/v0.8.0/ote-events.js- Automatic updates
https://tools.opentechevents.org/embed/latest/ote-events.js- Legacy alias
https://tools.opentechevents.org/embed/ote-events.js
The widget follows semantic versioning: patch releases are compatible fixes, minor releases add compatible features, and major releases may require migration. Check the changelog before changing the version used by a production page.
Component versions and OTE Spec versions are independent. The component version tells you which widget bundle you are loading; the OTE Spec version tells you which event/feed shape that bundle is designed to consume.
OTE Spec 0.4.0 lets a feed publish an http:// image. On an
https page the browser blocks such an address as mixed
content, so the widget treats that image as absent — cards show their
placeholder (or your placeholder-image) and the detail
view renders without one, instead of showing a frame that can never
load. If an event lists several images, the first
https:// one is used even when an http://
entry comes before it.
| Component | OTE Spec | Links |
|---|---|---|
v0.8.0 |
v0.4.0 | release script |
v0.7.0 |
v0.3.0 | release script |
v0.6.0 |
v0.3.0 | release script |
v0.5.0 |
v0.3.0 | release script |
v0.4.0 |
v0.3.0 | release script |
v0.3.1 |
v0.3.0 | release script |
v0.3.0 |
v0.3.0 | release script |
v0.2.0 |
v0.3.0 | release script |
v0.1.1 |
v0.3.0 | release script |
v0.1.0 |
v0.3.0 | release script |
Host app integration
Apps such as OTE Reader can pass already-filtered events in memory and
keep subscriptions, folders, read state, and persistence outside the
widget. Use sort="none" when the host app owns ordering.
await customElements.whenDefined("ote-events");
const widget = document.querySelector("ote-events");
widget.setAttribute("sort", "none");
widget.setAttribute("event-actions", "none");
widget.events = filteredEvents.map((event) => ({
...event,
_feedUrl: event.feedUrl,
_feedTitle: event.feedTitle,
}));
widget.eventActions = (context) => [{
id: "save",
label: saved.has(context.originalEvent.id) ? "Saved" : "Save",
icon: saved.has(context.originalEvent.id) ? "bookmark" : "star",
pressed: saved.has(context.originalEvent.id),
placement: "preview",
onClick(_previewEvent, actionContext) {
saveEvent(actionContext.originalEvent.id, actionContext.feed?.url);
},
}];
widget.eventClassName = (context) =>
read.has(context.originalEvent.id) ? "is-read" : "is-unread";
widget.eventBadges = (context) =>
context.originalEvent.cfpOpen ? [{ label: "CFP", icon: "plus" }] : [];
| API | Use |
|---|---|
el.events |
Render an in-memory array of OTE events without fetching. |
sort="none" |
Keep the host application's ordering instead of widget date sorting. |
empty-message |
Customize the empty state shown after host-side filters. |
eventActions(context) |
Return per-event actions with dynamic label, icon, placement, and pressed state. |
eventClassName(context) |
Add host-owned state classes such as read, unread, saved, or canceled. |
eventBadges(context) |
Add host-owned badges such as folder, collection, CFP, or saved state. |
The ote-event-action event includes action,
previewEvent, originalEvent, index,
feed, and source. The widget renders only; it
does not store favorites, folders, read state, or filters.
Using AI agents?
The embed package includes an agent-facing guide with the typed
eventActions contract, placement rules, and the tests that
act as living documentation.