ADEO Design System: Building a Web Component library with Svelte and Rollup
In this article, we showcase how we built a Web-Component library at ADEO, using SvelteJS, Typescript, and Rollup and how we integrated them with the popular JS frameworks Angular, Vue, and React. Also, we discuss the pain points and the challenges we faced.
Readers needing a reminder about the Web Component philosophy may check out this article:
Four years ago, we started a Design System at ADEO (ADS) to achieve the following goals: create a coherent customer experience on the web, shorten the team’s delivery time and improve the quality of deliverables.
Powered by inputs like Design, Tech, Business, and Inclusion, ADS is used by the corporate platforms, Communities, and Business Units (e.g. Leroy Merlin). With ADS, we provide a constantly evolving system that answers the corporate’s needs.
Welcome to Mozaic Design System
ADEO Design System: is a product designed to help set up a coherent human experience, reinforce the team’s delivery time and improve the quality of deliverables…
Web Components has been around for almost 10 years now, but it seems to have been adopted only recently by developers.
Starting with new frameworks can be complicated and time costly, in reading articles and tutorials. In our case, we felt like we were the early adopters and few resources related to our stack were available.
That leads us to our first part, how did we choose our stack?
How we choose our Tech Stack
When we decided to build our ADS Web Components library, we had to define criteria to validate other choices.
During our spike, we defined 3 main criteria:
- Bundle size: Web performance is an important driver at ADEO. We want to make sure that the framework used is the smallest in size.
- Community & repo activities: It is nice to have a framework with great features, but a dedicated community was really important for us.
- Easy for contribution: Inner source is the key to success for our Design System. The chosen framework must be easy to understand and learn.
Focusing on the processes is the ADS team’s DNA. To ensure we have the strongest stack we’ve put these actions in place:
- Create a dev community | They must be willing to spend time on the project, have enough experience to question choices, and are open for discussion. We have more than 15 developers who contributed to the library.
- Show your work | From research, spikes, and POC, we share and show our work to the community and encourage feedback.
- Facilitate the discussion | Slack discussions are set up, weekly meetings to follow progress, and Github tools are used to keep track of changes.
Thanks to our early adopters and community members, we were able to implement this stack.
Our Tech Stack
After much research, proof of concept, and discussion, we were able to find our winners.
Used in all our libraries, it showcases our components and lets users interact with them thanks to controls.
ADS Web Components
Now that we have our technical stack and a group of devoted and highly knowledgeable developers, we are almost ready to start coding. All we need now is to find the right architecture for the project and define ground developing rules.
An organized project structure allows contributors to dive easily into the development process.
The first and foremost thing to check in a JS project is the `package.json` file. It will tell you everything about what is required. This is where we defined all our scripts (lint, build, package..), all dependencies and our lint staged (script to run on each commit)
- SRC folder
It is clearly the heart of the project, where you will find all the needed source files for each component under the “components” folder. Also, we’ve decided to create a utility folder with all reusable methods. And finally, a stories folder is used to showcase components in the storybook.
- Config files
All our config files are in the root folder, from code linting config to building config. All these files are highly sensitive.
We’ve tried to keep the storybook config as easy as possible. We’ve added the essential plugins and some basic styles.
Following the official Svelte documentation, we’ve split our Svelte component into 4 parts.
- Svelte options
It provides a way to pass options to the compiler. We used it to pass the tag name of the web component expected
In most cases, our component is defined at ‘null’ to let the user create their tag name to avoid name collision.
In this part, you will find all the defined props and specific methods for a component.
Thanks to our design system, we have written all the HTML for each component. We had to manage dynamic behaviors and styles.
Thanks to ADS core libraries, JS libraries do not need to add any styles to components. The component will be styled only by importing the component SCSS file from ADS core libraries.
Integrate with the main framework
Knowing that frameworks are evolving rapidly, I encourage you to check the latest news from your favorite framework. A complete article could be written about how each framework works with Web Components.
To make it clear and concise, I’ve created a project on each framework, and I focused on these actions
- Installed and config ADS Web Component library
- Basic data binding
- Event handling.
All these showcase repositories are open and can be challenged.
Visit our showcase website using WebComponent in Vue.
1 — Angular
GitHub - Showcase ADS Web Components usage in the Angular project
Showcase ADEO Design System Web Component library used in an Angular project.
Installation and configuration: (Angular >= v.10)
Angular installation and configuration are pretty simple and well documented on the main sites. By adding the following to your App.module.ts, you will be able to use custom elements on your application.
The one-way binding, from the source to the view works out of the box using expression.
Two-way data binding was not working out of the box, Web Components are being considered as HTML native elements. Angular manages two-way data binding only for Angular components. With advanced Angular developers, we are creating directives to handle two-way data binding for our Web Component.
Custom events are well managed in Angular. Out of the box, it is possible to listen to native and custom events emitted by our Web Components.
A code example showing data and event:
Easy to use out of the box, Angular manages the basics pretty well. To go further and have more dynamic usage, developers will need to implement a wrapper around the Web Component to handle two-way data binding.
2 — VueJS
GitHub - Showcase ADS Web Components usage in Vue
Showcase ADEO Design System Web Component library used in a Vue Project
Installation and configuration: (Vue >= v.3)
No configuration is needed to use Web Components in Vue3. Once the library is installed (by NPM or Yarn), you only need to import Web Components into your main.ts file. Pretty simple indeed!
Same as that we have for Angular, one-way data binding is well managed by Vue.
The native event works fine, but custom events don’t work out of the box. Developers will need to write specific wrappers to catch the custom event.
A nice integration and easy to use for basic Web Components. You will need to write specific code to manage events and slots to make the most of the library.
To further on the web component usage with Vue, follow this documentation:
3 — ReactJS
GitHub - Showcase web component usage using React
Showcase ADEO Design System Web Components library used in a React project…
Installation and configuration: (React >= v.18)
React is a library, not a framework. For this tests purposes we used CRA CLI tool.
React added features in their new version to improve Web Components support and no configuration is needed. Once the library is installed (by NPM or Yarn), you only need to import the Web Components via the index.ts file.
Using React State feature makes it easy to bind data with Web Components.
As for Vue, React works fine with the native events, but doesn’t handle custom events. Developers will need to write a specific wrapper to support custom events.
For further information, follow this article:
React’s latest version manages Web Components well. Unfortunately for more advanced usage, developers will need to write wrappers to better handle data and events.
Overall the 3 main JS frameworks handle Web Components with hardly any configuration. For more complex usages we advise our developers to write wrappers to make sure they fully control the Web Components.
After developing more than 30 components with Svelte and Rollup, we learned a lot and faced some challenges. This is a summary of the most important takeaways.
With Web Components, we use attributes and not properties. Both are key/value pairs. On one hand, attributes pertain to HTML elements and take string values only. On the other hand, properties pertain to JS objects and take any JS object as a value.
It is mostly accepted to name properties using camelCase or kebab-case. Unfortunately using Svelte with Web Components, both won’t work. Properties will need to be defined as full lowercase.
- Boolean props:
HTML specification defines that a boolean attribute presence will represent the True value.
Users need to make sure to remove or put undefined for the boolean attributes to have the False value.
- Complex props
All attributes passed to Web Components must be in string format. For arrays and object properties, the developer must stringify the props before passing them. If the data is huge and complex, this step slows the rendering.
Svelte offers a SvelteDispatcher, but unfortunately, it doesn’t simultaneously emit a custom DOM event. Since we use both Svelte and Web Components, we created an EventHandler utility method to chain together SvelteDispatcher and dispatchEvent methods to raise a Svelte custom event and a DOM custom event simultaneously.
In a Design System library, nested components are a must to have. Some components need to be used only inside other components such as the Svelte component. Meanwhile, other components need to be generated as Web Components. To manage this part, you must customize your Rollup configuration.
Components that are nested must have a specific extension ‘.nested.svelte’, Rollup will then exclude them from the web component build.
For complex components such as the data table, declaring a named slot in a loop will not work, as specified in the Svelte documentation. Several issues are still open on the subject. Let us know if you found a way for this!
Style | User customization
Using the Web Component approach, the CSS will be encapsulated inside the shadow root. It means the user won’t have access to the style to override it in their application. We have to add it to the root component.
Svelte offers a tremendous final build. At ADS we have more than 40 components and more than 900 icons. Releasing only a single bundle with all the components and icons can be overkill in some projects. We decided to have a bundle file and also an individual file for each component and icon. The size of an average component is between 10Ko to 15Ko without using compression tools (e.g. GZIP or Brotli).
On our journey to building a Web Component library using Svelte, today we successfully offer more than 40 components including forms, charts, and more complex ones, both as Web Components and Svelte components. Along the way, a strong and vibrant dev community has been created in the ADEO group; they enjoy great developer experiences both as contributors and as users of the library.
I think our journey is not finished. We want to offer more support for specific frameworks and add even more components. So do tell us what you think and what new features you would like to see in the ADS Web Components library.
I would like to give a big shout-out to all the devs that contributed and helped us make this library happen.