agentsclimarketplace

International typography

Skill jpoindexter/typography-skills/skills/international-typography

Typography skills for AI coding agents — 20 focused skills + a /type router, extracted from 10 canonical typography books and reconciled against WCAG 2.2 and current browser behavior.

Install
npx -y skills add jpoindexter/typography-skills --skill international-typography

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 10 days oldThe repository was created 10 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use whenever type will render in a non-Latin script or a language other than English — Arabic, Hebrew, CJK, Indic, Cyrillic, Greek, Thai. Also use for bidirectional text, vertical writing, localisation-driven layout breakage, font stacks for multiple scripts, and any assumption that Latin typographic rules generalise.

SKILL.md

9.8 KB, as published. Nobody here has run it

International typography

The default assumption in every other skill is Latin script. That assumption is wrong for most of the world's readers, and it fails in ways that are invisible to a monolingual reviewer.

1. When to invoke

  • Any product supporting a non-Latin script, now or planned.
  • Bidirectional text (Arabic, Hebrew) mixed with Latin.
  • CJK, Indic, Thai, or vertical writing.
  • Layout breaking after localisation.
  • Building a font stack that must cover several scripts.

Always co-load for any non-English or multi-script product.

2. Required context

  • Every script in scope, and any planned.
  • Whether text mixes scripts within a line (numerals, brand names, code).
  • Whether content is user-generated (you cannot predict the script).
  • Whether the font covers each script, or a per-script stack is needed.
  • Locale-specific conventions: numerals, quotation marks, date formats.

3. Invariant principles

  • Latin typographic rules do not generalise. The Latin axes are cap height, x-height, and baseline. Other writing systems have different points of focus: many Indic scripts hang from a headline; Chinese characters gravitate to the heart (a centred square); Japanese characters occupy a square; Indic and Arabic emphasize horizontal flow. (Lupton)
  • "x-height" and "cap height" are meaningless in scripts without those features. So is any rule derived from them, including much size- and leading-matching advice.
  • Measure in characters does not transfer. A CJK line of 40 characters carries far more content than 40 Latin characters; Arabic and Devanagari have different densities again. Use per-script guidance, not a translated CPL number.
  • Line-height needs are script-specific and usually larger than Latin. Devanagari and Thai have tall stacked marks above and below; CJK needs space to avoid crowding. Latin-tuned leading clips or crowds them.
  • Never hardcode direction. Use logical CSS properties so RTL works without a parallel stylesheet.
  • Never synthesize. Faux bold and faux italic are wrong in Latin and actively destructive in Arabic (which is cursive and connected) and CJK.
  • Italic is not universal. Many scripts have no italic tradition; emphasis is carried differently.

4. Context-dependent heuristics

Use logical properties everywhere.

Physical (avoid)Logical (use)
margin-leftmargin-inline-start
padding-rightpadding-inline-end
text-align: lefttext-align: start
border-leftborder-inline-start
width / heightinline-size / block-size
top / bottominset-block-start / inset-block-end

This makes a single stylesheet work in both LTR and RTL.

Mark up language and direction. <html lang="ar" dir="rtl">. Set lang on any inline span in a different language — it drives font selection, hyphenation, line-breaking, and screen-reader pronunciation.

Bidi. Mixed RTL/LTR text (an Arabic sentence containing a Latin brand name or a number) is resolved by the Unicode bidirectional algorithm. Use dir="auto" on user-generated content, and isolate embedded runs with <bdi> or unicode-bidi: isolate. Do not attempt to reorder characters manually.

Per-script font stacks. One font rarely covers several scripts well. Use unicode-range to serve a different file per script, which also prevents downloading a CJK font for a Latin-only page:

@font-face { font-family: App; src: url(latin.woff2);
             unicode-range: U+0000-00FF, U+0131, U+2000-206F; }
@font-face { font-family: App; src: url(arabic.woff2);
             unicode-range: U+0600-06FF, U+FB50-FDFF; }

CJK specifics. Fonts are very large — subsetting and unicode-range matter far more. Characters are monospaced-square, so justification behaves differently and works well. Line-breaking can occur between most characters, but line-break and word-break control kinsoku rules (characters forbidden at line start/end). Vertical writing via writing-mode: vertical-rl needs text-orientation handled for embedded Latin.

Arabic specifics. Cursive and connected; letters change shape by position (initial/medial/final/isolated). Never letterspace Arabic — it breaks the connections. Never synthesize bold. Arabic needs more line-height than Latin; justification uses kashida elongation, not word-space stretching.

