Applet UX Guidelines & Dev Tips

Tips on theming, choosing components, and more

There's a few basic tenets to developing Applet UIs in the NH-Launcher. The first is understanding the environment. Each app is loaded into a Lit component with its own shadow DOM. This provides a bit of isolation in terms of CSS styling. However, CSS variables do leak through to children and can provide an interface for theming.

Usage of Lit & Web Components

Each application needs at least one top level Lit component. This creates the shadow DOM for the web component. Below this top level Lit component, each application can use whichever framework they want to render themselves. That framework must support being passed an HTMLElement to use as the root for rendering.

This means you are free to use React. However, there are potential issues that may crop up when mixing and matching bits of Lit and React. This is noticeable when using the lit-svelte-stores (if you do run into this issue, there's always react-store-adaptors).

Their may be many other libraries that are compatible with being embedded in a specific HTMLElement. We don't have a list of compatible frameworks yet. If you know of any off the top of your head or try any and find they work, please let us know.

If you have your own styles that need to be loaded into the :host scope of the CSS, then you should wrap it as a Lit CSS module. There's an example of this in the Material Components Github repo. This is necessary because Lit requires CSS to be processed by a JS library to implement a Shadow DOM CSS polyfill.

Of course, you are free to use Lit to build out the entirety of your applet. You can also just use Web Components and vanilla JavaScript. It's up to you.

Since we plan to provide themes for Shoelace and Material Components, you may want to choose one of these libraries to help speed along your app development.

Scoping

When building applications out of large sets of components and applets authored by many different people and organizations, the possibility of an unintended interaction increases. This means that each component and applet should have its own scope for both itself and its dependencies. This applies to both HTML, CSS, and the code behind each applet and component.

To this end there are several things that can help:

  1. @lit-labs/scoped-registry-mixinScopedRegistryHost — Provides a simple LitElement mixin to enable using multiple versions of web components as long as the @webcomponents/scoped-custom-element-registry polyfill (or compatible) is loaded.

  2. Follow the conventions in the applet-template repo. This includes using Lit and the ScopedRegistryHost mixin.

  3. Additionally, CSS is scoped to your component by following the Web Component/Lit convention.

  4. There are scoped versions of Shoelace and Material Components, but there may be issues with styling them. We may need to provide our own scoped versions of Material Components.

Note

I found that the ScopedRegistryHost works with newer versions of Lit than the older @open-wc/scoped-elements ScopedElementsMixin.

Theming

We currently working on getting design tokens implemented. In the meantime, this section is slightly more aspirational than the others.

There is a repo which will have all of the CSS variables for our light and dark themes as well as basic typography. This will provide JS modules for use with Lit. Using these should be as simple as importing the module and using the Lit CSS in your component.

Our most basic goals with this system are:

  1. The underlying "We" code uses Lit and scoped components. This means certain bits of CSS need to be loaded as TypeScript modules.

  2. There must be standard variables defined that apps can use.

  3. It should be easy to style new component libraries using the defined styles.

  4. There should be a few "adaptors" from our theme to Shoelace, Material, and maybe a few others so apps can use those libraries and automatically have them properly styled.

  5. Other developers should be able to use our design tokens and write an "adaptor" for other component libraries and design systems.

  6. Each independent app can use as little or as much of the Neighbourhoods style as it wants.

Please check back later for more information on our design system.

Accessibility

Around the world, about 2.2 billion people (or about 27% of the global population) suffer from some visual impairment. There are 2.5 billion (about 31%) globally who require assistive technologies, some of which may impact how they use an application. The same number have hearing disabilities. Globally ~10% of people have ADHD. Anywhere between 5-9% of people have learning disabilities. Approximately 2.9% of people have intellectual disabilities.

Obviously, this is a wide gamut of capabilities which need to be kept in mind. Fortunately, a lot of people have been working on various guidelines for web applications. The basic recommendations that people have come to (WAI-ARIA) only add a small amount of overhead and increase the usability of the apps to a wider gamut of users. Unfortunately, technology companies and start-ups can oftentimes ignore these issues in the rush to deliver a product that a small group of users can use.

Notes

There are potential performance and rendering pitfalls when using the lit-svelte-stores Library. Some of these are mentioned in the Readme. Some are a little less obvious. If you are using the library with lot of data that might change frequently, then care must taken to make sure that only what changed is updated. This means creating many derived stores for each potential changing bit of data and potentially having separate components for rendering each bit of data each with their own ReactiveController from lit-svelte-stores.

For instance, you might have a table with many updates happening over Holochain signals, and you might not want to update the entire table each time just one cell changes. This means splitting the store for all of the rows into one store per row and even into one store per column per row. Each cell would have a Lit component that gets a store corresponding to its data. Each time that specific store updates, only that cell would rerender.

Last updated