A foundational module that allows other modules to add custom resource bars to D&D 5e character sheets, matching the native HP and Hit Dice styling.
- Universal API - Any module can register custom resource bars
- User-Created Bars - Players can add custom bars directly via "+" button (edit mode)
- Native Styling - Matches D&D 5e character sheet aesthetics perfectly
- Auto-Refresh - Bars update automatically when actor data changes
- Click-to-Edit - Optional editable values with simple input dialog
- Customizable - Colors, positions, visibility conditions
- Config Buttons - Optional gear icon for advanced configuration
- GM-Only Mode - Hide bars from players if needed
Manifest URL:
https://raw.githubusercontent.com/TinyDragonEgg/aspects-custom-bars/main/module.json
Or search for "Custom Resource Bars" in Foundry's module browser.
This module provides two ways to add custom resource bars:
- User-Created Bars - Players can add bars directly via the character sheet
- Module API - Modules can programmatically register bars with advanced features
- Open your character sheet
- Click the Edit button (lock icon) to enter edit mode
- Scroll to the resources section (below Hit Dice)
- Click the "+ Add Resource Bar" button
- Fill in the details:
- Resource Name (e.g., "Focus Points", "Rage", "Superiority Dice")
- Current Value
- Maximum Value
- Bar Color (pick your favorite!)
- Click Create
Your custom bar will appear immediately! You can:
- Click the value to edit it quickly
- Click the trash icon (edit mode) to delete the bar
- Track any resource you want without needing a module
This module provides an API for other modules to register custom bars with advanced features. It doesn't add any bars by itself - it's a foundation for other modules to build on.
// Wait for the custom bars system to be ready
Hooks.on('customBars.ready', () => {
// Register a Ballistic Ward bar for Artillery Witch wizards
game.customBars.registerBar({
id: 'ballisticWard',
name: 'Ballistic Ward',
// Calculate current value
getValue: (actor) => {
return actor.getFlag('my-module', 'ward.current') || 0;
},
// Calculate max value
getMax: (actor) => {
const conMod = actor.system.abilities.con.mod;
const wizLevel = actor.classes?.wizard?.system?.levels || 0;
return conMod * wizLevel;
},
// Only show for Artillery Witch subclass
showWhen: (actor) => {
return actor.items.some(i =>
i.type === 'subclass' &&
i.name.includes('Artillery Witch')
);
},
editable: true,
barColor: '#4a90e2',
position: 0
});
// Listen for value changes
Hooks.on('customBars.barValueChange', async (actor, barId, newValue, oldValue) => {
if (barId === 'ballisticWard') {
await actor.setFlag('my-module', 'ward.current', newValue);
ui.notifications.info(`Ballistic Ward updated: ${newValue}`);
}
});
});Registers a new custom resource bar.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
String | Yes | Unique identifier |
name |
String | Yes | Display name |
getValue |
Function | Yes | (actor) => number - Returns current value |
getMax |
Function | Yes | (actor) => number - Returns max value |
showWhen |
Function | No | (actor) => boolean - Visibility condition (default: always shown) |
editable |
Boolean | No | Allow click-to-edit (default: false) |
barColor |
String | No | CSS color (default: '#4a90e2') |
position |
Number | No | Display order, lower = earlier (default: 999) |
configButton |
Object | No | {tooltip, action} - Adds config button |
gmOnly |
Boolean | No | Only visible to GMs (default: false) |
Returns: Bar configuration object
Example:
game.customBars.registerBar({
id: 'arcaneWard',
name: 'Arcane Ward',
getValue: (actor) => actor.getFlag('dnd5e', 'arcaneWard.current') || 0,
getMax: (actor) => (actor.classes?.wizard?.system?.levels * 2) + actor.system.abilities.int.mod,
showWhen: (actor) => actor.items.some(i => i.name.includes('Abjuration')),
editable: true,
barColor: '#9b59b6',
position: 1
});Removes a registered bar.
Parameters:
id(String) - Bar identifier
Returns: Boolean - Success status
Force refresh all bars for an actor.
Parameters:
actor(Actor) - The actor to refresh
Programmatically update a bar (triggers refresh).
Parameters:
actor(Actor) - The actorbarId(String) - Bar identifiernewValue(Number) - New current value
Fired when the custom bars system is initialized and ready for registration.
Hooks.on('customBars.ready', () => {
// Register your bars here
});Fired when a user edits a bar's current value.
Parameters:
actor(Actor) - The actorbarId(String) - Bar identifiernewValue(Number) - New valueoldValue(Number) - Previous value
Hooks.on('customBars.barValueChange', async (actor, barId, newValue, oldValue) => {
if (barId === 'myBar') {
await actor.setFlag('my-module', 'value', newValue);
}
});Fired when a bar's config button is clicked.
Parameters:
actor(Actor) - The actorbarId(String) - Bar identifieraction(String) - Action name from config
Hooks.on('customBars.configClick', (actor, barId, action) => {
if (action === 'showWardConfig') {
// Show configuration dialog
}
});Hooks.on('customBars.ready', () => {
// War Resonance
game.customBars.registerBar({
id: 'warResonance',
name: 'War',
getValue: (actor) => actor.getFlag('aspects-resonance', 'aspects.war') || 0,
getMax: () => 100,
barColor: '#e74c3c',
position: 10
});
// Life Resonance
game.customBars.registerBar({
id: 'lifeResonance',
name: 'Life',
getValue: (actor) => actor.getFlag('aspects-resonance', 'aspects.life') || 0,
getMax: () => 100,
barColor: '#2ecc71',
position: 11
});
});Hooks.on('customBars.ready', () => {
game.customBars.registerBar({
id: 'companionBond',
name: 'Companion Bond',
getValue: (actor) => actor.getFlag('companions', 'bondStrength') || 0,
getMax: () => 20,
showWhen: (actor) => actor.getFlag('companions', 'hasCompanion'),
editable: true,
barColor: '#2ecc71',
configButton: {
tooltip: 'Manage Companion',
action: 'showCompanionDialog'
}
});
// Handle config button click
Hooks.on('customBars.configClick', (actor, barId, action) => {
if (action === 'showCompanionDialog') {
new Dialog({
title: 'Companion Management',
content: '<p>Companion configuration here...</p>',
buttons: { close: { label: 'Close' } }
}).render(true);
}
});
});Hooks.on('customBars.ready', () => {
game.customBars.registerBar({
id: 'corruptionLevel',
name: 'Corruption',
getValue: (actor) => actor.getFlag('my-module', 'corruption') || 0,
getMax: () => 10,
barColor: '#8b0000',
gmOnly: true // Players can't see this
});
});Bars inherit native D&D 5e styling automatically. For custom styling:
/* Target specific bar */
.custom-bar[data-bar-id="ballisticWard"] .progress {
box-shadow: 0 0 10px var(--bar-color);
}
/* Glow animation */
.custom-bar .progress[aria-valuenow]:not([aria-valuenow="0"]) {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}- Foundry VTT: v11+
- D&D 5e System: v3.0+
- Character Sheets: Legacy and v2 sheets supported
This module is designed to support:
- Ballistic Ward (Artillery Witch subclass)
- Arcane Ward (Abjuration Wizard)
- Aspectual Resonance tracking
- Companion Bond Strength
- Ki Points (custom tracking)
- Rage Uses (Barbarian)
- Any custom resource your module needs!
Repository: https://github.com/TinyDragonEgg/aspects-custom-bars
Issues: https://github.com/TinyDragonEgg/aspects-custom-bars/issues
MIT License - See LICENSE file
Created by TinyMagus for the Aspects of Verun campaign setting.
Special thanks to the Foundry VTT and D&D 5e system developers for their excellent APIs.