1

I am working on a project in ReactJS. I have to create multiple themes feature in this website. Color code of all themes are coming from an API.

I have used variables in SCSS and imported them throughout the project to set color. For example

$primary-color: #fec00b;

but now I need to set value of that primary-color dynamically using color code that I am receiving in API response.

So how I can set it programatically?

1
  • I did this once by injecting a style tag into the document. The API was returning a stylesheet as plain text and by document.createElement('style') I was injecting. You can then use global to check if the style is present and conditionaly read that or use default theme Commented Aug 24, 2021 at 6:37

2 Answers 2

2

If you want to change values dynamically then SASS it is not suitable for this task, because of it should be compiled into CSS before it can be used by browser.

It is much more better to use native css variables for such tasks - they are dynamic, you could change them any time both from css and from js. And browser support is also very good (unless you must support IE).

Here's a little example of how to change such vars:

let $color = document.querySelector('#color');

$color.addEventListener('change', (e) => {
  let color = e.target.value;
  document.querySelector("html").style.setProperty('--box-bg', color);
})
html {
  --box-bg: gold;
}

.box {
  background: var(--box-bg);
  width: 100px;
  height: 100px;
  border-radius: 4px;
  margin-bottom: 16px;
  transition: background .3s ease-in-out;
}
<div class='box'></div>

<label>
  Choose color
  <input type='color' id='color' />
</label>

Sign up to request clarification or add additional context in comments.

Comments

0

To achieve this I create this custom hook:

import { useEffect } from 'react'
import { camelToKebab } from '../utils/strings'

const useGlobalVariables = (className = 'global', styles = {}) => {
  const setVariable = (variable, value) => {
    document.querySelector('html').style.setProperty(variable, value)
  }

  useEffect(() => {
    Object.entries(styles).forEach(([property, value]) => {
      setVariable(`--${className}-${camelToKebab(property)}`, value)
    })
  }, [])
}

export default useGlobalVariables

/**
 * Usage
 * Component:
 * useGlobalVariables('styled-modal', {
 *   maxWidth,
 *   width,
 *   height,
 *   maxHeight,
 *   overflow,
 * })
 * CSS:
 * .class-name {
 *   width: var(--class-name-width);
 *   height: var(--class-name-height);
 *   max-width: var(--class-name-max-width) !important;
 *   max-height: var(--class-name-max-height) !important;
 *   overflow: auto !important;
 * }
 */

Unit tests:

import { renderHook } from '@testing-library/react-hooks'
import useGlobalVariables from './useGlobalVariables'

describe('useGlobalVariables', () => {
  beforeEach(() => {
    // remove global variables
    document.querySelector('html').removeAttribute('style')
  })

  it('should set global variables', () => {
    const styles = {
      backgroundColor: 'red',
      fontSize: '16px',
    }

    renderHook(() => useGlobalVariables('global', styles))

    expect(
      document.documentElement.style.getPropertyValue(
        '--global-background-color',
      ),
    ).toBe('red')
    expect(
      document.documentElement.style.getPropertyValue('--global-font-size'),
    ).toBe('16px')
  })

  it('should convert property names to kebab-case', () => {
    const styles = {
      backgroundColor: 'red',
      fontFamily: 'Arial',
    }

    renderHook(() => useGlobalVariables('custom', styles))

    expect(
      document.documentElement.style.getPropertyValue(
        '--custom-background-color',
      ),
    ).toBe('red')
    expect(
      document.documentElement.style.getPropertyValue('--custom-font-family'),
    ).toBe('Arial')
  })

  it('should not set variables if styles object is empty', () => {
    renderHook(() => useGlobalVariables())

    expect(document.documentElement.style.cssText).toBe('')
  })
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.