Talks

Web Components: Designing Frontends for Reusability

Have you ever tried to develop front end code that can be easily used in multiple projects? Reinventing the wheel is no fun. In this talk, I will use an example to share my design process for developing web components that are accessible, pretty, and most importantly easy to reuse.

By Joy Heron https://twitter.com/@iamjoyheron

Joy Heron is a consultant at INNOQ and develops software as a full-stack developer. She is passionate about developing responsive web applications using progressive enhancement and loves learning new things. Sketchnotes are a hobby.

https://rubyonice.com/speakers/joy_heron

Ruby on Ice 2019

00:00:11.740 Our next speaker is a consultant at INNOQ and a full-stack developer. She is also an amazing sketchnote artist, and you can follow her on Twitter to see the incredible sketchnotes she has been drawing for this conference. Today, she is going to show us her design process aimed at reducing the time spent setting up new front-end projects. Her quest is to create reusable, accessible, and, most importantly, beautiful web components. Please welcome to the stage Joy Heron with her talk on Web Components: Designing Frontends for Usability.
00:01:01.460 Hi, everybody! Is my mic working? I'm really happy to be here today. My name is Joy Heron, and today I'm going to share with you all my journey with web components.
00:01:18.960 When I was starting out as a software developer, I considered myself part of the Stack Overflow generation. This meant that whenever I encountered a problem, I would simply look it up on Stack Overflow and figure out how to resolve it. However, the issue with this approach was that I often asked the wrong questions. I would ask 'How can I do this?' rather than 'Why do I want to do this?' or even 'Is there a better way to achieve my goal?' As a result, during my early days, I started developing web applications but found front-end development to be extremely frustrating and irritating. I vowed never to do it again.
00:01:52.290 However, as I grew as a software developer and learned more about how the web works, I fell in love with it all over again. I began to develop a system to conceptualize front-end user interfaces, which helped me design them in a way that minimized my frustration. I want to share this design process with you all today, as it addresses the problems we face when creating user interfaces and translating them into code.
00:02:49.319 My journey began in the spring of 2016 when I was tasked with developing a web application that required something resembling a table. This table needed text fields for filtering the data and sorting different columns. I was working with a Neo4j database, but that is not the main point. After completing that project, I thought to myself that I had learned something valuable that I might use again. Just three months later, I was assigned a completely different project where I had to create a web application displaying data in an HTML table with filter criteria.
00:03:16.670 In the spring of 2017, I faced the same situation: developing a web application presenting data as a table with filtering options. This time, I was dealing with a Cassandra datastore—though I must say, never use Cassandra for this use case. If you're curious why, feel free to ask me during the break. In the winter of 2017, I was again asked to implement an HTML table with filtering and sorting functionalities, but relying on a Solr datastore. Solr works excellently for this task. Then, in the fall of 2018, in a completely different domain, I was required to implement an HTML table yet again, with similar filtering and sorting capabilities. By that point, I started questioning why I was continually implementing the same functionality in nearly every project I worked on.
00:05:03.610 As a full-stack developer, I realized that in the back end, I wouldn’t allow myself to continually reinvent the wheel. When I identified functionality I had reused multiple times, I would copy it just a couple of times to see if I was really reinventing the wheel or if I could identify a common abstraction. After realizing this similarity across implementations, I would create something reusable to avoid starting from scratch every time.
00:05:58.000 We can adopt the same approach in front-end development, specifically by utilizing web components, which I will elaborate on later. To understand what abstraction is possible in this context, you can see on the right side a front-end component I've repeated multiple times on the slide. This indicates that there is a similarity across the different applications. When I type in these filter boxes, the goal is to send an HTTP request to the server with the query parameters needed to filter the data.
00:06:58.000 The server will filter the data and return a new table, allowing me to create an interactive component. On the left hand side, we can imagine a small server in a Rails app; it's the index where we already have a table of data. We can extend it with a few filter parameters to achieve the desired functionality.
00:07:11.260 This led me to the process of building this web component, which I named 'Bela.' For those who understand German, 'Bela' translates to 'pretty table.' Today, I aim to draw upon my experiences and my design process for developing web components to illustrate this concept through a visual example.
00:08:30.850 So, the first question that arises is, what is a web component? The foundation of every web component is HTML markup, and my primary focus when developing HTML is making it functional for all users. This includes not just those using a mouse, but also individuals utilizing a screen reader or navigating via a keyboard. It's entirely possible to create websites using only HTML, which is pretty remarkable, but we often include CSS in our web components to ensure they look appealing and do not resemble a web page from the 90s.
00:09:34.630 In addition to HTML and CSS, we can add JavaScript to enhance our web components, introducing interactivity and functionality that may otherwise be absent. However, it's crucial to emphasize that when designing our web components, we should always start with HTML. Regardless of whether we're rendering our components on the client-side, the final output is still HTML and CSS, which users interact with. We never engage with JavaScript directly; our interaction is always through the HTML markup we have created.
00:10:40.120 There may be some confusion regarding the definition of web components, as they consist of HTML, CSS, and JavaScript. The web component specifications for JavaScript include custom elements, shadow DOM, and template, while some portions are already deprecated. While all of these elements are beneficial, I believe it's essential to point out that the true foundation of web components resides in HTML. When developing web components, we want to adhere to the Single Responsibility Principle.
00:11:08.320 Each component should perform a single function, and if additional functionality is required, we can create a new component or encapsulate related components within one another. This modular approach allows for natural composition in HTML since it is a markup language that uses parent tags containing their children. This leads us to the process of identifying web components. We should examine our user interface for repeated elements, such as filter fields or arrow buttons, which may appear consistently across various applications.
00:12:47.890 Let's begin with the filter field component: we need a text box where users can input data, which will map to query parameters when generating an HTTP request. To achieve this, we can utilize an input type text element or a select element. The functionality of HTML forms allows us to wrap this input in a form, ensuring that once the form is submitted, the input element's value will be sent as a query parameter. This is a clever feature that I believe isn't always common knowledge—I learned it through my master's program in computer science.
00:13:35.200 With a working input component, the next question is whether it is accessible for all users. It's vital that our component can be utilized with screen readers. I encourage everyone to use a screen reader as part of this process—it is incredibly easy to install one on most operating systems. The most common excuse for not utilizing them is the perception that it's challenging, which is true; however, the time spent focusing on accessibility is an invaluable investment.
00:14:48.210 Another excuse might be that we fear breaking the user interface while learning how to use these tools. We should prioritize learning how to use a screen reader before making changes, as the basic commands for navigation are not difficult to grasp. One highly-recommended resource for learning about accessibility is a book called "Inclusive Design Patterns," which I found extremely helpful. Now, let's see how our component is perceived through a screen reader.
00:15:25.680 Let’s listen closely to the audio output. Right now, the screen reader states that we are currently on a text field, asking us to enter text into it. However, it does not indicate the specific action that will occur upon editing the text. To improve this, we can utilize the aria-label attribute within our HTML input element to provide that contextual information. For example, if we label it as "Filter Column 1," the screen reader will inform users that filtering column 1 will occur when they edit this text.
00:16:30.800 This addition ensures that we now have an HTML component that works for all users. The next step is to make it visually appealing. However, I add a question mark next to this point because while I believe aesthetics matter, modifying CSS can be more challenging than writing it. Based on past experiences working with various libraries, I've found that when they come with a very imposing CSS implementation, it becomes quite difficult to adjust them according to my website's design.
00:17:09.170 Therefore, my suggestion is to maintain minimal CSS styles in reusable components, allowing for effortless overrides later. For now, my focus is simply to add a CSS class to my input elements, enabling future customization across various projects. In this way, we have an HTML element that functions properly, is accessible to all users, and can be styled by others moving forward. Now, we must consider the JavaScript component.
00:17:57.000 As stated earlier, although JavaScript is an integral part of web components, I find that in this particular case, further interactivity is unnecessary. Thus, at this stage, I can conclude that we do not require JavaScript within this component. The second component I want to discuss today is the arrow component, highlighted by the yellow area. The goal for this is to enable the selection of one arrow, triggering an HTTP request with the corresponding sort behavior for the selected column.
00:18:37.350 Does anyone know which HTML element achieves this functionality? A radio button! Precisely! When I create an input element of type radio with the name 'sort,' I can position multiple radio buttons so that only one can be selected at a time. Then, when we wrap this selection within a form, it will similarly send the chosen radio button's value as a query parameter for the server to process.
00:19:19.840 For this component, it is also essential to ensure accessibility. Let's see how it sounds for someone using a screen reader. The screen reader will announce it as 'radio button one of two.' This conveys not only the fact that it is a radio button, but also the purpose and expected action, allowing for a smooth user experience.
00:19:51.840 Next, I want to make the component visually appealing. One interesting aspect of checkboxes and radio buttons is that they can be interacted with via their associated HTML labels. Hence, we can visually hide the radio button while styling its label to create an attractive arrow display. Using certain CSS techniques, we can position the input elements off-screen, ensuring they remain accessible to screen readers while still achieving a balanced visual design.
00:20:24.430 For example, I can utilize the plus operator in CSS to change the styles of my label elements based on the state of the preceding input element. This allows me to create a visual cue—changing the label color when the associated radio button is checked or hovered over—without compromising accessibility. This lays the groundwork for a user-friendly, aesthetically pleasing interface.
00:21:01.000 Regarding whether JavaScript is necessary for this component, the interaction with the radio button's state can be handled via the HTML itself. Thus, for now, we can proceed without JavaScript. Before moving on to the next component, I would like to touch on templating engines. As we've seen, we have developed several HTML snippets up to this point that we could utilize across different projects.
00:21:56.240 However, managing these snippets may become difficult as they evolve, requiring changes in multiple instances if we alter them. To combat this problem, we can use a templating engine to create an abstraction for our components, enabling us to adjust them in one place and automatically applying those changes across all instances. This approach simplifies maintenance and provides versatility when reusing components.
00:22:43.210 Let's consider how we can apply this template engine to the arrow component. The styling and functionality can be encapsulated within a template. The third component I'll introduce is the column header component. We want to ensure these components are laid out as desired.
00:23:20.390 So I encapsulated them all within a div, but it's clear that the layout requires further refinement. Next, we need to consider how this component sounds through a screen reader. Users should be able to navigate without hassle. If we group the radio buttons together and use the role='group' attribute in our HTML, we are effectively creating a navigational level for screen readers. This way, users can easily navigate through elements associated with different columns.
00:24:22.730 This enhancement informs users that they are navigating a group of sorting options, allowing for much easier navigation and a better user experience. After ensuring the HTML meets accessibility standards, we still need to enhance the visual layout—keeping aesthetic considerations in mind.
00:25:17.130 For guidance on visually structuring the layout, we can think of a comparison with the game 'whack-a-mole.' If you've ever developed CSS, you understand the frustration that arises when modifications disrupt existing layouts. A successful strategy for this is to design our CSS as if we are arranging a box of chocolates—creating a fixed container for our components rather than letting them stray wild throughout the application layout.
00:26:32.320 Using flexbox or grid for layout management makes the process far easier. For example, when we implement CSS grid for our components, we can define various grid template areas to control how they display within that container. Different areas can be assigned to specific components or features, contributing to an organized layout that remains consistent.
00:27:21.500 It's important to note that earlier, centering elements within a container was quite a hassle in CSS. Fortunately, with CSS grid and flexbox, vertical alignment is now straightforward. By applying 'align-self: center' to our header CSS, we can neatly center our text. Wrapping this in an HTML template allows for easy reproduction and reusability in future applications.
00:28:14.890 Thus, we have finished our header column layout, ensuring it functions properly and is visually attractive. However, when it comes to JavaScript, we must recognize that the primary responsibility for layout and styles lies within CSS. Therefore, for this specific component, we can postpone the use of JavaScript.
00:29:07.930 The final component I wish to address is a wrapper component that incorporates our HTML table and column headers. As mentioned earlier, we need to wrap the entire setup in a form to enable users to submit queries effectively. Once the user populates the fields with different criteria and submits them, it will filter the data accordingly while allowing sorting through the header arrows.
00:30:39.180 Although this interaction is dynamic, we also want to minimize any visual flickering that may occur with the loading of new data after submission. To enhance user experience, we can implement JavaScript to handle form submissions asynchronously. This allows us to fetch new data and replace the existing table body without a full page reload, creating a smoother interface.
00:31:36.780 To accomplish this, we can create a helper function that serializes our form inputs, leveraging the fetch API to gather data from the server. Once we receive the response, we can update our table efficiently using a function that transfers the new HTML from the server to the DOM.
00:32:29.730 In defining our own submit function for the form, we must ensure to prevent the default submit action while integrating our JavaScript handling. This increases functionality without sacrificing a user-friendly experience.
00:32:55.780 The next stage involves establishing the initialization process of this JavaScript behavior within the framework of our custom components. By using the custom elements specification, which allows us to establish a new HTML element that incorporates desired JavaScript functionality, we streamline interactivity and enhance our component.
00:33:32.420 As demonstrated, when defining the connected callback method within the class for this element, any JavaScript functionality needed is executed as soon as the custom element is injected into the DOM. JavaScript operates within the scope of its affiliated HTML element, providing easier access to the necessary APIs.
00:34:16.340 We can increase our component's dynamism by triggering submissions ourselves when it becomes necessary. For example, adding an event listener to the change event allows for immediate requests to be made when a user interacts with the interface.
00:35:04.410 Integrating a debounce function can significantly boost user experience when performing live filters or searches, as it prevents excessive server queries during rapid input. In taking these additional steps, we ensure that our custom component becomes fluid and responsive while maintaining a clean, organized underlying structure.
00:36:45.160 In conclusion, after implementing the various improvements discussed, we will end up with a functional interface where users can interact seamlessly with sorting columns and filtering results instantly. The key takeaways from this journey are as follows: a solid understanding of HTML forms and web components, accessibility considerations, CSS for stylish presentations, and efficient JavaScript integration—the perfect combination for creating user-friendly web applications.
00:37:58.650 Thank you very much for your time!