Skandha: generic interacting behaviours
May 2020, by Maarten Nieber
Introduction

Goals

In this article I will explain how we can add generic behaviours (such as selection, filtering, drag-and-drop) to a user interface. These behaviours can have interactions that the UI components are agnostic about.

Source code

The source code that this approach is based on is found in the following npm packages:

Sample code

There is a sample application, annotated with comments, that explains the approach. Enter the following commands to run it:

git clone git@github.com:mnieber/blog-sample-apps.git
cd blog-sample-apps
yarn skandha-sample
# Open the browser at http://localhost:3000

Caveats

The approach presented in this article is experimental. It has worked well for me in practice, but I expect that for many people it's too elaborate to consider using it. That said, I hope you find this article interesting for the ideas that it puts forward.

Rationale and outlook

Creating a UI takes a lot of effort, but we can save time by using existing solutions. These solutions can help us in two major ways:

  • they render components;
  • they add behaviour to these components.

Solution that take care of both rendering and behaviour (we can call these solutions "frameworks") tend to produce mixed results. If the solution covers the requirements, then it can be very efficient. However, if the solution must be adapted to get the right result then it can lead to frustration, because it forces the developer to "fight the framework".

To avoid the risks that come with choosing a framework, you can choose:

  • a UI kit for rendering (such as DaisyUI, TailwindUI, Flowbite), and
  • a headless UI solution (such as Adobe Aria, Radix, HeadlessUI) for storing the state of a user-interaction. The headless UI takes care of processing mouse- and keyboard-events, such as the mouse-click on a list item.

This article shows how to take this one step further by also considering the interactions between behaviours. The first section introduces the concepts of containers and facets. Each facet represents a behaviour, and we can connect facets by mapping data between them. The next section explains how to install callback functions in each facet to assist with the implementation of the different behaviours. These callback functions allow us to create interactions between the behaviours. In the final section, I will discuss the pros and cons of this approach.