Introducing Odom: The Open UI Framework

Mishieck Mwale
Nerd For Tech
Published in
8 min readJun 10, 2021
Odom Cover Photo

Introduction

Any time is a bad time to be monopolistic on the web. A good web technology must be as inclusive as possible.

There are countless web technologies and the number increases significantly by the day. When developers want to use a new technology, they usually have to throw away the tools they use to make room for it. Changing tools so often and drastically gets in the way of growth of developers. Publishers of UI components, themes and plugins usually have to rewrite code to create new products that are compatible with the new technology.

Moreover, web standards are exponentially catching up with libraries and frameworks. Therefore, a technology is doomed to have a short lifespan if it does not leave a lot of room for evolution. When web standards catch up with particular technologies, developers are left with skills that are obsolete. This forces developers to learn new skills in order to survive in the industry.

Odom, a JavaScript framework for building user interfaces, aims not to replace any technology, but work with every technology. It provides a lot of great features on its own. But, let’s be realistic. No single tool is best for every task. Odom leaves a lot of room for other technologies. It can also be easily integrated into existing projects. It provides ways to override and supplement the default processes it performs.

I’m the author of the framework. I started this series in order to demonstrate how Odom works. In this part of the series, I will introduce the basic features of the framework. We will also look at what the future holds for Odom.

Features

Compatibility

Odom can render four types of assets to the DOM; components, nodes, markup and text. Integrating Odom into an existing project is as easy as inserting any of such assets into the existing DOM. The snippet below shows one of the ways to insert a DOM node into the DOM.

The node is exported from an ES module at /components/header.js. Calling the method render (one of the exports of Odom) without any parameters inserts the node into the DOM. The DOM node can be created using any method.

Flexibility

Apart from HTML, Odom allows you to use XML. You are offered two ways of converting XML to HTML. One way is by using an attribute in XML markup. On the XML element, you set the value of the attribute html to the corresponding HTML element. The other way is by providing a middleware that converts each XML element to a corresponding HTML element. The snippet below shows XML markup and the corresponding HTML markup after converting using attributes.

Odom supports supports writing styles at element level, component level and global level. At component level, global CSS can be imported from any file.

There are various ways of creating components in Odom. A component can be local to a script or module. Single-file components can be created using either ES modules or HTML files. It also provides support for multiple-file components.

Performance

Odom has very fast initial rendering. All child components of any component are created at once using concurrency/parallelism. Every component which is in the hierarchy of the component we want to render is built before rendering. At this point you are probably raising your eyebrows thinking about what would happen if any of the components took too long to be built. Odom provides a way for you to specify the maximum amount of time a component should be waited for. If the building of a component goes beyond the time limit, a placeholder is used in the DOM. When the component is ready, the placeholder is replaced with the actual component. If you have not specified the time, the placeholder will be used immediately.

Odom allows you to prefetch assets like components, text, blobs and JSON at once. The assets are made available globally and can be used when needed.

Any DOM element can be lazily loaded in Odom. This ensures that content is loaded only when needed. The snippet below shows how to apply lazy loading to an element.

As more content gets added to the DOM, Odom tries by all means to keep the performance up by reducing the amount of extra content. When you explicitly set an ID on a component, the styles for that component will be cached. When that component is created again, the styles are reused. Odom also uses event delegation in order to reduce the number of event listeners added to the DOM. When all instances of a component have been removed from the DOM, the styles for that component are discarded.

Odom provides a class for building components. The class contains utilities for creating and manipulating DOM nodes. These utilities are also used to add styling and behaviour to DOM nodes. Choosing carefully when to use the class and how to use it can result in significant performance gains.

Earlier, we looked at the different kinds of assets that Odom can insert into the DOM. If you don’t need to use the class after rendering the node, keeping it would be inefficient. You can explicitly provide a node for rendering instead of a component. But, even if you don’t, Odom will only extract the node and forget about the component. In either case the component will be claimed by the garbage collector. If all you need to do is create a DOM node from markup, using the class would be overkill. Odom allows you to provide markup for rendering. The markup will be converted into a DOM element and rendered.

In Odom, DOM mutations can be applied in a way that is friendly to performance. Whether you want to apply major or minor mutations, Odom provides a way to minimise the impact on performance.

