Skip to content

Widgets

The widget system lets external modules add custom UI elements to Calendaria applications.


Module developers can:

  • Add custom buttons to the HUD, BigCal, and MiniCal
  • Add custom indicator displays
  • Replace built-in indicators with custom implementations

Widgets can be inserted at these locations:

PointLocationWidget Types
hud.buttons.leftLeft side of HUD barButtons
hud.buttons.rightRight side of HUD barButtons
hud.indicatorsIndicator section of HUD barIndicators
hud.trayTime controls trayButtons
minical.sidebarMiniCal sidebarButtons
bigcal.actionsBigCal action barButtons

Access these via CALENDARIA.api.widgetPoints.


Built-in indicator elements that custom widgets can replace. Replacements apply across all three applications (HUD, MiniCal, BigCal):

Element IDDescription
weather-indicatorWeather condition display
season-indicatorCurrent season display
era-indicatorEra/year display
cycle-indicatorCycle (zodiac, etc.) display

Access these via CALENDARIA.api.replaceableElements.


Widgets registered during calendaria.ready appear on the first render of all Calendaria applications.

Hooks.once('calendaria.ready', () => {
CALENDARIA.api.registerWidget('my-module', {
id: 'my-button',
type: 'button',
insertAt: 'hud.buttons.right',
icon: 'fas fa-star',
label: 'My Button',
tooltip: 'Click to do something',
onClick: () => {
console.log('Button clicked!');
}
});
});

An indicator renders from its icon, label, color, and tooltip properties:

CALENDARIA.api.registerWidget('my-module', {
id: 'mana-indicator',
type: 'indicator',
insertAt: 'hud.indicators',
icon: 'fas fa-gem',
label: 'Mana: 50',
color: '#3aa0ff',
tooltip: 'Current mana',
onAttach: (element) => {
// Called when the widget is added to the DOM
}
});

A type: 'custom' widget supplies a render function. The function receives the location string ('hud', 'minical', or 'bigcal') and returns an HTML string, which Calendaria wraps in a <div class="calendaria-widget-custom">:

CALENDARIA.api.registerWidget('my-module', {
id: 'mana-display',
type: 'custom',
insertAt: 'hud.indicators',
render: (location) => {
return `
<span class="indicator-icon"><i class="fas fa-gem"></i></span>
<span class="indicator-label">Mana: 50</span>
`;
},
onAttach: (element) => {
// Called when the widget is added to the DOM
}
});

A replacement that needs a render function must use type: 'custom'. The render function receives the location string and returns an HTML string:

CALENDARIA.api.registerWidget('my-module', {
id: 'custom-weather',
type: 'custom',
replaces: 'weather-indicator',
render: (location) => {
const weather = CALENDARIA.api.getCurrentWeather();
return `<span>Custom: ${weather?.label || 'None'}</span>`;
}
});

PropertyTypeDescription
idstringUnique widget identifier within your module
typestring'button', 'indicator', or 'custom'

One of these is required:

PropertyTypeDescription
insertAtstringWidget point ID from widgetPoints
replacesstringElement ID from replaceableElements
PropertyTypeDescription
iconstringFontAwesome icon class
labelstringButton text (optional)
tooltipstringHover tooltip text
colorstringIcon color (CSS color value)
onClickfunctionClick handler
disabledbooleanWhen true on a replaces widget, hides the replaced built-in element
PropertyTypeDescription
iconstringFontAwesome icon class
labelstringIndicator text
colorstringIcon color (CSS color value)
tooltipstringHover tooltip text
onAttachfunctionCalled with the DOM element when the widget is attached
PropertyTypeDescription
renderfunctionReceives the location string and returns an HTML string
onAttachfunctionCalled with the DOM element when the widget is attached

// Get all widgets at a specific point
const hudButtons = CALENDARIA.api.getRegisteredWidgets('hud.buttons.right');
// Get widget replacing a specific element
const weatherWidget = CALENDARIA.api.getWidgetByReplacement('weather-indicator');

Force all widgets to re-render:

CALENDARIA.api.refreshWidgets();

Widget-related hooks are documented in Hooks > Widget Hooks:

  • calendaria.widgetRegistered - Fired when a widget is registered
  • calendaria.widgetsRefresh - Fired when widgets are refreshed

Each widget receives a class based on its module ID for targeted styling:

/* Style all widgets from your module */
.widget-my-module {
/* Your styles */
}
/* Style a specific widget */
.widget-my-module[data-widget-id='my-module.my-button'] {
/* Your styles */
}

The data-widget-id attribute holds the fully-qualified id (moduleId.id).


my-module.js
Hooks.once('calendaria.ready', ({ api }) => {
// Add a button to show party resources
api.registerWidget('my-module', {
id: 'party-resources',
type: 'button',
insertAt: 'hud.buttons.right',
icon: 'fas fa-coins',
tooltip: 'Party Resources',
onClick: () => openResourceTracker()
});
// Add a custom widget
api.registerWidget('my-module', {
id: 'party-gold',
type: 'custom',
insertAt: 'hud.indicators',
render: (location) => {
const gold = getPartyGold();
return `
<span class="indicator-icon"><i class="fas fa-coins" style="color: gold;"></i></span>
<span class="indicator-label">${gold} gp</span>
`;
}
});
// Update the widget when gold changes
Hooks.on('updatePartyGold', () => {
api.refreshWidgets();
});
});