Indic specifics. Complex shaping with conjuncts, reordered vowel signs, and marks above and below the headline. Requires proper shaping support; needs generous line-height. Never assume character order equals visual order.

Localisation length. German and Finnish run ~30–40% longer than English; Japanese and Chinese often much shorter. Design containers to grow. See ui-component-typography.

5. Failure patterns

PatternCauseFix
RTL layout mirrored wrong or not at allPhysical propertiesLogical properties + dir
Arabic looks like disconnected lettersLetterspacing applied, or wrong fontRemove tracking; use a real Arabic face
Devanagari/Thai marks clippedLatin-tuned line-heightIncrease; test with real strings
Tofu (□) for some charactersFont lacks coveragePer-script unicode-range stack
CJK font downloaded on an English pageNo unicode-rangeSplit by range
Arabic bold looks smearedFaux bold synthesisShip a real bold; font-synthesis: none
Brand name inside Arabic text appears in the wrong placeBidi not isolated<bdi> / unicode-bidi: isolate
Measure guidance applied to CJKLatin CPL assumed universalPer-script guidance
Screen reader reads Japanese with English phoneticsMissing langSet lang on the element

6. Evaluation procedure

  1. List every script in scope. For each, confirm the font actually covers it.
  2. Render real text in each script — never lorem ipsum, never transliteration.
  3. Switch dir="rtl" and inspect the whole layout. Anything not mirrored is using a physical property.
  4. Check marks and extenders aren't clipped — Devanagari, Thai, Vietnamese, Arabic diacritics.
  5. Test mixed-direction strings: Arabic sentence + Latin brand + digits.
  6. Confirm lang and dir are set correctly, including on inline runs.
  7. Confirm font-synthesis: none where synthesis would be wrong.
  8. Check the longest localisation of every UI string.
  9. Verify network: does a Latin-only page avoid downloading CJK/Arabic files?

7. Output format

Scripts in scope: <list>
Coverage: <script> → <font file> via unicode-range <range> | GAP
Direction: <ltr|rtl|both> — logical properties used: <yes/no>
lang/dir set: <where>
Bidi isolation: <yes/n-a>
Line-height per script: <script>: <value> — <verified with real text?>
Synthesis disabled: <yes/no>
Payload: <script>: <n>KB — conditionally loaded: <yes/no>
Not tested: <scripts not rendered with real content>

8. Examples

Product adding Arabic to an existing English interface.

This isn't a translation task, it's a layout task. Three things must change before any copy lands. First, every physical CSS property becomes logical — otherwise the RTL layout won't mirror. Second, a real Arabic face loaded by unicode-range (U+0600–06FF, U+FB50–FDFF), with font-synthesis: none, because faux bold on connected cursive script smears the joins. Third, line-height rises for Arabic runs; the Latin-tuned value clips diacritics. Then test with a real Arabic sentence containing the Latin brand name, isolated with <bdi> so bidi doesn't reorder it.

9. Counterexamples

  • ❌ "We'll use the same font for all scripts." — Very few faces cover Latin, Arabic, CJK, and Indic well.
  • ❌ "Measure is 45–75 characters, so keep CJK lines to 60." — CPL doesn't transfer across scripts.
  • ❌ "Letterspace the Arabic heading to match the Latin one." — Breaks cursive connections.
  • text-align: left in a stylesheet that must serve RTL.
  • ❌ "Bold it — the browser will synthesize if the weight is missing." — Wrong in Latin, destructive in Arabic and CJK.
  • ❌ Testing RTL with reversed English placeholder text.
  • ❌ "Indic is fine, the characters render." — Rendering ≠ correct shaping; conjuncts and reordered vowel signs must be verified by a reader of the script.

10. Source citations

  • Lupton, Thinking with Type 3e — primary book source. "The main axes of alignment in Latin typography are the cap height, x-height, and baseline. Other writing systems have different points of focus. Many Indic scripts hang from a headline. Chinese characters gravitate to the heart. Japanese characters occupy a square, while Indic and Arabic scripts emphasize horizontal flow."
  • W3C Internationalizationw3.org/International — authoritative for script behavior, bidi, and vertical text. Outranks the books here (source-hierarchy rule 1).
  • MDN — logical properties, unicode-range, writing-mode, font-synthesis.
  • Latin, Better Web Typography — language ranges and why the Latin range isn't always sufficient.
  • Rutter, Web Typographyunicode-range and subsetting by script.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.