Text is likely the most used component in any app. Today I want to share a solid reusable Text component that is very useful in my front-end development work and should save time for you too.
The Text
component is just a styled paragraph that we can also turn into any other element like div, span, or heading by passing the as
property. Also, we can modify it any way we like, using the styles
property or wrapping it with the styled
function. Because of that flexibility, it serves as a building block for other components.
import styled, { DefaultTheme, css } from "styled-components"
const getTextColorRecord = ({ colors }: DefaultTheme) =>
({
regular: colors.text,
supporting: colors.textSupporting,
supporting2: colors.textSupporting2,
supporting3: colors.textSupporting3,
primary: colors.primary,
attention: colors.attention,
alert: colors.alert,
success: colors.success,
reversed: colors.background,
contrast: colors.contrast,
} as const)
type TextWeight = "regular" | "semibold" | "bold"
const fontWeight: Record<TextWeight, number> = {
regular: 400,
semibold: 500,
bold: 600,
}
type TextHeight = "small" | "regular" | "large"
const lineHeight: Record<TextHeight, number> = {
small: 1,
regular: 1.2,
large: 1.5,
}
export type TextColor = keyof ReturnType<typeof getTextColorRecord>
export interface TextProps {
color?: TextColor
weight?: TextWeight
size?: number
height?: TextHeight
centered?: boolean
cropped?: boolean
nowrap?: boolean
}
export const oneRemInPx = 16
const getFontSize = (sizeInPx: number) => {
const sizeInRem = sizeInPx / oneRemInPx
return `${sizeInRem}rem`
}
export const Text = styled.p<TextProps>`
margin: 0;
padding: 0;
overflow-wrap: break-word;
${({ color, theme }) =>
color &&
css`
color: ${getTextColorRecord(theme)[color].toCssValue()};
`}
${({ weight }) =>
weight &&
css`
font-weight: ${fontWeight[weight]};
`}
${({ height }) =>
height &&
css`
line-height: ${lineHeight[height]};
`}
${({ size }) =>
size &&
css`
font-size: ${getFontSize(size)};
`}
${({ centered }) =>
centered &&
css`
text-align: center;
`}
${({ nowrap }) =>
nowrap &&
css`
white-space: nowrap;
`}
${({ cropped }) =>
cropped &&
css`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`}
`
For UI consistency we reset default browser CSS at the beginning of the Text
styling by removing margings and padding and setting overflow-wrap
to break-word
to prevent text overflow from breaking the layout. Then we continue by handling it's properties.
Let's look into extra properties that we can pass to the Text
component. While we can set set color through styles or className, most often we will use the color
property. It's a limited set of named colors, such as regular, supporting, or alert. We access all of them through the getTextColorRecord
function that in turn relies on the styled-components theme. Note, that all the names describe the color rather that saying ellow or white. That way we can change the color palette and add new themes without changing the Text
component.
The weight
property turns regular
, semibold
and bold
into 400, 500, 600. It's usually enough for most cases. But you can also extends it for you situation, andd thin
or extrabold
options for example.
The size
is an interesting and opinionated property. While we pass a pixels as a number it's being converted to rems. For us it's easier to think in pixels. We think of 16px
as a default size, and to make a smaller variant it's more intuitive to pass 14px
rather than 0.875rem
. At the same time we make the app more accessible by converting pixels to rems, so that users can change the font size in their browser settings and the app will adjust accordingly.
The height
property represents line-height
and can be small
, regular
, or large
, yet we are rarely going to use it.
While some people prefer having variants for texts, from my experience I found it more practical to rely on properties like size
and weight
to create a variety of text styles. Yet, if your project would benefit from having variants, you can add it as a property, and then have a record of styles for each variant.
Finally there are centered
, cropped
, and nowrap
helpers that are self-explanatory.