This is a development instance of xesite. Things here are probably unfinished or in drafting. Don't take anything here super seriously. If you want to share this to an online aggregator, please don't. Drafts are not finalized yet for a reason. Please don't be the reason I need to implement more advanced security than just obscurity.

The GraphicalEmoji hack

Read time in minutes: 3

Today, I tried to write a JSX component that included an emoji in it. Specifically this emoji: ⚠️.

This emoji is special because there's actually two forms of it:

  • ⚠︎ (the textual form)
  • ⚠️ (the graphical form)

EDIT: Apparently this difference isn't showing up on every browser engine the same way. Please trust me that there is a difference, one of them on my MacBook running Microsoft Edge is a text-only emoji that has no color in it. This is why I was so confused, scared, and on the verge of tears after being gaslit by my browser. God is dead because font rendering killed him.

When I was making this component, I wanted the graphical form of it. The following things did not work:

  • Adding the explicit "make this graphical" Unicode instruction: \u{FE0F}
  • Declaring the emoji as the string "\u{26A0}\u{FE0F}" and then using it as a variable: <span>{warningEmoji}</span>
  • Using the variable in a format string: <span>{`${warningEmoji}`}</span>

Turns out, this is actually a fairly widespread problem with fonts that have the textual form of emoji defined but not the graphical form of it defined. The font my blog uses is one of them, so to get the graphical ⚠️ I've been using above, I had to paste this HTML snippet:

<span style="font-family: Times New Roman">⚠️</span>

Aoi is facepalm
<Aoi> Oh god. Really? That is so, so cursed.

Yes, really. In order to make the emoji render correctly, I had to instruct the browser to render it in Times New Roman because that does not have the emoji defined. It will then fall back to the system font, giving us the ⚠️ that we truly desire.

Here is the JSX component I had to write:

export interface GraphicalEmojiProps {
  emoji: string;
}

/** Listen to me for my tale of woe:
 * Fonts are complicated. Fun fact: fonts are actually Turing-complete programs
 * that run in browsers. Yes, font rendering is really that complicated. This
 * component is a dirty, ugly, disgusting HACK that works around font
 * rendering in order to forcibly display the graphical form of an emoji.
 *
 * This works because times new roman always displays the graphical forms of
 * emoji. No, I don't know why either. It slightly scares me.
 *
 * Either way, this works and I'm not brave enough to question why.
 */
export default function GraphicalEmoji({ emoji }: GraphicalEmojiProps) {
  return <span style={{ fontFamily: 'Times New Roman' }}>{emoji}</span>;
}

This code is free as in mattress. If you decide to use it, it's your problem.

Cadey is coffee
<Cadey> I hate fonts.


This article was posted on M03 08 2023. Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.

Tags: cursed JavaScript JSX fontRendering dearGodHelpMe

This post was not WebMentioned yet. You could be the first!

The art for Mara was drawn by Selicre.

The art for Cadey was drawn by ArtZora Studios.

Some of the art for Aoi was drawn by @Sandra_Thomas01.