Search code examples
reactjschakra-ui

Avoid magic strings when working with Chakra Ui


A problem, I keep running into with Chakra Ui are "magic strings". Let's look at an example of muted text:

function Example() {
    const color = useColorModeValue("gray.400", "gray.200");
    return (
       <Text color={color} />
    )
}

What's the problem with this code: In my app, I want to share the muted color between many components. However, defining it explicitly as a string means that I have to remember that muted text has a value of "gray.400". If I have another component that wants to use muted text, I have to copy the string "gray.400" to all other components. I will end up with lots of strings that make it really hard to change things across the entire app. I explored two solutions so far:

Solution 1 - TextStyles API: Chakra comes with a textStyles API out of the box but this doesn't work well for more complicated situations (what if I want to have a hover and active state with different colors?).

Solution 2 - Create a global object: I've created a hookuseConsistantStyles() that returns a theme-like object with values, e. g.: {"borderLight": "gray.200"}. However, this feels like I'm fighting the library.

I'd really love to have a better solution since I keep running into this.


Solution

  • This is probably late but for any new onlookers, since Styled System v1.17.0, chakra ui has semantic tokens. The changelog and docs have some details but the important bit is:

    Semantic tokens provide the ability to create css variables which can change with a CSS condition.

    CSS conditions would be states like dark mode or hover etc.

    In this case, you would define a semantic token like so (with a better token name):

    const customTheme = extendTheme({
      semanticTokens: {
        colors: {
          nicelyNamedToken: {
            default: 'gray.400',
            _dark: 'gray.200',
          },
        },
      },
    })
    

    import the theme however you currently do, and then anywhere the theme is present, you should be able to just use the token

    function Example() {
        return (
           <Text color='nicelyNamedToken' />
        )
    }