Skip to content

Instantly share code, notes, and snippets.

@eldonwilliams
Last active June 18, 2023 05:58
Show Gist options
  • Save eldonwilliams/a187521c97319c3c92ae20a708a05f5a to your computer and use it in GitHub Desktop.
Save eldonwilliams/a187521c97319c3c92ae20a708a05f5a to your computer and use it in GitHub Desktop.
A cool little bit of code and how it works

What is going on here

Let's break down the thing we're trying to do...

The problem

We want to display text to the user. HTML lets us do this using p tags (short for paragraph). What if we need some text with a link inside it like

Hello, how are doing?

We cannot put links on p tags, we must put them on a tags (a is short for anchor). So we need to use the span tag (I promise this is the last one) as it is allowed to hold multiple sub-p and a tags. p tags may not have p tags inside them.

The solution

We can make a component called Typography (a fancy word for text) which will allow text and other Typography components inside it. Simple usage of a Typography component would be like so

<Typography>Hello, world!</Typography>

Which would just output "Hello, world!". So what about the harder problem of mixing a and p tags? The example above could be written as

<Typography>
  <Typography>Hello, how </Typography>
  <Typography href="google.com">are</Typography>
  <Typography> doing?</Typography>
</Typography>

Notice I wrapped all extra text in Typography components. Once a Typography component has 1 sub-Typography component, it will only accept Typography components, so you must convert all extra text to Typography. Also when this gets rendered, Typography is smart enough to convert the highest Typography to a 'span' element.

Now why make this so complicated? Well have you noticed the variants file? We can add extra, reusable styles to all these text elements.

Usually in css making a interactive text object that is bold and italic is hard, with this we can do it this quickly

<Typography interactive italic bold>Easy!</Typography>

Literally, all we do is put it there and it will appear that way.

Another benefit is refactoring which is a big word for changing your mind later. What if we decide we want to change the color of primary to blue half way through the project? With this solution, we just need to change TypographyVariant.ts' color property, and all Typography components will be updated.

import React from "react";
/**
* The props for the Typography component.
*/
type TypographyProps = {
/**
* The content of the Typography component.
*/
children?: string | JSX.Element | JSX.Element[];
/**
* The tag to use for the Typography component.
*/
element?: keyof JSX.IntrinsicElements | React.ComponentType<any>,
} & JSX.IntrinsicElements['p'];
/**
* Typography components are for showing text.
* Typography components should idealy only contain strings or other Typography components - for composition.
* Typography are p tags by default, but can be changed to other tags.
* If they are being composed, the parent element by default becomes a span tag.
*
* @returns
*/
export default function Typography(props: TypographyProps) {
let { children = "", element, ...rest } = props;
element ??= typeof children !== "string" ? "span" : "p"; // If children is not a string, then it is being composed, so we use a span tag.
return React.createElement(element, { ...rest, }, children);
}
import { tv } from "tailwind-variants"
export const TypographyVariant = tv({
base: '',
variants: {
bold: {
true: 'font-bold',
},
italic: {
true: 'italic',
},
interactive: {
true: 'cursor-pointer',
},
selectable: {
true: 'select-auto',
false: 'select-none',
},
color: {
primary: 'font-black dark:font-white',
}
},
defaultVariants: {
bold: false,
italic: false,
interactive: false,
selectable: true,
color: 'primary',
}
});
export default TypographyVariant;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment