DEV Community

Cover image for bpmn-visualization: All you need to know about styling BPMN elements
Nour Assy for Process Analytics

Posted on • Originally published at Medium

bpmn-visualization: All you need to know about styling BPMN elements

In this article, we're going to dive deep into the capabilities of the bpmn-visualization Typescript library, with a specific focus on managing styles. You'll learn how to use its APIs to set styles and, when necessary, revert them back to their default state. Additionally, we'll feature some advanced usage scenarios to provide you with inspiration for your own use.

If you're just getting started with bpmn-visualization, we recommend that you take a look at our Getting Started tutorial. It provides a step-by-step guide on how to set up and begin using the library.

You can try out and experiment with the APIs in one of our preconfigured live environments, available here (see the 'Miscellaneous' section).

So let's dive in 🏊!

Setting styles in bpmn-visualization

There are two primary ways to go about setting styles of BPMN elements: via CSS or programmatically. Let's delve into these two methods.

Styling with CSS: The addCssClasses API

The first method involves CSS, the cornerstone technology used to apply stylistic choices to elements on a web page. To use CSS for styling your BPMN elements, bpmn-visualization provides the addCssClasses API. This method is especially useful when you've already defined CSS class names that you wish to apply to your BPMN elements.

In order to use addCssClasses, begin by defining your styles in your stylesheet, assigning a unique class name to each style. You can then apply these classes to your BPMN elements using the API.

To illustrate, let's consider an example where we're styling activities to indicate an error state. Here's how you could define such a CSS class in your CSS file:

/* Define your CSS class in your CSS file */ 
/* CSS class for an activity in error */ 
.bpmn-activity-error > rect { 
  fill: Tomato !important; 
  fill-opacity: 15% !important; 
  stroke: Tomato; 
}
Enter fullscreen mode Exit fullscreen mode

Once you've defined your CSS class, apply it in your JavaScript or TypeScript code. This can be done by calling the addCssClasses API, which accepts the IDs of one or multiple BPMN elements, along with the CSS classes to be applied. Here's an example of how you can add the bpmn-activity-error class to two different activities:

// Create a new instance of BpmnVisualization
const bpmnVisualization = new BpmnVisualization(...);

// Load your BPMN diagram
bpmnVisualization.load(...);

// Apply the CSS class to a BPMN element
bpmnVisualization.bpmnElementsRegistry.addCssClasses(['task_id1', 'task_id2'], 'bpmn-activity-error');
Enter fullscreen mode Exit fullscreen mode

For a more in-depth explanation of styling BPMN elements through CSS, be sure to check out our Getting Started tutorial.

Dynamic styling: The updateStyle API

There might be cases where the style of a BPMN element is not known beforehand, or needs to change dynamically based on certain conditions. In these cases, the ability to programmatically apply styles is necessary.

This is where the updateStyle API comes into play. The updateStyle API allows you to alter the style of BPMN elements directly within your code. This offers the flexibility to adjust styles instantly and responsively.

Much like the addCssClasses method, updateStyle accepts the identifiers of one or more BPMN elements, along with a set of style properties defined through the StyleUpdate type. The properties that can be adjusted are dependent on the type of BPMN element: edge or shape (e.g., activities, events, gateways, etc.).

Let's consider the following example:

bpmnVisualization.bpmnElementsRegistry.updateStyle(
  ['task_id1', 'task_id2'], { 
    fill: { color: "blue", opacity: 15 }, 
    font: { opacity: 40 }, 
    stroke: { color: "blue", width: 3 }, 
    opacity: 30
});
Enter fullscreen mode Exit fullscreen mode

In this instance, the style is directly applied to the BPMN elements identified by task_id1 and task_id2.

Resetting styles in bpmn-visualization

Inevitably, there will be cases where you want to reset the styles applied to your BPMN elements, either returning them to their default state or preparing them for a new style. bpmn-visualization provides easy-to-use APIs for this as well, mirroring the ways you can set styles.

Resetting CSS classes: The removeCssClasses API

You can easily remove classes for styles that have been applied using CSS with the addCssClasses API, by using the removeCssClasses API, providing the IDs of the BPMN elements, and providing the names of the CSS classes you want to remove. If you want to remove all CSS classes, you can do so with a call to removeAllCssClasses.

Here's an example:

// Remove one or more CSS classes
bpmnVisualization.bpmnElementsRegistry.removeCssClasses(['task_id1', 'task_id2'], ['bpmn-activity-error']);

// Remove all CSS classes
bpmnVisualization.bpmnElementsRegistry.removeAllCssClasses(['task_id1', 'task_id2']);
Enter fullscreen mode Exit fullscreen mode

Resetting dynamic styles: The resetStyle and updateStyle APIs

bpmn-visualization provides a couple of options for programmatically applied styles. If you want to completely reset the style of a BPMN element, you can use the resetStyle API. Just pass the IDs of the BPMN elements to this API and it will clear all style settings applied through the updateStyleAPI. Here's an example:

// Reset the style of a BPMN element 
bpmnVisualization.bpmnElementsRegistry.resetStyle(['task_id1', 'task_id2']);
Enter fullscreen mode Exit fullscreen mode

If, however, you want to reset some specific style properties while leaving others intact, you can use the updateStyle API. Instead of setting the property to a specific value, you pass the value as default. Here's an example:

