b8r v2

b8r v2 is going to adopt web-components over components and import over require

Having developed b8r in a period of about two weeks after leaving Facebook (it was essentially a distillation / brain-dump of all the opinions I had formed about writing front-end code while working there), I’ve just spent about two years building a pretty solid social media application using it.

But, times change. I’m done with social media, and the question is, whither b8r?

In the last few weeks of my previous job, one of my colleagues paid me a huge compliment, he said I’d “really hit it out of the park” with b8r. We’d just rebuilt half the user interface of our application, replacing some views completely and significantly redesigning others, in two weeks, with one of the three front-end coders on leave.

I designed b8r to afford writing complex applications quickly, turn hacky code into solid code without major disruption, make maintenance and debugging as easy as possible, and to allow new programmers to quickly grok existing code. As far as I can tell, it seems to do the trick. But it’s not perfect.

Two significant things have emerged in the front-end world in the last few years: import and web-components (custom DOM elements in particular)

import

When I wrote b8r, I found the various implementations of require to be so annoying (and mutually incompatible) I wrote my own, and it represents quite an investment in effort since then (e.g. I ended up writing an entire packaging system because of it).

Switching to import seems like a no-brainer, even if it won’t be painless (for various reasons, import is pretty inimical to CommonJS and not many third-party libraries are import-friendly, and it appears to be impossible for a given javascript file to be compatible with both import and require).

I experimentally converted all of the b8r core over to using import in an afternoon — enough that it could pass all the unit tests, although it couldn’t load the documentation system because any component that uses require or require.lazy won’t work.

Which got me to thinking about…

web-components

I’ve been struggling with improving the b8r’s component architecture. The most important thing I wanted was for components to naturally provide a controller (in effect, for components to be instances of a class), and pave some good paths and wall up some bad ones. But, after several abortive attempts and then thinking about the switch from require to import, I’ve decided to double-down on web-components. The great thing about web-components is that they have all the virtues I want from v2 components and absolutely no dependency on b8r.

I’ve already added a convenience library called web-components.js. You can check it out along with a few sample components. The library makes it relatively easy to implement custom DOM elements, and provides an economical javascript idiom for creating DOM elements that doesn’t involve JSX or other atrocities.

Using this library you can write code like this (this code generates the internals of one of the example components):

fragment(
div({classes: ['selection']}),
div({content: '▾', classes: ['indicator']}),
div({classes: ['menu'], content: slot()}),
)

I think it’s competitive with JSX while not having any of the dependencies (such as requiring a transpile cycle for starters).

<div className={'selection'}></div>
<div className={'indicator'}>▾</div>
<div className={'menu'}>
{...props.children}
</div>

To see just how lean a component implemented using this library can be, you can compare the new switch component to the old CSS-based version.

Aside — An Interesting Advantage of Web Components

One of the interesting properties of web-components is that internally the only part of the DOM they need to care about is whether they have a child <slot>. Web-components don’t need to use the DOM at all except for purposes of managing hierarchical relationships. (Annoyingly, web-components cannot be self-closing tags. You can’t even explicitly self-close them.)

E.g. imagine a web-component that creates a WebGL context and child components that render into container’s scene description.

In several cases while writing b8r examples I really wanted to be able to have abstract components (e.g. the asteroids in the asteroids example or the character model in the threejs example). This is something that can easily be done with web-components but is impossible with b8r’s HTML-centric components. It would be perfectly viable to build a component library that renders itself as WebGL or SVG.

Styling Custom Elements for Fun and Profit

One of the open questions about custom DOM elements is how to allow them to be styled. So far, I’ve seen one article suggesting subclassing, which seems to me like a Bad Idea.

I’m currently leaning towards one or both of (a) making widgets as generic and granular as possible (e.g. implement a custom <select> and a custom <option> and let them be styled from “outside”) and (b) when necessary driving styles via CSS variables (e.g. you might have a widget named foo that has a border, and give it a generic name (–widget-border-color), specific name (–foo-border-color), and a default to fall back to.

So, in essence, b8r v2 will be smaller and simpler — because it’s going to be b8r minus require and components. You won’t need components, because you’ll have web-components. You won’t need require because you’ll have import. I also plan one more significant change in b8r v2 — it will be a proper node package, so you can manage it with npm and yarn and so forth.

<b8r-bindery>

One idea that I’m toying with is to make b8r itself “just a component”. Basically, you’d get a b8r component that you simply stick anywhere in the DOM and you’d get b8r’s core functionality.

In essence the bindery’s value — presumably an object — becomes accessible (via paths) to all descendants, and the component handles all events the usual way.

I’m also toying with the idea of supporting Redux (rather than b8r’s finer-grained two-way bindings). There’s probably not much to do here — just get Redux to populate a bindery and then instead of the tedious passing of objects from parent-to-child-to-grandchild that characterizes React-Redux coding you can simply bind to paths and get on with your life.

Summing Up

After two years, I’m still pretty happy with b8r. Over the next year or so I hope to make it more interoperable (“just another package”), and to migrate it from (its) require and HTML components to import and web-components. Presumably, we’ll have import working in node and (and hence Electron and nwjs) by then.

Happy new year!