Reputation: 391
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.
/* 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.
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.
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.
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