// Reset specific style properties of a BPMN element
bpmnVisualization.bpmnElementsRegistry.updateStyle('task_id1', { 
  fill: {color: 'default', opacity: 'default'},
  stroke: 'default' 
});
Enter fullscreen mode Exit fullscreen mode

In this case, the fill and stroke properties are reset to their default values, while other style settings remain unchanged.

And that's it for the basics of bpmn-visualization's styling APIs. In the next section, we'll be looking at some more advanced usage scenarios, so keep reading 👌

Advanced usage scenarios

Beyond the basics, bpmn-visualization can be used in a multitude of scenarios to address your specific needs. To illustrate this, we'll examine two practical applications of the library: interactive styling and data-driven styling.

Interactive styling

Interactive styling brings your BPMN diagrams to life by enabling them to respond to user actions. This could include actions like highlighting an element on hover, changing the style upon clicking, or indicating the active task in a process.

Our team has prepared different demos that showcase how to implement interactive styling using bpmn-visualization. Although the library does not currently support attaching event listeners directly to BPMN elements, we can still achieve interactivity by accessing the corresponding HTML elements.

Here's a code snippet that shows how to change the style of a BPMN element while the mouse is hovered on it, as it is done in the Draw me a path demo:

// Retrieve the BPMN element using its ID
const bpmnElementId = 'task_id';
const bpmnElement= bpmnVisualization.bpmnElementsRegistry.getElementsByIds(bpmnElementId)[0];

// Retrieve the corresponding HTML element
const htmlElement = bpmnElement.htmlElement;

// Add a mouse event listeners to the HTML element
htmlElement.onmouseenter = () => {
    // Change the style of the corresponding BPMN element
    bpmnVisualization.bpmnElementsRegistry.updateStyle(bpmnElementId, {
     stroke: 'MediumVioletRed',
     font: {color: 'MediumVioletRed'}
    });
};

htmlElement.onmouseleave = () => {
    // Reset the style of the corresponding BPMN element
    bpmnVisualization.bpmnElementsRegistry.resetStyle(bpmnElementId);
};
Enter fullscreen mode Exit fullscreen mode

In this snippet, we first retrieve the BPMN element using its ID by calling getElementsByIds. We then access the corresponding HTML element. After this, we add hover event listeners to this HTML element. As a result, when the mouse hovers over this HTML element, the style of the corresponding BPMN element changes. As soon as the mouse leaves, the style resets to its original state.

Below is a screenshot from the demo illustrating the final outcome:

Example of interactive styling

For more inspiration on interactive styling use cases, we invite you to explore the additional interactive features in our Draw me a path and Bonita Day 2023 demos. You might also be interested in checking out an example of integration with the Vue framework, contributed by our amazing community. Happy exploring! 🤗

Data-driven styling

Data-driven styling allows you to customize the appearance of BPMN diagrams based on associated data. This can be particularly useful when you want to display certain characteristics or metrics related to your process, such as frequency or performance statistics, directly on your BPMN diagrams.

For example, consider one of our demos that integrates the bpmn-visualization library with pm4py. In this demo, we perform process discovery, compute frequency of execution statistics, carry out conformance checking, and then visualize the results using bpmn-visualization.

One interesting aspect of this demo is how it handles the computation and visualization of process statistics. The first step involves using the scaleSequential function from the D3 library to map frequency statistics to a color gradient, ranging from light blue to dark blue as shown in the code snippet below:

// Create a D3 scale to map frequency to a color
const colorScale = d3.scaleSequential()
  .domain([0, 1]) // Initialize domain with dummy values
  .interpolator(d3.interpolatePuBu);
Enter fullscreen mode Exit fullscreen mode

Next, we use the fetch API to retrieve frequency statistics computed in the backend by pm4py. After obtaining these statistics, we call the visualizeFrequency function to visualize the results on our BPMN diagram:

// Compute frequency statistics using pm4py in the backend
fetch(`${apiUrl}/stats/frequency`)
    .then(response => response.json())
    .then(data => visualizeFrequency(data))
    .catch(error => console.log(error));
Enter fullscreen mode Exit fullscreen mode

For clarity, let's examine key portions of the visualizeFrequency function. In the code snippet below, we show how this function updates the fill color and the color of labels for activities based on their frequency of execution:

// Function to visualize frequency data on BPMN elements
function visualizeFrequency(stats) {
    // Update the domain of colorScale with the actual values
    colorScale.domain([0, d3.max(Object.values(stats))]);
    // Compute the mean of frequencies
    const avg = d3.mean(Object.values(stats))];

    // Update the color of activities based on their frequency
    for (const [elementId, frequency] of Object.entries(stats)) {
        // isActivity is a function that returns true if elementId corresponds to an activity
        if (isActivity(elementId){
           const fillColor = colorScale(frequency);
           const fontColor = frequency > avg ? 'white' : 'default';

           bpmnVisualization.bpmnElementsRegistry.updateStyle(elementId,{
               fill: { color: fillColor },
               font: { color: fontColor }
           });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Below is a screenshot from the demo that displays the end result of these manipulations:

Example of data-driven styling

Conclusion

That wraps up our overview of style management in bpmn-visualization. If you're interested in learning more or looking for some inspiration, feel free to check out our bpmn-visualization-examples repository on GitHub. Have fun on your learning journey!

If you want to stay on top of the latest news and releases from the Process Analytics project, follow us through:

Top comments (0)