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('')
})
})
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