Building custom components in Vue.js/NuxtJS can be a breeze when using a default design that can be easily customized through a JSON configuration

Currently, I am in the process of developing a NuxtJS website where the pages and components can either have a generic design by default or be customizable based on client specifications provided in the URL.

The URL structure is as follows:

http://localhost:3000/registration
- Generic page

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

To achieve this functionality, I have created a JSON configuration file for each client, such as client-name.json, which follows a specific structure outlined below:

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

I have successfully implemented the routing system to read the client's configuration based on the current route within the Vue file setup method using @nuxt/composition-api. However, I am currently facing an issue on how to pass these "design variables" into the <style> tag of my Vue file, which utilizes SCSS.

  • Initially, I considered using CSS variables to create default values that could be overridden within styles. While this approach worked for individual components, I quickly realized that creating classes for each client would not be ideal due to maintainability concerns.

For example:

// Customizable component
.my-button {
   color: (--button-color, teal);
}

// Styling from a parent component/view
v::deep {
  div {
    &.my-button {
      --button-color: purple;
    }
  }
}
  • Although the /deep/ selector or ::v-deep pseudo selector could be used, it may lead to messy code and decreased maintainability when styling components from parents.

  • Another potential solution involves passing a variable, such as classArray, within the setup method to dynamically bind CSS classes to DOM elements. Yet, creating a CSS class for each client with associated styles could prove cumbersome.

As seen here:

<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 {
  // custom styles
}
</style>

What approach would you recommend in handling this situation?

Thank you for your assistance!

Answer №1

To dynamically load custom theme configuration, CSS variables (properties) must be utilized. These variables can be encapsulated within SCSS functions and incorporate default theme fallbacks:

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

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

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

Subsequently, primaryColor(), etc should replace direct usage of $primaryColor, etc as in conventional SCSS themes:

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

The custom theme can then be applied on page load either globally across the entire document or selectively to specific components within the hierarchy that necessitate customization:

const config = await loadClientConfig();
document.documentElement.style.setProperty(--primary-color, config.design.primaryColor)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to determine the count of results using a v-if statement?

Sorry for the possibly silly question, but I'm trying to figure out how to retrieve the number of results from my v-if statement within a v-for loop? This is what my code looks like: <div v-for="conv in conversation.hits" :key="conv ...

Angular, JavaScript, and PHP are three powerful programming languages that

This file contains HTML code <ul class="list"> <li id="numword" data-score="{{item.score}}" class="" ng-repeat="item in words track by $index"> {{item.word}} {{item.score}} </li> </ul> Here is the visual representa ...

Tips for ensuring that CSS hover effects stay in place even when the page is scrolled

i'm having trouble with a project in React JS that I received from another team. I'm not very confident in my skills with React JS, and I'm facing an issue where a certain part of the page is supposed to change to red when hovered over. Howe ...

Troubleshooting problem with div width in Outlook causing table cell padding issue for curved corners on another div

This question has significantly evolved following additional testing I am using a table layout similar to this: <table> <tr> <td></td> <td style="padding:10px 3%;"> <div border="2px solid #000000;"> ...

Using React client to accept messages from a Socket.io server: A guide

I have a setup where a Node.js server with Socket.io is used to send messages between React clients. Currently, I can send a message from Client 1 to Client 2, but the recipient must click a button to view the message on their screen. I am trying to make i ...

Overriding a shared module service in Angular from a separate module: A step-by-step guide

I am working with various modules such as SchoolModule, UniversityModule, and SharedModule The SharedModule includes a BaseService that both the SchoolModule and UniversityModule providers are utilizing as an extension When loading the SchoolModule, I ne ...

The positioning of the Material Ui popover is incorrect

I am currently working on a web application project with React and have implemented Material UI for the navbar. On mobile devices, there is a 'show more' icon on the right side. However, when I click on it, the popover opens on the left side disp ...

Exploring the World of GiantBomb APIs

I have successfully created an account and obtained my API key. I am looking to implement a basic search functionality on my webpage, where users can enter a search query and click a button to display the game title and image. You can find more informatio ...

Chrome causing a background-size cover issue

I encountered an issue with this code. It runs smoothly on Firefox 50, but encounters failures on Chrome Version 54.0.2840.99 m (64-bit) on Windows. .test { background: url(datas/images/3.jpg) no-repeat center center fixed; background-size: cover ...

What is the process for extracting the time value from a jQuery timepicker?

I have been attempting to retrieve values from my time picker when a selection is made, but I am not seeing any values returned nor displayed in my HTML timepicker input field. Despite trying various methods, none have proven successful thus far. Method ...

You cannot use the router inside the Vuex store in VueJS framework

After removing the authentication data in my logout function, I would like to redirect the user to the home page. logout({commit}){ commit('clearAuthData') router.replace('/') I have also included my routes.js file like th ...

Is there a way to implement full-page scrolling in Vue?

Looking for a Vue equivalent of the fullpage.js library. Need a component that allows scrolling through elements similar to fullpage.js. ...

Attempting to execute a PHP script through a JavaScript if statement

I have a form that requires validation with JavaScript. In one of the if statements, after all other conditions have been validated, I want to execute my PHP script that updates an SQL database with one of the passwords. Here is the final validation code: ...

Can you explain the functionality of this particular line of code in JavaScript?

As I continue to practice my coding skills, despite still being relatively new, I often come across this type of code in loops when searching for solutions to practice problems. I am intrigued by what exactly this particular line of code is accomplishing. ...

HTML form submission with a grid of multiple choice options

I have created my own multiple choice grid: <div style="overflow-x:auto;"> <table class="w-100"> <tr> <td class="border"></td> <td class="border">Yes</td> <td class="border">No</ ...

Are there other options besides Chrome Frame for enhancing Raphael performance on Internet Explorer?

Currently, I am using Raphael 2.1 to simultaneously draw 15 lines, each consisting of 50 two-pixel paths. The performance is optimal in Safari and Chrome, acceptable in Firefox, subpar in Opera, and struggles in IE9. Despite Microsoft's claim that SVG ...

Unexpected unhandled_exception_processor in Google Chrome

I keep encountering a strange uncaught exception handler in Google Chrome. After updating all follow buttons to download JavaScript asynchronously, I noticed an error in the content.js file mentioned in the exception message which advises against polluting ...

Maximizing cell background color in HTML table with limited width fails to function properly

Having difficulty turning the header cells (th) with background-color applied? It seems like the color bar may be too small, the cell width too large, or the background isn't being affected properly. Is there a way to create a narrow cell with long t ...

How can I transform JSON data into MySQL INSERT and CREATE TABLE statements?

I have a dataset in JSON format that contains details about different exercises, and I need to transfer this data into a MySQL database. Each exercise comes with attributes like name, force, level, etc. To accomplish this task, I must convert the JSON da ...

Limit access to Google Fusion Table to specific types of maps. Eliminate Google Fusion Table for selected map formats

Currently, I am in the process of creating a web map using the Google Maps Javascript API. My objective is to display a Google Fusion Table containing buildings in Boston exclusively on a stylized map named "Buildings." When I switch to the Buildings map t ...