Custom components with Shadow DOM-Now available in Gameface!
news
1/20/2025
Martin Bozhilov
Learn how to create game inventory with the newly added Shadow DOM feature in Gameface!
Overview
With version 1.61, Gameface now has Shadow DOM
support for custom elements ,
bringing a robust way to encapsulate styles and markup.
Shadow DOM is a core feature of the modern Web Components standard, providing a mechanism to
attach a hidden DOM tree to an element—effectively shielding the element’s structure, style, and behavior from the rest of the page.
This encapsulation ensures that the component’s internal layout and styles are self-contained and do not leak or clash with other parts of the application.
Usage
To showcase the power and usability of the Shadow DOM,
we will create a simple inventory menu with custom elements.
In this inventory, players will be able to:
Open/close the inventory
View an item’s description
Equip or consume an item
The entire UI will be built with custom elements, using only vanilla JavaScript features.
Project setup
For this project, we are going to utilize data binding .
We’ll also mock game data with a model containing all the necessary information for the inventory.
Click to reveal model
The inventory items will have an id, name and path to an image.
Additionally, we wil define a field with the number of available inventory slots.
Inventory component
We’ll start with the main UI component: the inventory component.
In your project, create a folder named game-inventory and place a script.js file inside it. We’ll use a dedicated folder for each component to keep the project organized.
Let’s initialize the game-inventory component:
To enable the Shadow DOM, we must attach the shadow root to our custom element.
Adding markup to the shadow root
There are several ways to add HTML to your custom elements.
We’ll use a straightforward approach: keep the HTML and styles in the script as a string, then append them to the Shadow DOM after initialization.
Let’s create an inventoryTemplate variable and assign it the element’s markup.
Now in the connectedCallback method—which is called when the component is added to the DOM—we’ll append the template to the Shadow DOM:
We’ve also added a onTemplateLoaded method, where we will initialize the component’s logic after the HTML has been attached.
That’s it for the element’s markup! Thanks to the Shadow DOM, the component’s styles and markup are now encapsulated inside the game-inventory element.
Component Logic
To complete the inventory we’ll need to add the following methods.
addInventoryItems - adds the items from the InventoryItems model to the Inventory
addInventoryItem - adds a single item
addItemAt - adds an item at a specific slot
findFreeSocketId - finds the id of a socket that has no item in it
isSocketFree - checks if a socket has an item in it
toggle - shows/hiddes the inventory
onTemplateLoaded - Inits inventory logic
Click to view source
The rest of the components
We’ll create the other custom elements in this UI using the same approach: defining a template string and appending it to the element’s Shadow DOM (where applicable).
Inventory item
The inventory item will have a details panel that opens on click.
Inventory items will of two types - consumable and weapon. Where the consumable item will have quantity and the weapon - won’t.
We’ll be making new components for the weapon and consumable that will be wrapped in an inventory-item in order to share functionality.
The inventory-item will populate itself with either a weapon or a consumable depending on the type of the current item.
Note: The inventory-item component doesn’t contain its own markup since it acts solely as a functionality wrapper, so we won’t use the Shadow DOM there.
Click to reveal component’s implementation
We will define the following methods:
setup - Will collect the item’s info and create the child component depending on it’s category
createModal - Creates the modal element that will show the details of each inventory item
showDetailsModal - Creates the modal and appends it to the page with the correct item’s information
Inventory weapon
The weapon component is going to have logic for setting up the content - the image and the description and a method for equipping. We will set up the content and attach the event listeners in the connectedCallback.
Apart from that we’ll also need to call engine.synchronizeModels() to make sure that the
data binding attributes are updated.
We will define the following methods:
setupContent - Sets the html content of the weapon item and sets the data-binding attributes.
equip - Equips the weapon by setting its equipped property to true.
Remember, because elements in the Shadow DOM are scoped to that shadow root, we must query them with this.shadowRoot.querySelector instead of the global document.querySelector.
Inventory consumable
Similar to the weapon component, the consumable component is also an inventory item, but the right-click interaction uses one of the consumables instead of equipping it.
Its template differs slightly by including a quantity badge.
We will define the following methods:
setupContent - Sets the HTML content of the consumable item and sets the data binding attributes.
use - Decreases the consumable’s quantity by 1 when used.
Click to reveal component’s implementation
Bringing it all together
After setting up our custom components, the last step is to bring everything together.
First, create an index.html file and import the cohtml.js file, the previously created model, and all your custom elements:
Next, let’s create a button to toggle the inventory, as well as add our game-inventory element—the main entry point for this project’s logic:
Finally, add some basic styles for the toggle button and the modal element:
With that, our custom-elements-based game inventory UI is finished!
In conclusion
By leveraging custom elements and the Shadow DOM, building modular UI components becomes both simpler and more robust.
The level of encapsulation and reusability custom elements provide means you can confidently scale your project and reuse all elements
without worrying about conflicting styles or tangled logic.
Sample location
You can find the complete sample source within the ${Gameface package}/Samples/uiresources/ComponentsWithShadowDOM directory.