I am working on a React project where I need to animate individual letters in a string, so I am splitting the string into separate span
elements for each letter. However, I'm facing an issue where the text looks different when split compared to when it is rendered normally as a continuous string.
Note: The text is split until character level, with each character wrapped with span
.
Here is a simplified version of my code:
import { ReactNode } from "react";
export function splitWord(input: string): string[] {
return input.match(/\S+|\s+/g) || [];
}
export function splitLetter(input: string): string[] {
const characters: string[] = [];
const regex = /[\s\S]/gu;
let match;
while ((match = regex.exec(input)) !== null) {
characters.push(match[0]);
}
return characters;
}
export function splitStringWithSpan(input: string): ReactNode {
return (
<>
{splitWord(input).map((wordOrSpace, wordIndex) => (
<span
className="inline-block [&>*]:overflow-y-clip [&>*]:inline-flex leading-tight"
key={wordIndex}
>
{splitLetter(wordOrSpace).map((letter, letterIndex) => (
<span key={letterIndex}>
<span>{letter === " " ? "\u00A0" : letter}</span>
</span>
))}
</span>
))}
</>
);
}
const Help = () => {
return (
<div className="text-5xl font-semibold flex flex-col">
<div>
<div className="text-xs">Splitted</div>
{splitStringWithSpan("Young")}
</div>
<div>
<div className="text-xs">Not Splitted</div>Young
</div>
</div>
);
};
export default Help;
In the code, I render the text "Young" in two ways:
splitStringWithSpan
.example The splitted text looks slightly wider from the non-split text. You can see there's slightly different width at the character "Y" in the example.
How can I make the splitted text look the same as the non-split text in terms of character width and spacing?
I tried changing the max width of each individual character to
max-width: 0.94em;
It works for some character like "W", but still can't handle all character's spacing.
There isn't any extra width in the splitted text.
The lack of the gap between the Y and the o in the non-splitted text is a result of kerning pairs in the font you are using. If you set a different font (through font-mono
or similar), the rendering will become consistent between both versions.
The horizontal spacing is disrupted in the splitted version because you set [&>*]:inline-flex
on the individual letter spans, which forces each to be an individual container and prevents the kerning pairing from taking place. You can see this by either removing [&>*]:inline-flex
, or by splitting on every second letter (which causes the first span to contain Yo
together), either of which allow the kerning to happen properly (removing the extra space).
Possible fixes - which is correct will depend on the context you are using this in:
[&>*]:inline-flex
- not sure why it's necessary here