Tips for Implementers

May 17, 2026 · View on GitHub

  • CJK character contains the following characters:
    • 〰 (U+3030)
    • 〽 (U+303D)
    • 🈂 (U+1F202)
    • 🈷 (U+1F237)
    • ㊗ (U+3297)
    • ㊙ (U+3299)
  • Do not treat every character in emoji-data.txt in the below data list ("emoji character" defined in Unicode UTS #51) as emoji. It includes ASCII digits, ASCII asterisk, ASCII hash sign, copyright symbol, trademark symbol, and so on. They should not be treated as emoji unless followed by a U+FE0F. We have to extract only characters with the Emoji_Presentation label.
  • You can use /^\p{Emoji_Presentation}/u, or /^\p{Basic_Emoji}/v or /^\p{RGI_Emoji}/v in JavaScript to check if a code point is an emoji (as a default emoji presentation character or in the RGI emoji set). RGI_Emoji characters other than Basic_Emoji (basic emoji set) have multiple code points and are not CJK as of Unicode 17. Never use /^\p{Emoji}/u instead of them because it is useless due to the fact that /^\p{Emoji}/u.test("1") is true (who on earth would insist that 1 is an emoji?). The v flag is available since ES2024 and supported by Node >= 20, Chrome (Edge) >= 112, Firefox >= 116, and Safari >= 17.
    • "ES2024" as "target" and "lib" in tsconfig.json is supported by TypeScript >= 5.7, Vite >= 6, and Vitest >= 3. You should use "ESNext" instead of "ES2024" for older ecosystems.
  • There are no emojis whose East Asian Width is F or H as of Unicode 17.
  • The East Asian Width of Ideographic Variation Selector and Standard Variation Selector is A.
  • The East Asian Width of characters whose Script is Hangul can be N (U+1160–U+11FF). However, there are no characters whose Script is Hangul and East Asian Width is A or Na as of Unicode 17.
  • You can use /^\p{sc=Hangul}/u in JavaScript to check if the Script of a character is Hangul.
  • The East Asian Width of unassigned characters (e.g. U+3097) is undefined. You should follow the guideline by Unicode. Note that U+2FFFE–U+2FFFF and U+2FFFE–U+2FFFF are Noncharacter, not Reserved (Unassigned). The East Asian Width of Noncharacter does not seem to be mentioned in the specifications of the East Asian Width property. Therefore, you can treat them as W to join two product terms for U+20000–U+2FFFD and U+30000–U+3FFFD.
  • The Unicode category of Ideographic Variation Selector and Standard Variation Selector is Mn, not P or S. It means there is no Unicode punctuation character or non-CJK punctuation character that is also Standard Variation Selector or Ideographic Variation Selector.
  • You do not have to care about the existence of continuous Standard Variation Selector or Ideographic Variation Selector, or Ideographic Variation Selector preceded by *. It is up to you implementers to decide how to treat them.
  • The character two positions back is rarely referenced in real-world documents. Prioritize optimization in most cases where such a character is not needed, even if it means adding some overhead to its retrieval. It should not be retrieved unless it is actually needed.
  • If at least one of the adjacent characters are Unicode whitespaces, you do not have to check if adjacent characters are CJK characters or Unicode punctuation characters. This fact also applies to the plain CommonMark.
  • It is only necessary to determine whether the adjacent characters are CJK characters when both adjacent characters are not Unicode whitespace and the delimiter run is *.

Emphasis Marker Start/End Conditions

CMOutMInEndStart
✅️*❌️❌️
✅️*"❌️✅️
✅️*a❌️✅️
❌️*❌️✅️
❌️*❌️✅️
✅️_❌️❌️
✅️_"❌️✅️
✅️_a❌️✅️
❌️_❌️✅️
❌️_❌️✅️
✅️"*"✅️✅️
✅️"*a❌️✅️
❌️"*✅️✅️
❌️*a✅️✅️
✅️"_"✅️✅️
✅️"_a❌️✅️
❌️"_❌️✅️
❌️_a❌️✅️
✅️a*a✅️✅️
✅️a_a❌️❌️

Legend:

Symbol/TermMeaning
CM✅️ = CommonMark, ❌️ = CJK Friendly Emphasis only
OutCharacter outside (before the marker)
MMarker symbol
InCharacter inside (after the marker)
EndWhether the marker can end a range
StartWhether the marker can start a range
CharacterMeaningAlt Punctuation
Unicode whitespace characterN/A
"Non-CJK punctuation (in CommonMark, includes CJK characters)(, )
aNon-CJK character (in CommonMark, includes CJK characters)N/A
CJK punctuation,
CJK character

The above table implies:

  • If a marker is adjacent to Unicode whitespace characters, whether the marker can start/end a range does not depend on whether the adjacent characters are punctuation or CJK characters.
  • If a marker is _, whether the marker can start/end a range does not depend on whether the adjacent characters are CJK characters.

Flowchart to suppress extra processes

flowchart TD
    A["Is at least one of the adjacent characters a Unicode whitespace?"]
    B["Determine flankingness"]
    C["Retrieve the character two positions before if necessary,<br/>and identify the previous base character "]
    D["Is the delimiter run <code>_</code>?"]
    E["Check whether both adjacent &quot;character&quot;s are CJK characters"]

    A -->|Yes| B
    A -->|No| C
    C --> D
    D -->|Yes| B
    D -->|No| E
    E --> B

Logical expressions transformations

Whether the delimiter run is left-flanking

!after.space && (!(!after.cjk && after.punct) || (before.space || (!before.cjk && before.punct) || before.cjk || before.ideoVs))
= !after.space && ((after.cjk || !after.punct) || (before.space || (!before.cjk && before.punct) || before.cjk && (before.punct || true) || before.ideoVs))
= !after.space && (after.cjk || !after.punct || (before.space || (!before.cjk && before.punct) || (before.cjk && before.punct) || before.cjk || before.ideoVs))
= !after.space && (!after.punct || before.space || (!before.cjk || before.cjk) && before.punct || after.cjk || before.cjk || before.ideoVs)
= !after.space && (!after.punct || (before.space || before.punct) || (after.cjk || (before.cjk || before.ideoVs)))

c.f. CommonMark:

!after.space && (!after.punct || (before.space || before.punct))

Whether the delimiter run is right-flanking

!before.space && (!(!before.cjk && before.punct) || (after.space || (!after.cjk && after.punct) || after.cjk))
= !before.space && ((before.cjk || !before.punct) || (after.space || (!after.cjk && after.punct) || after.cjk && (after.punct || true)))
= !before.space && (before.cjk || !before.punct || (after.space || (!after.cjk && after.punct) || (after.cjk && after.punct) || after.cjk))
= !before.space && (!before.punct || after.space || (!after.cjk || after.cjk) && after.punct || before.cjk || after.cjk)
= !before.space && (!before.punct || (after.space || after.punct) || (before.cjk || after.cjk))

c.f. CommonMark:

!before.space && (!before.punct || (after.space || after.punct))