Uber’s Base design system offers a set of map marker components that are used extensively across our products, displayed any time we feature a map to the user. We’ve recently come across the need to showcase additional context on the map markers and settled on adding badges — an existing Base component — to achieve that.
The challenge was that both the map markers and the badges had multiple variations. Markers can be customized by size, needle length, icon slots and layout behavior, while the badges can have multiple sizes, label, and icon styles. The final set of badge-supporting map markers is a combination of all these options, a total of 256 new components.
The common approach is for a designer to manually create these new components, which is not only inefficient but very error prone due to the repetitiveness and volume of work required to create all variants and individually test them under multiple scenarios.
I decided to take a programmatic approach.
Why did we add badges to the markers?
Badge UI is generally used to showcase an update or an offer of some kind. In this case, the badge is supplementing the marker.
The design specification for these badges offers four different sizes:
Since Uber operates in markets where the default language is written from right to left, the badges also support bidirectionality.
What is the most efficient process to construct the new components?
Once we completed the design specification for the badges, the next step was to “productionize” the components within our Figma workflow. This process entails creating the Figma component for each customization, writing documentation, and then publishing the components to the organization library.
Adding the badges to the map markers would nearly quintuple (5x) the number of map marker components within Base, as we were essentially adding four new badge variations for each existing marker.
For example, the new map marker component naming convention would go from:
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off / Badge none
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off / Badge x small
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off / Badge small icon
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off / Badge medium icon
- Map marker / Medium / Icon leading + trailing / Fixed / Needle off / Badge medium text
Base already contains 68 map markers, so repeating this process 68 times would be quite the tedious task.
To complicate matters even more:
- each badge was to be positioned in a different relative position on the marker’s top right corner, depending on both the size of the badge and marker.
- only certain badges were eligible to be placed on each marker. This eligibility depended on the size of both the badge and marker.
Accounting for these constraints, a total of 256 new badge-supporting map markers would be created. Seeing the large number of permutations that I would have to build, I knew this would be a daunting manual process with lots of repetitive mouse movements.
Constructing these new components by hand would be a significant time investment that would most certainly lead to human error due to the pixel precision required of repetitively positioning each badge on a marker hundreds of times.
This type of monotonous task was a perfect candidate for replacing with automation with the Figma Plugin API.
Automation via the Figma Plugin API
The Figma Plugin API enables one to programmatically interact with the Figma document with nearly a 1:1 feature parity compared to normally interacting with the traditional UI. One can traverse the document as a node tree, create and modify objects, configure the viewport settings, access shared styles, and much more with the plugin. For those familiar with the web’s DOM (Document Object Model) API, the Figma Plugin API is very similar.
Uber has an extensive internal Figma ecosystem of plugins and tooling to augment the design process. Getting started with creating a new Figma plugin at Uber is relatively low-effort, as scaffolding is available to help with the initial plugin creation, testing, building, and publishing processes.
You can learn more about Uber’s Figma plugin ecosystem on the Figma blog.
Configuring the Figma API to construct the new components
I began to write the step-by-step process of the map marker construction algorithm to be implemented in Figma.
Since each badge and marker contained specific sizing and positioning logic, I created a configuration object in code to hold this logic. This object contained badge positioning and pairing eligibility information.
The Figma API plugin code would conduct the follow tasks:
- Query all eligible map marker components to append badges to. Only certain map marker badges were qualified to contain badges.
- Loop through queried markers and look up eligible badge pairings.
- If the marker had an eligible badge pairing:
- Look up placement position info for marker + badge combination.
- Using some auto layout trickery, append the badge to the marker in the correct relative position. This step was the most time-intensive, as I had to replicate and encode many Figma auto layout rules into the object programmatically. At this point the new map marker badge component were created.
- Neatly place the newly constructed map marker badge component in the main Base component document.
- Repeat for all remaining markers until the process is complete.
Executing the script was simple at this point. In the plugin’s UI window, a button click kicked off the script execution. All I had to do was click Build Markers, and the plugin would work its magic.
With just a single click, I was able to take a set of markers and badges and automatically bind them together to create hundreds of new Figma objects programmatically.
Creating the script was definitely an iterative process to nail every step of the component construction. However, the beauty of scripting in this situation is that I could dynamically undo and repeat the process repetitively until the output was exactly as I desired. There was no need to manually step back or reset the document by spamming the undo button. The entire workflow could be executed or completely reversed at once.
Testing markers at scale
At this point, the new map marker badge components were constructed, but the process was not quite complete. I needed a methodology to test these markers at scale to root out any potential positioning or sizing issues that may have appeared as a side effect of the script. Having a scalable testing framework available would also be important in the event the root components were ever changed in the future.
I wanted to test for two criteria:
UI test 1: badge positioning
The badge’s relative offset positioning depended on both marker and badge size. For example, some badges would be -3px offset, while some badges would be -5px offset.
In order to test this, I decided to automatically generate guides placed in the desired position of each badge using the plugin API. With these guides in place, I could perform an eyeball test to ensure each badge was positioned properly. Both a horizontal and vertical guide would be placed on every single new marker.
UI test 2: text length
The next test I implemented was to ensure proper badge positioning based off marker and badge text lengths. The length of both the marker and badge could differ on each custom instance of the component, depending on its usage.
To implement this test, I created two input fields within my plugin UI so that I could specify custom strings. This way, any text that I would enter into the inputs would propagate down into all the map marker components. This would let me ensure the badges stayed fixed in the correct position, no matter the length of the marker or badge text.
As design systems grow and mature, the complexity of the components increases with it. This additional complexity opens up room for human error due to the repetitiveness and volume of work required to generate all variants — even without errors, such a manual process is highly inefficient. And with a design system that is being used at scale, even small errors can get compounded and deteriorate the user experience. By incorporating automation into the design system workflow, we can create a high quality set of well-tested, production-ready components.
By automating the construction of the markers, I was able to avoid human-error and efficiently generate the full set of variants. Programmatically testing the components allowed me to thoroughly validate them under multiple scenarios and be confident of their quality.
Give the finished components a try in the public Figma Base Gallery.
Thanks to my colleagues Cady Wachsman, Vietanh Nguyen, Anton Chang, Christian Rauh, and Joshua Lee for the help throughout this process.