So recently i've been working on building react components with StorybookJS. For those who don't know what Storybook is ,Storybook is a JavaScript tool that allows developers to create organised UI systems making both the building process and documentation more efficient and easier to use.
With that said , i would like to share a common design pattern that i learned from building these components, and that is creating variants of a single component through the use of props.
*Do note that i'm using ChakraUI as my UI library
In this example we take a look at building a simple button component. Starting with the Button function
export function ReactButton(props) {
let {children} = props;
return (
<Button>
{children}
</Button>
);
}
Following that , we add propTypes to establish our variants, you can definitely do without this step but it helps to ensure we're using the right data type
ReactButton.propTypes = {
variant: PropTypes.arrayOf(
PropTypes.oneOf(["primary,secondary,outline,danger"])
),
};
export function ReactButton(props) {
let {children} = props;
return (
<Button>
{children}
</Button>
);
}
Now for the game changer, we make use of CSS to enable to the browser to display what we want want it to display. Using a switch statement we set the base styles for different variants
let baseStyles = {},
invertStyles = {},
stretchedStyles = {};
switch (variant) {
case "primary":
baseStyles = {
backgroundColor: "brand1.primary.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.primary.500",
},
};
break;
case "secondary":
baseStyles = {
backgroundColor: "brand1.secondary.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.secondary.500",
},
};
break;
case "outline":
baseStyles = {
backgroundColor: "transparent",
color: "brand1.secondary.300",
borderColor: "brand1.secondary.300",
_hover: {
color: "brand1.secondary.500",
borderColor: "brand1.secondary.500",
},
};
break;
case "danger":
baseStyles = {
backgroundColor: "brand1.danger.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.danger.500",
},
}
break;
You will notice i created an empty object for inverted and stretched, keep reading on to see how we implement those.
With that we set our default variant to primary, and establish some props like invert and stretched and give them a boolean value
let {
children,
variant = "primary",
invert = false,
stretched = false,
} = props;
Now we can go ahead and add stylings for our props that we declared (invert and stretched).
switch (variant) {
case "primary":
baseStyles = {
backgroundColor: "brand1.primary.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.primary.500",
},
};
if (invert) {
invertStyles = {
backgroundColor: "brand1.primary.50",
border: "0.25rem solid #ffe8ae",
color: "#969696",
_hover: {
backgroundColor: "brand1.primary.100",
},
};
}
if (stretched) {
stretchedStyles = {
width: "100%",
justifyContent: "space-between",
};
}
break;
case "secondary":
baseStyles = {
backgroundColor: "brand1.secondary.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.secondary.500",
},
};
if (invert) {
invertStyles = {
backgroundColor: "brand1.secondary.50",
border: "0.25rem solid #c4c9ef",
color: "#969696",
_hover: {
backgroundColor: "brand1.secondary.100",
color: "#fff",
},
};
}
if (stretched) {
stretchedStyles = {
width: "100%",
justifyContent: "space-between",
};
}
break;
case "outline":
baseStyles = {
backgroundColor: "transparent",
color: "brand1.secondary.300",
borderColor: "brand1.secondary.300",
_hover: {
color: "brand1.secondary.500",
borderColor: "brand1.secondary.500",
},
};
if (invert) {
invertStyles = {
color: "#c9c9c9",
borderColor: "#c9c9c9",
_hover: {
color: "#fff",
borderColor: "#fff",
},
};
}
if (stretched) {
stretchedStyles = {
width: "100%",
justifyContent: "space-between",
};
}
break;
case "danger":
baseStyles = {
backgroundColor: "brand1.danger.400",
color: "#fff",
_hover: {
backgroundColor: "brand1.danger.500",
},
};
if (invert) {
invertStyles = {
backgroundColor: "brand1.danger.50",
border: "0.25rem solid #feb6b9",
color: "#969696",
_hover: {
backgroundColor: "brand1.danger.100",
color: "#fff",
},
};
}
if (stretched) {
stretchedStyles = {
width: "100%",
justifyContent: "space-between",
};
}
break;
Now that we have our complete styles in place and in a conditional statement , let us look at how we can use this styles in our component.
By spreading our stylings into the component through the object spread operator. Take note on the order in which the styles are added.
return (
<Button
{...baseStyles}
{...props}
{...invertStyles}
{...stretchedStyles}
>
{children}
</Button>
);
}
Now about the order in which we have placed our props , baseStyles first followed by props then invertStyles and lastly stretchedStyles. The order is very important as the latter props on the list will take precedence over the styles of the earlier props on the list. Thats why we have baseStyles first , this give the developer the flexibility to deviate from the baseStyles.
To demonstrate, i will show 4 different buttons using the variant="danger" and the invert and stretched props
<Button variant="danger">Danger Button</Button>
<Button variant="danger" stretched>Danger Button</Button>
<Button variant="danger" invert>Danger Button</Button>
<Button variant="danger" invert stretched>Danger Button</Button>
Thats all folks , i hope this helps you as much as it has helped me. If theres any way i can improve on this i would really love to hear it. Drop a comment below.
Top comments (0)