Customizing web apps in real time

Los invisibles de la CDN

It is always a challenge, both at the design and development level, to customize or theme web applications, especially when there are a variety of brand identities and service resellers involved.

As a front-end solutions architect, my job is to plan, execute and provide solutions that are simple and do not add unnecessary complexity at a technical level. I seek, as far as possible, to make the use easier for both the user and the client.

To do this, we always apply our KISS (Keep It Simple Stupid) methodology ;), seeking that these solutions fill the gap they are intended to solve and/or improve performance if they replace an existing functionality or architecture and, above all, that they are usable.

Searching for optimal solutions

The need for each company or reseller of our service to be able to identify the product with its brand posed a challenge for our team: finding the most efficient way to offer real-time customization.

At first glance, to cover this requirement, environments had to be duplicated, styles, assets and code dependent on the brand image itself had to be established, so that when the static compilation was carried out, each reseller could obtain their personalized space.

We explored several ideas to address this use case, but each proposed solution seemed difficult to maintain in the long term and generated more needs and tasks than we were looking for. Therefore, we discarded them and continued looking for an optimal solution.

Do something today that your future self will thank you for

When inspiration arrives

So, we immersed into the documentation, ran proofs of concept, and allowed ourselves moments of reflection to clear our minds in the form of walks which are known to help us think more clearly. And indeed, they seem to work; the results speak for themselves.

The idea arose in the form of a question, why not create a brand image simply by segmenting by URL in real time based on a configuration file? The result was the creation of a small ecosystem of tools, which, well communicated and organized, allow us to do just that.

The tool ecosystem

  • Control access via URL

Initially we controlled that each visit to a web app URL corresponded to a specific domain/subdomain. Later, by implementing a series of route middlewares in our router (vue-router), we saw that this was a common occurrence. Therefore, we moved this functionality from our vue entry point (APP, main, etc.) to a theme and customization control middleware that we integrated into our pipeline of middlewares for route access control.

  • Dynamic loading of style variables

For dynamic loading we had to solve the problem that stylus (we could have used scss or sass too) and CSS do not allow color changing at runtime when loaded via file. In other words, we cannot write the variables dynamically. For example, if we have color variables like $primary, $secondary, etc… (just to mention a few and there are more, since it is possible to set spacing, shading, fonts and a thousand other things), this will cause that during the execution of our build with webpack (or the bundler we use), the styles are generated based on these variables only the first time, without the ability to change them in real time.

The solution we found is to set those variables in our CSS with a default and then, thanks to our utility library, we can store and override those variables in the :root tag of our tag.

An example of defaults and how they could be used to allow dynamic loading and changing of pre-defined styles in our root element:

You can read more about CSS custom properties at https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties

  • Brand image control

Once we have identified from which domain or URL they are visiting us and we have the variables in our CSS, the next step is to load the different style variables from a configuration file. To do this, we have created a JSON file that contains the URL and all the available customization options. In this way, we only need to consult this file to get the variables and load them into our HTML.

Control de imagen de marca
  • Persistence

At this point it is crucial to add a performance layer, so we need to persist data. To achieve this, we can use tools like Vuex, Pinia, or even the browser’s LocalStorage object. This persistence allows us to store the theme data if we don’t have it yet, so that next time we can validate the absence of changes in the theme or domain during the loading of the visited URL. Thanks to this, changing colors, images or other elements is extremely easy.

Pros vs cons

Pros:

  • Agility, simplicity and speed
  • Registering a reseller only involves modifying a JSON and pointing a domain/subdomain
  • Adding assets to the project does not imply uploading them to the project repo since we have a static manager linked from the front end
  • There are no extra requests to get the configuration
  • It is very easy to maintain
  • Immediacy and performance in loading brand image
  • In our case, most variables are usable in any component, so a single place to keep all variables (:root) keeps everything centralized when making these changes.

Cons:

  • Mainly, putting all variables in :root causes the code to lose modularity (in this particular case, we have preferred maintainability over modularity), since we could benefit from defining local-scopes and using fallbacks per class to limit the use of variables.

You can see how to manage your variables in a modular way here: https://css-tricks.com/breaking-css-custom-properties-out-of-root-might-be-a-good-idea

Personalized results in real time

In our app and during the build of the same we establish default values ​​for the style variables. By minifying and compressing and/or hashing files, the client/reseller front end will have these default values ​​and the variables that later, appropriately encoded, allow modifications to be made according to the criteria we establish.

In our case, these criteria are based on the URL or domain/subdomain, but they could also be a selector in the interface with communication and persistence through a REST API, for example.

In this way we ensure that during the execution of the application, each user can enjoy a customization adapted to their individual needs.

Author: Antonio Gregorio Bacete Ramírez

Antonio Bacete Ramírez is a Frontend Solutions Architect at Transparent Edge. He is particularly interested in innovation and front-end usability applied to each project he develops. He has advanced knowledge in other languages ​​and frameworks and is one of the great references for the tech team.