Building Our Extremely Lite styled-components
styled-components
is great when it comes to css-in-js solution. Now we will be doing extremely basic styled-components
implementation to understand how it works.
Letโs start!
An example styled-components
usage is like below;
const StyledButton = styled.button`
color: pink;
`;
As you see styled-components
using template literals to apply styles.
Template literals allow us to call functions with backticks. Letโs check what i mean below: For more: tagged-template-literals
const boo = (strings: TemplateStringsArray) => {
return strings[0];
};
boo`hello` // output: hello
Our first implementation is below. We have tag
param which is our html tag, strings
is used for template-literal styles and finally react element is created based on our tag
& template-literal strings
.
We are also using css2obj
helper function to convert css strings into javascript object.
const styled = (tag) => {
return (strings) =>
({ children }) => {
const cleanCss = strings[0].replace("\n", "");
const objCss = css2obj(cleanCss);
return React.createElement(tag, { style: { ...objCss } }, children);
};
};
const StyledButton = styled("button")`
color: pink;
`;
This works fine! But i donโt want to use html tags as string here.
So i changed code a bit. Now we seperated component generation into generateComponent
function and looped through some predefined html tags to ensure type safety.
const tags: Tag[] = ["div", "button", "span", "header"];
function generateComponent(tag: Tag) {
return (strings: TemplateStringsArray) =>
({ children }: StyledComponent) => {
const cleanCss = strings[0].replace("\n", "");
const objCss = css2obj(cleanCss);
return React.createElement(tag, { style: { ...objCss } }, children);
};
}
const styled = Object.fromEntries(
tags.map((tag) => [tag, generateComponent(tag)]),
)
Now we can use it like below:
const StyledButton = styled.button`
border: none;
border-radius: 4px;
background-color: pink;
color: #111;
padding: 3px 10px;
cursor: pointer;
`;
<StyledButton>๐
๐ป Hello</StyledButton>;
Full Code ๐๐ผ
import React from "react";
import type { DetailedReactHTMLElement } from "react";
type StyledComponent = {
children: React.ReactNode;
};
type Tag = keyof JSX.IntrinsicElements;
type Styled = Record<
Tag,
(
strings: TemplateStringsArray,
) => (props: StyledComponent) => DetailedReactHTMLElement<any, HTMLElement>
>;
const tags: Tag[] = ["div", "button", "span", "header"];
function generateComponent(tag: Tag) {
return (strings: TemplateStringsArray) =>
({ children }: StyledComponent) => {
const cleanCss = strings[0].replace("\n", "");
const objCss = css2obj(cleanCss);
return React.createElement(tag, { style: { ...objCss } }, children);
};
}
export const styled = Object.fromEntries(
tags.map((tag) => [tag, generateComponent(tag)]),
) as Styled;
const css2obj = (css: string) => {
const r = /(?<=^|;)\s*([^:]+)\s*:\s*([^;]+)\s*/g,
o: Record<string, string> = {};
css.replace(r, (m, p, v) => (o[p] = v));
return o;
};