Most often, web UI libraries fall under one of two categories. They may be part of a “framework”, or a grouping of UI components that share an appearance, a similar API, or are otherwise cohesive. Another category is usually the standalone library. Regardless of whether a standalone library depends on jQuery (I’m singling out this scenario because of its prevalence), they aren’t directly related to other UI components, which means they set their own terms with regard to API, appearance, and other conventions. Components that are part of a framework are usually hard to integrate with anything that’s not part of said framework, resulting in vendor lock-in. Components that aren’t part of a framework tend to be easier to integrate with other things, but most often they’re not designed to that effect. Composability is a much better alternative, and something we’ll explore in depth in this article.
Before I go any further,
dragula now has over 7500 stars on GitHub and was even featured on The Next Web! I’ll need a few moments to wrap my head around that! Wow.
If you’ve never seen
dragula, check out the demo to get an idea of what it does.
Instead of having an autocomplete, a drop down list, a combo box, and a select box, it’s nice when you can do all of that with a single component. That’s reusability, you get to use the same component for many different use cases. Another form of reusability is composability, the ability to integrate a few different components into a cohesive user experience. It’s an often overlooked factor when developing UI components, but I think good UI components must be highly composable.
Composability can also go the opposite direction. Some programs are notoriously modular, and it gets quite a few bits and pieces (otherwise called “modules”) to get to something that “works”. One such example is
woofmark. This library provides an editor on top of browser HTML
<textarea> elements. I’m not using
woofmark on ponyfoo quite yet, because I haven’t had the time to integrate it here, but I did implement some of the underlying modules. For instance,
megamark under the hood to parse Markdown strings into HTML strings. Megamark is really an abstraction layer on top of
markdown-it, which adds a couple of minor niceties such as syntax highlighting in code blocks, and the ability to use <mark> DOM elements to highlight a “fancy” piece of text. Woofmark also uses
domador to convert DOM trees or HTML strings back to Markdown, and that has a extensibility structure in place allowing one to extend their understanding of how HTML should be converted into Markdown. If you had custom Markdown -> HTML directives, it’s only logical that you implement the reverse for a HTML -> Markdown conversion. With all this converting going on, it’s probably
insane to try and sanitize the inputs and outputs on your own, so a specialized library takes care of sanitization at the HTML level.
I’ve already mentioned a few modules, each in charge of a different portion of the rich-editing experience in
woofmark. Here’s a list of the modules I’ve mentioned, plus a few others even deeper in the dependency chain.
markdown-it is one of the lowest-level modules used in this use case, and it parses CommonMark-compliant markdown into HTML strings
megamark is a wrapper around
markdown-it which ties it with
highlight.js, a syntax highlighting module and
insane, the HTML sanitizer
domador parses HTML or DOM nodes back into Markdown, and can be extended to match the extensions provided to
megamark, so that the output stays consistent both ways
woofmark ties everything together and provides a nice
<textarea> upgrade that allows entering Markdown, HTML, and WYSIWYG input in exchange for plain Markdown
- Plenty of other low-level modules are at work, such as
crossvent for dealing with DOM events in a cross-browser manner;
he, which deals with unicode; and
jsdom, which deals with creating a
window context on the server-side; and many others
As you can probably imagine, trying and cramming all of this functionality into a single module would be a dreadful endeavour, not to mention a waste of productivity, time, and hence, ultimately, money. In contrast, keeping the functionality in separate modules enables us to reuse them across our stack, across our projects, and out on the open-source world, where people find it way easier to contribute patches if the code is small, self-contained, and does one thing well.