Korean Label Handling and Hangul Font Requirements in diagram-design: A Complete Guide

Korean (Hangul) labels in diagram-design are managed through a strict style guide that mandates specific font stacks, a 12 px minimum size floor, and per-character width budgeting using 1 em per full-width CJK character.

The cathrynlavery/diagram-design repository treats Korean text as a first-class label type with dedicated handling rules. This article breaks down the font requirements, sizing constraints, and rendering logic defined in /skills/diagram-design/references/style-guide.md and enforced by verify-treemap.py.


Primary Hangul Font Stack

The diagram templates load Noto Sans KR as the primary Korean typeface alongside the default Latin families. According to the style guide, the complete web-font stack includes:

  • Geist and Geist Mono (default Latin families)
  • Instrument Serif (default serif)
  • Noto Sans KR and Noto Serif KR (Korean-specific Google Fonts)

This combination ensures Hangul glyphs render correctly whether the diagram is viewed online with live web fonts or offline with bundled fallbacks. The Google Fonts stylesheet is preloaded in skills/diagram-design/assets/template-full.html across all four diagram templates.


Element-Level Font Family Extensions

Because Geist contains no Hangul glyphs, any <text> element with Korean characters must explicitly extend its font-family declaration. The recommended stack from line 94 of the style guide:

<text font-family="'Geist', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif">
  결제 서비스
</text>

The fallback chain covers all major platforms:

  • Noto Sans KR — Primary web font (Google Fonts)
  • Apple SD Gothic Neo — macOS system fallback
  • Malgun Gothic — Windows system fallback

For mixed Latin/Korean titles using the serif family, the stack switches to 'Instrument Serif', 'Noto Serif KR', serif' so Korean portions fall back to the matching Korean serif face.


Per-Character Width Budgeting

Korean label handling includes precise width calculations for layout stability. The verify-treemap.py script enforces these rules during CI:

Character Type Advance Width
Full-width CJK (Hangul, Hanja, Kana) 1.0 em
Latin (Geist default) ~0.60 em
Monospace (Geist Mono) ~0.62 em

The total label width equals the sum of per-character advances multiplied by font size, plus padding, rounded up to the next multiple of 4. This prevents overflow in treemap cells and maintains the 4 px grid alignment used throughout the design system.


Three Mandatory Rules for Korean Labels

The style guide defines three non-negotiable rules for Hangul text at line 103:

1. Sublabels Stay Latin

Technical sub-labels (ports, protocols, URLs) must remain in Geist Mono at 9 px. Hangul is never rendered at this small mono size because readability collapses. If a Korean name includes technical metadata, the Korean portion separates to a larger non-mono line.

2. 12 px Minimum Size Floor

Hangul must never appear smaller than 12 px. When a Korean name would require a smaller font to fit, the name is truncated instead. This rule protects stroke clarity in complex Hangul syllables.

3. Arrow Labels Switch Register

Arrow labels, eyebrows, and legend text normally use 7–8 px uppercase Geist Mono. When Hangul appears in these slots, the rendering changes:

Property Latin Default Hangul Override
Font family Geist Mono Sans-serif stack with Noto Sans KR
Font size 7–8 px 12 px
Text transform uppercase none
Mask rectangle height 8 px 16 px (rounded to multiple of 4)

This register switch is documented at line 107 of the style guide.


Skill Manifest Integration

The Korean label requirements are discoverable at runtime through SKILL.md line 202, which points agents to the dedicated Korean-label section. This integration ensures that automated diagram generation follows the same constraints as manually authored content.


Practical Code Examples

Korean SVG Label with Proper Font Stack

<svg width="200" height="40" xmlns="http://www.w3.org/2000/svg">
  <text x="10" y="30"
        font-family="'Geist', 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif"
        font-size="12px">
    결제 서비스
  </text>
</svg>

Width Budget Calculation (verify-treemap.py Logic)

def label_width(text, font_size_px):
    em_per_char = {
        'wide': 1.0,          # Hangul, CJK full-width

        'latin': 0.60,        # Geist default

        'mono': 0.62          # Geist Mono default

    }
    width_em = 0
    for ch in text:
        if is_full_width(ch):                # Hangul or other CJK

            width_em += em_per_char['wide']
        elif is_mono_context():             # technical sublabel

            width_em += em_per_char['mono']
        else:
            width_em += em_per_char['latin']
    # padding & rounding to next multiple of 4

    raw_px = width_em * font_size_px
    padded_px = raw_px + 8                # 4 px left + 4 px right

    return ((padded_px + 3) // 4) * 4

Arrow Label Fallback for Hangul

<text font-family="'Geist Mono', 'Noto Sans KR', sans-serif"
      font-size="12px"
      text-transform="none"
      letter-spacing="0">
  인증
</text>


Key Source Files

File Role
skills/diagram-design/references/style-guide.md Defines Korean font stack, width budgeting, and Hangul rendering rules
SKILL.md Runtime agent reference pointing to Korean-label requirements
scripts/verify-treemap.py CI enforcement of 1 em per full-width character rule
skills/diagram-design/assets/template-full.html Template including Korean and Latin font preloads

Summary

  • Font requirement: Noto Sans KR (sans) and Noto Serif KR (serif) must load alongside Geist families
  • Element rule: Korean <text> elements extend font-family with platform fallbacks
  • Sizing rule: 12 px minimum for Hangul; truncate rather than shrink
  • Width rule: 1 em per full-width CJK character, rounded to 4 px grid
  • Register switch: Arrow labels and small elements switch from 7–8 px mono to 12 px sans when containing Korean

Frequently Asked Questions

What happens if a Korean label is too long for its container?

The name is truncated rather than rendered below 12 px. The style guide mandates this floor to preserve Hangul stroke clarity, and verify-treemap.py validates container fits during CI.

Can I use a different Korean font instead of Noto Sans KR?

The style guide specifies Noto Sans KR as the primary web font for consistency across diagrams. System fallbacks (Apple SD Gothic Neo, Malgun Gothic) are acceptable for offline scenarios, but the Google Fonts link must remain in templates.

Why do technical sub-labels stay in monospace even when the main label is Korean?

Readability of Hangul at 9 px monospace collapses due to dense stroke patterns. The mono size is reserved for Latin technical content (ports, URLs) that benefits from fixed-width alignment, while Korean names render at 12 px in proportional sans.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →