Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js/NuxtJS - how to create components with a default design customizable by a JSON configuration file

I'm developing a NuxtJS website and the pages/components can have either a generic design by default, either one that is customizable by client, and will be specified in the url.

Something alone the lines of:

http://localhost:3000/registration - For a generic page

http://localhost:3000/client-name/registration - Client specific page

To achieve that goal, I have a JSON configuration file per client (say client-name.json) that has this structure.

{
  "some_configuration_property": {},
  "another_configuration_property": {},
  "design": {
    "logoUrl": "/assets/client-name/logo.png",
    "backgroundColor": "#000000",
    "primaryColor": "#ffffff",
    "secondaryColor": "#ffff00"
  },
}

To start things, I implemented the routing system and I can successfully read each client's configuration based on the current route (inside the <script> tag of the Vue file of that route), inside the setup method (I use @nuxt/composition-api).

The problem that I'm facing now is to figure out how to pass these "design variables" into the <style> tag of my Vue file, which uses SCSS. The behaviour that I wanted to implement was to have a default design for a specific component/page, but that could be overridden by these "design variables" specific to each client.

  • The first thing that came to my mind was to use CSS variables, that would allow me to create variables with a default values but that I would be able to override inside the styles. I created a sample component for test and it worked with CSS properties and the v-deep pseudo element. However, this means that I would have to create a class for each client in the customized component and that's what I'd like to avoid. I first thought to this approach because it would give me a lot of flexibility about how I choose to use these design colors inside the styles.

Example:

// From the customizable component
.my-button {
   color: (--button-color, teal);
}

// Styling from a parent component/view
// Had to create a selector with a style like <div> for superior specificity though, not so clean
v::deep {
  div {
    &.my-button {
      --button-color: purple;
    }
  }
}
  • I've seen the /deep/ selector or ::v-deep pseudo selector but I don't think it's a very clean solution since it would be used a lot in the codebase. Styling component from parents would make the code hardly maintainable.

  • Another approach could be to pass a variable, classArray for instance, inside the setup method to dynamically bind CSS classes on the DOM elements. Although, it would be way too cumbersome to create a CSS class per client with the associated styles.

Like this:

<template>
  <my-button :class="classArray"></my-button>
</template>

<script lang="ts">
import { defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  name: 'MyPage',
  setup() {
    const clientName = 'someClientName';
    const classArray = [clientName]
    return { classArray };
  },
})
</script>

<style lang="scss" scoped>
.someClientName {
  // some custom styles
}
</style>

What would be your approach in this situation?

Thanks for help!

like image 524
Théo Lavaux Avatar asked Nov 23 '25 19:11

Théo Lavaux


1 Answers

If custom theme configuration needs to be loaded at runtime, this requires to use CSS variables (properties). They can be wrapped in SCSS functions and have default theme fallbacks:

// theme.scss
$primaryColor: #abc;
// $buttonColor: $primaryColor

@function primaryColor() {
  @return #{var(--primary-color, $primaryColor)}
}

@function buttonColor() {
  @return #{var(--button-color, primaryColor())}
}

Then primaryColor(), etc are used instead of direct use of $primaryColor, etc like it's done in regular SCSS theme:

// From the customizable component
.my-button {
   color: buttonColor();
}

And custom theme can be applied on load to the entire document or a part of it (a hierarchy of components) that should be affected by custom theme:

const config = await loadClientConfig();
document.documentElement.style.setProperty(--primary-color, config.design.primaryColor)
like image 66
Estus Flask Avatar answered Nov 25 '25 11:11

Estus Flask