Maintenance

Odom encourages separation of concerns. Content, presentation and behaviour are separated. Even inline styles are applied in a declarative way using JavaScript objects. When using frameworks like Bootstrap and Tailwind, markup becomes blotted with long class names and/or a lot of attributes. This makes markup quite hard to read. Some of the attributes are duplicated for similar elements. This makes it hard to maintain code. Odom provides a way to add attributes to elements in a declarative way via JavaScript. The snippets below show how a Bootstrap dropdown could be rewritten using Odom. You don’t have to be familiar with Bootstrap to understand what is happening.

Original HTML code:

Rewritten HTML code:

Only classes needed to identify the elements have been left. By looking at these classes, we can get a general idea of the presentation and behaviour of the dropdown.

JavaScript code:

All other attribute names and values not put in the markup are applied using JavaScript. CSS selectors are used to select the elements. Notice that the attribute href for every .dropdown-item is set in only one place. This makes maintaining code much easier.

Asynchronous Programming

A lot of processes on the front-end are performed asynchronously. Many a time did I pull my hair out trying to figure out how to fit asynchronous code in my synchronous code. If I was writing a function that depended on an asynchronous function, I either had to use a callback or use Promise.then, which, in the end uses a callback. This meant that I had to move some of my code into a callback. This changed the flow of the code.

If the function my code depended on could be asynchronous or not, I had to create two branches. One branch would handle the synchronous case. The other would handle the asynchronous case. This complicated the flow of the code. If another function needed the function I was writing to finish executing every step before continuing, then I had to conditionally return a promise or some other value. This would complicate the signature of the function.

Using asynchronous functions made things less complicated. If the function I was writing depended on an asynchronous function, all I had to do was use the await keyword. If the foreign function could be asynchronous or not, I used the await keyword anyway. This kept the flow of my code intact. The signature of the function was also simple because only a promise would be returned in any case.

Asynchronous programming enables us to use promises for concurrency and parallelism. Odom uses concurrency and parallelism to perform some of the tasks. This increases performance.

What’s Next?

Development Tools

Currently I’m building development tools to perform the following tasks:

  • Converting HTML files to ES modules: At the moment, HTML single-file components are converted into ES modules at runtime. The performance would be increased if this was done at build time. Another problem with HTML components is that not all relative URLs work in the module provided in the script element. Converting HTML files to ES modules at build time would solve this issue.
  • Converting XML to HTML: Converting XML to HTML using both attributes and middleware.
  • CSS Preprocessing: Converting Sass (or anything of the kind) to CSS.
  • Scoping CSS: Scoping of component level CSS. Odom supports importing CSS in component level CSS. But, imported styles are not scoped, and not all relative URLs in @import rules work. These problems could be handled by the tools.
  • Minifying: Minifying of HTML, CSS and JavaScript.
  • Template literal processing: Template literals provide an easy way to write markup and styles in JavaScript. The available build tools don’t provide a way to process code in template literals. This means that all kinds of processing must be done at runtime. This has negative effects on performance. The build tools will provide a way to apply the aforementioned kinds of processing in template literals at build time.

I hope other developers will join me in building these tools.

Dependencies

Odom currently has no dependencies. I didn’t want to add any dependency unless I was sure it was better to do so. I’d like the community to help me decide which dependencies would be best for the framework. The following are some of the dependencies I’m considering:

  • fast-xml-parser: An XML parser. This could be used to parse XML before converting to HTML. It is much faster and simpler than vanilla JS methods.
  • parawait: A library I authored. It is used to achieve concurrency and parallelism. It is more memory efficient than the vanilla JavaScript methods.
  • CSS vendor pre-fixer: I haven’t yet settled on any library for this. Odom currently adds vendor prefixes to inline styles only.

Conclusion

Odom is published on GitHub and NPM. To get started, refer to the quick start guide or the documentation. We have already looked at how Odom works well with Bootstrap and Tailwind. But, that’s just the tip of the iceberg. In the coming episodes, not only will I dive deep into the features of Odom, but also include as many technologies as I can. I’d be glad if other people saw as much value in Odom as I do. For reading up to this part, thank you.

--

--

Mishieck Mwale
Nerd For Tech

A software developer, graphics designer and open source contributor (JavaScript/TypeScript).