Aayush Goyal
Aayush Goyal

Reputation: 391

styled-jsx not scoping the CSS to component level in standalone React library

I am creating a React library for a design system and using styled-jsx as a CSS-in-JS solution. Our web apps are powered by Next.js and as we are efficient in working with styled-jsx and because Vercel is the company behind styled-jsx too we thought of using this library in our design system React library.

I have followed this article to bootstrap the React library only difference being using styled-jsx instead of styled components.

The problem is that styled-jsx is not limiting the scope of CSS to the component level. I have created a Text component that is basically a wrapper for the typography. Here is the code of the component I wrote.

index.tsx

/* eslint-disable no-use-before-define */
import React from 'react';

import { ThemeContext } from '../context/index';
import { RAVEN, SWAN } from '../colors/index';

type TextProps = {
    children: string;
    className?: string;
    textColor?: string;
    margin?: string;
    variant: string;
};

function Text(props: TextProps) {
    let className = 'text';
    switch (props.variant) {
        case 'h1': {
            className += ' text-heading-1';
            break;
        }
        case 'h2': {
            className += ' text-heading-2';
            break;
        }
        case 'h3': {
            className += ' text-heading-3';
            break;
        }
        case 'h4': {
            className += ' text-heading-4';
            break;
        }
        case 'h5': {
            className += ' text-heading-5';
            break;
        }
        case 'h6': {
            className += ' text-heading-6';
            break;
        }
        case 'body': {
            className += ' text-body';
            break;
        }
        case 'ol': {
            className += ' text-overline';
            break;
        }
        case 'button': {
            className += ' text-button';
            break;
        }
        case 'button2': {
            className += ' text-button-2';
            break;
        }
        case 'caption': {
            className += ' text-caption';
            break;
        }
        case 'st1': {
            className += ' text-subtitle-1';
            break;
        }
        case 'st2': {
            className += ' text-subtitle-2';
            break;
        }
        default: {
            className += ' text-body';
            break;
        }
    }

    return (
        <ThemeContext.Consumer>
            {(theme) => (
                <p className={`${className} ${props.className}`}>
                    {props.children}
                    <style jsx>
                        {`
                            .text {
                                width: fit-content;
                                margin: ${props.margin ? props.margin : '0'};
                                color: ${props.textColor
                                    ? props.textColor
                                    : theme === 'swan'
                                    ? RAVEN.EXTRA_DARK
                                    : SWAN.LIGHT};
                                font-family: 'Kanit', sans-serif;
                                text-transform: lowercase;
                            }
                            .text-heading-1 {
                                font-size: 61px;
                                font-weight: 400;
                                line-height: 92px;
                            }
                            .text-heading-2 {
                                font-size: 48.8px;
                                font-weight: 400;
                                line-height: 76px;
                            }
                            .text-heading-3 {
                                font-size: 39px;
                                font-weight: 400;
                                line-height: 60px;
                            }
                            .text-heading-4 {
                                font-size: 31.25px;
                                font-weight: 500;
                                line-height: 48px;
                            }
                            .text-heading-5 {
                                font-size: 25px;
                                font-weight: 600;
                                line-height: 40px;
                            }
                            .text-heading-6 {
                                font-size: 20px;
                                font-weight: 500;
                                line-height: 32px;
                            }
                            .text-body {
                                font-size: 16px;
                                font-weight: 400;
                                line-height: 24px;
                            }
                            .text-overline {
                                font-size: 12.8px;
                                font-weight: 500;
                                line-height: 20px;
                            }
                            .text-button {
                                font-size: 16px;
                                font-weight: 700;
                                line-height: 24px;
                            }
                            .text-button-2 {
                                font-size: 20px;
                                font-weight: 700;
                                line-height: 32px;
                            }
                            .text-caption {
                                font-size: 12.8px;
                                font-weight: 400;
                                font-style: italic;
                                line-height: 20px;
                            }
                            .text-subtitle-1 {
                                font-size: 16px;
                                font-weight: 500;
                                line-height: 24px;
                            }
                            .text-subtitle-2 {
                                font-size: 12.8px;
                                font-weight: 700;
                                line-height: 20px;
                            }
                        `}
                    </style>
                </p>
            )}
        </ThemeContext.Consumer>
    );
}

export { TextProps, Text };

Now I tried to consume this component inside our web app for which here is the code of the page I was using it in.

beta.tsx

import React from 'react';
import { Container, Text, ThemeContext } from '@deriva-inc/davinci-react';

function BETA() {
    return (
        <ThemeContext.Provider value="raven">
            <Container>
                <Text margin="0" variant="h1">
                    h1. heading 1
                </Text>
                <Text margin="0" variant="h2">
                    h2. heading 2
                </Text>
                <Text
                    margin="0"
                    variant="h3"
                    className="heading-3-custom-1"
                    textColor="#FF0000"
                >
                    h3. heading 3
                </Text>
                <Text
                    margin="0"
                    variant="h3"
                    className="heading-3-custom-2"
                    textColor="#00FF00"
                >
                    h3. heading 3
                </Text>
                <Text
                    margin="0"
                    variant="h3"
                    className="heading-3-custom-3"
                    textColor="#0000FF"
                >
                    h3. heading 3
                </Text>
                <Text margin="0" variant="h4">
                    h4. heading 4
                </Text>
                <Text margin="0" variant="h5">
                    h5. heading 5
                </Text>
                <Text margin="0" variant="h6">
                    h6. heading 6
                </Text>
                <Text margin="0" variant="body">
                    body
                </Text>
                <Text margin="0" variant="ol">
                    overline
                </Text>
                <Text margin="0" variant="button">
                    button
                </Text>
                <Text margin="0" variant="button2">
                    button 2
                </Text>
                <Text margin="0" variant="caption">
                    caption
                </Text>
                <Text margin="0" variant="st1">
                    subtitle 1
                </Text>
                <Text margin="0" variant="st2">
                    subtitle 2
                </Text>
                <p className="text text-aayush">Aayush</p>
            </Container>
            <style jsx>
                {`
                    .text {
                        color: #00ff00;
                    }
                `}
            </style>
            <style jsx global>
                {`
                    main {
                        margin: 60px;
                    }
                    .heading-3-custom-1::selection {
                        background: #43bd99;
                    }
                    .heading-3-custom-2::selection {
                        background: #03865f;
                    }
                    .heading-3-custom-3::selection {
                        background: #409978;
                    }
                `}
            </style>
        </ThemeContext.Provider>
    );
}

export default BETA;

And here is how the page got rendered.

enter image description here

As you can see clearly, I applied different colors to all three heading 3 texts via props but all were rendered in the default color in context of the theme (i.e. White). When I debugged the page, I found this.

enter image description here

enter image description here

So, I found the problem. Component is getting the color given to it via prop, but as the CSS is not scoped to component, all the Text components are rendering their CSS in global scope and as we know the last CSS applied to will be taken in precedence order and hence all component are taking the CSS of the Text component rendering Subtitle 2. I don't know what am I missing here or why it is not working as expected.

I know we can switch to styled-components and that is what we will do if we do not find a solution to this problems. Any help would be appreciated. Thanks.

Upvotes: 1

Views: 514

Answers (0)

Related Questions