Introduction
Let us continue building our chakra components using styled-components
& styled-system
. In this tutorial we will be cloning the Chakra UI Text
component.
- I would like you to first check the chakra docs for text.
- We will create the
Text
component from scratch. - All the code for this tutorial can be found here under the atom-typography-text branch.
Prerequisite
Please check the previous post where we have completed the Grid Components. Also please check the Chakra Text Component code here.
In this tutorial we will -
- Create a Text component.
- Create stories for the Text component.
Setup
- First let us create a branch, from the main branch run -
git checkout -b atom-typography-text
Create a new folder under the
atoms
folder calledtypography
. Under thetypography
folder create a new folder calledtext
.Under the
atoms/typography/text
folder we will create 2 filesindex.tsx
&text.stories.tsx
.So our folder structure stands like - src/components/atoms/typography/text.
Text Component
For our Text component we have 2 additional props,
noOfLines
andisTruncated
. Given noOfLines = 2, it will truncate the text and add an ellipses (...) on the second line. Similarly if we are to pass isTruncated it will limit the text only to one line and add an ellipses (...).Let me paste the following code -
import * as React from "react";
import styled, { CSSProperties } from "styled-components";
import shouldForwardProp from "@styled-system/should-forward-prop";
import {
compose,
display,
space,
typography,
color,
borderRadius,
layout,
system,
DisplayProps,
SpaceProps,
TypographyProps,
ColorProps,
BorderRadiusProps,
LayoutProps,
ResponsiveValue,
} from "styled-system";
interface TextOptions {
isTruncated?: boolean;
noOfLines?: number;
whiteSpace?: ResponsiveValue<CSSProperties["whiteSpace"]>;
textOverflow?: ResponsiveValue<CSSProperties["textOverflow"]>;
decoration?: ResponsiveValue<CSSProperties["textDecoration"]>;
transform?: ResponsiveValue<CSSProperties["textTransform"]>;
}
export type TextProps = DisplayProps &
SpaceProps &
TypographyProps &
ColorProps &
BorderRadiusProps &
LayoutProps &
React.ComponentPropsWithoutRef<"p"> &
TextOptions & {
as?: React.ElementType;
children?: React.ReactNode;
};
const BaseText = styled.p.withConfig({
shouldForwardProp,
})<TextProps>`
${compose(space, display, typography, color, borderRadius, layout)}
${system({
whiteSpace: true,
textOverflow: true,
decoration: {
property: "textDecoration",
},
transform: {
property: "textTransform",
},
})}
${({ noOfLines }) =>
noOfLines &&
`
display: -webkit-box;
line-clamp: ${noOfLines};
overflow: hidden;
-webkit-line-clamp: ${noOfLines};
-webkit-box-orient: vertical;
`};
`;
export const Text = React.forwardRef<HTMLParagraphElement, TextProps>(
(props, ref) => {
const { children, isTruncated, ...delegated } = props;
const truncatedProps = isTruncated
? {
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
}
: {};
return (
<BaseText ref={ref} {...truncatedProps} {...delegated}>
{children}
</BaseText>
);
}
);
export const VisuallyHiddenText = styled.span`
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
`;
Notice we have added / extended our system with the following props, whiteSpace, textOverflow, decoration (text-decoration), transform (text-transform).
Also we have regular style utility functions from styled-system like space, display, typography, layout, etc.
Also check that we have intercepted the isTruncated props and we conditionally add necessary props.
We also have an additional
VisuallyHiddenText
which we will use for accessibility purposes, example say we have a checkbox with a label and we want the screen reader to read some different text.Under
src/theme/typography.ts
paste the following -
export function fontSizesOptions() {
const options = Object.keys(typography.fontSizes);
const labels = Object.entries(typography.fontSizes).reduce(
(acc, [key, value]) => {
acc[key] = `${key} (${value})`;
return acc;
},
{}
);
return { options, labels };
}
Story
- With the above our
Text
component is completed, let us create a story. - Under the
src/components/atoms/typography/text/text.stories.tsx
file we add the below story code. - We will create 3 stories - Playground, Default, TextTypes.
import * as React from "react";
import { fontSizesOptions } from "../../../../theme/typography";
import { Text, TextProps } from ".";
export default {
title: "Atoms/Typography/Text",
};
export const Playground = {
argTypes: {
fontSize: {
name: "fontSize",
type: { name: "string", required: false },
defaultValue: "md",
description: "Font Size from theme.fontSizes",
table: {
type: { summary: "string" },
defaultValue: { summary: "-" },
},
control: {
type: "select",
...fontSizesOptions(),
},
},
isTruncated: {
name: "isTruncated",
type: { name: "boolean", required: false },
defaultValue: false,
description: "Truncate Text.",
table: {
type: { summary: "boolean" },
defaultValue: { summary: "false" },
},
},
noOfLines: {
name: "noOfLines",
type: { name: "number", required: false },
defaultValue: 0,
description: "Number of Lines to show",
table: {
type: { summary: "number" },
defaultValue: { summary: "-" },
},
},
},
render: (args: TextProps) => (
<Text {...args}>
Lorem ipsum is placeholder text commonly used in the graphic, print, and
publishing industries for previewing layouts and visual mockups.
</Text>
),
};
export const Default = {
render: () => (
<Text>
Text component is the used to render text and paragraphs within an
interface. It renders a p tag by default.
</Text>
),
};
export const TextTypes = {
argTypes: {
as: {
name: "as",
type: { name: "string", required: false },
defaultValue: "p",
description: "Element type to render.",
table: {
type: { summary: "string" },
defaultValue: { summary: "p" },
},
control: {
type: "select",
options: [
"i",
"u",
"abbr",
"cite",
"del",
"em",
"ins",
"kbd",
"mark",
"s",
"samp",
"sub",
"sup",
],
},
},
},
render: (args: TextProps) => <Text {...args}>Sample Text</Text>,
};
Now run
npm run storybook
check the stories. Under the Playground stories check the controls section play with the props, add more controls if you like.Also check the TextTypes story we are using the styled-component polymorphic
as prop
.
Build the Library
Under
atom/typography
folder create anindex.ts
file.Under the
/typography/index.ts
file and paste the following -
export * from "./text";
- Under the
atom/index.ts
file paste the following -
export * from "./layout";
export * from "./typography";
Now
npm run build
.Under the folder
example/src/App.tsx
we can test ourText
component. Copy paste the following code and runnpm run start
from theexample
directory.
import * as React from "react";
import { Text } from "chakra-ui-clone";
export function App() {
return (
<>
<Text fontSize="50px" color="tomato">
I'm using a custom font-size value for this text
</Text>
<Text as="i">Italic</Text>
<br />
<Text as="u">Underline</Text>
<br />
<Text as="s">Strike Through</Text>
</>
);
}
Summary
There you go guys in this tutorial we created Text
components just like chakra ui and stories for them. You can find the code for this tutorial under the atom-typography-text branch here. In the next tutorial we will create a Heading component. Until next time PEACE.
Top comments (0)