Web technologies are constantly evolving, it can feel overwhelming for developers with only 24 hours in a day. This week, I found some timeless library designs advice under a foundational block of the web.
During a debate a work, a teammate proposed we make a FE notification component 'self-aware'. This way, it can position itself correctly regardless of its location in the DOM or a hypothetical site's header dimensions. The header element will emit a message that the notification component can listen to and reposition itself.
The proposed solution felt off to me. It goes against my team's existing component positioning strategy – we do not emit events cross-component communication – and it feels over-engineered. Most of all, it felt to me like a solution that pushes back on the 'spirit of the web'.
I always prefer clear cerebral logic to vague gut feelings. After the meeting, I looked to conjure the feeling into words. I found the HTML Language Design Principles, one of those principles of Web development that stood the test of time – perfect learning for a lazy 24 hours-in-a-day developer like me. The design principles work as a front-end library development checklist – especially when you are building UI widgets sitting on top of HTML.
Compatibility
The first design guideline for HTML is compatibility. The term encompasses backward and forward compatibility – less arduous updates for browser developers – and compatibility with existing usage. Did you know that <br>
tags should not be closed, i.e. <br/>
is incorrect? Though both are correct because HTML is pragmatic and respectful of common usage.
What does compatibility mean for your component library? It means breaking changes should be avoided whenever possible – backward compatibility – and unexpected inputs handled with grace – forward compatibility. In concrete terms, a button component should be able to handle both an old variation name, 'primary', and its new variation name equivalent, 'main'. And when it receives an unknown variation, 'unicorn', it should throw a non-blocking error and revert to the default style instead of barfing up an error and die. Compatibility also means that instead of forcing a new usage paradigm down your user's throats, instead look for existing usage patterns and pull their cowpaths into your feature roadmap.
Although this is not the right approach to take for backend services handling customers' order data, the tradeoff could be worthwhile in the context of a front-end framework.
Utility
I can praise HTMl's many virtues, but above all, it is useful. The language once powered GeoCities fan sites also powers Electron apps like Visual Studio Code. HTML is designed to solve real-world problems, <audio>
and <video>
were introduced so we can consume more media on this platform once used only for academic documents. As someone who still dabbles in academic documents, I am looking forward to full <math>
support in the not too distant future.
As library designers, we too should aim to solve real-life problems above all. It is more important to provide ugly solutions to actual use cases than elegant solutions that solve 'almost but not quite real' use cases. Of course, worst of all is spending days working out new features that satisfy the programmers' – in some cases product managers' – vanity and curiosity instead of a widespread problem. The last is especially tempting, I am guilty of leaping into the technical weeds for a shiny hare of a problem over mowing the backlog of actual user needs on more than one occasion.
A utility-first approach to library development does not equal an ugly codebase. Refactoring and clean-ups have their rightful place in a developer's day – they make adding future features easier. But keep in mind that no one recommends a library for its architectural purity. Above all, your code should do its job.
Interoperability
Imagine a world where HTML is not interoperable. Each browser would have a slightly different implementation and we got Github and Chrome flavored HTML – they sound like multiverse villains in a programming superhero movie. Interoperable code is the simplest code that does the job. Its behavior is well defined and its errors recoverable, it can be used and reasoned easily. Its shortcomings are handled with grace.
I like to think my libraries have well-defined behavior, but I know I love to use libraries that are simple to reason about. When it comes to front-end frameworks, I especially encourage Typescript for this reason. Clear method signatures do wonders for interoperability.
While simplicity is key to interoperability, the framework should only be as simple as necessary to do the job well. Simplicity can conflict with other principles, and we should not use it as an excuse to compromise on quality. As an example, front-end components often have complex accessibility-related behaviors and attributes to satisfy the Universal Access principle.
Universal Access
My introduction to the web was not through newsfeeds, funny videos, or even video games. I got to know the internet as the place I got to read Chinese martial fantasy novels. If HTML was only designed to display the Latin alphabet for a western audience, I would not be a web developer today. And if HTML is not designed with universal access in mind, the smartphone revolution might never have occurred.
What does universal access mean for front-end developers? It means more than just creating responsive web applications. It means we should test our frameworks using different languages and other writing modes. Instead of paying lip service to the benefits of accessibility, change one of your browser's default font size to something other than 16px and regularly navigate the web using a screen reader.
I applied the HTML principles checklist to the component library at my company and found room for improvement in the Interoperability and Compatibility areas. While I can't make my libraries as durable and flexible as HTML in the short term, I'll be pushing for a team-wide adoption of these design guidelines. How do your FE libraries measure?
Top comments (0)