---
name: cjk-text-wrap-audit
description: >
Diagnose and fix CJK (Korean, Japanese, Chinese) text-wrapping issues
in web UIs. Systematically traces mid-syllable breaks, orphaned
glyphs, and awkward line splits through a layered fix strategy
covering global CSS cascade, component-library overrides, headline
balancing, and translation-level phrase binding. Produces an
evidence-based diagnosis and verification report before any code
change is applied.
license: MIT
compatibility:
- Claude Code
- Cursor
metadata:
type: execution
category: review
maturity: draft
estimated_time: 15 min
---
# Skill: CJK Text Wrap Audit
**Type:** Execution
## Purpose
Turn ad-hoc CJK (Korean, Japanese, Chinese) line-break firefighting into
a layered, evidence-first audit. CJK wrap bugs are a class of
cross-cutting cascade defects: a missing `word-break` rule on `body`,
an Ant Design / MUI / Mantine `cssinjs` override that wins on
specificity, an over-eager `text-wrap` algorithm, and a translation
string with no phrase boundary can each independently cause text to
shatter mid-syllable or strand a single glyph at line edge.
This skill prescribes a fixed sequence of diagnostic gates so the audit
produces an evidence-backed report — codepoint dump, computed-style
trace, and verification matrix — *before* any code is changed.
---
## When to Use
- Korean / Japanese / Chinese hero or headline text breaks mid-syllable.
- A single glyph or word is orphaned at the end or start of a line.
- The bug appears only in CJK locales while Latin locales render fine.
- A user reports an awkward break in a screenshot but the code "looks
right".
- Browser zoom (WCAG 1.4.4 Resize Text) reveals breaks that are
invisible at 100%.
- A previous "fix" used `
`, hardcoded widths, or `!important` and
the bug came back.
---
## When NOT to Use
- Pure Latin or RTL wrapping issues (use a generic CSS audit instead).
- Font-loading or glyph-substitution problems (FOUT / FOIT, missing
glyphs, fallback fonts).
- Print or PDF rendering pipelines (different break algorithm).
- Plain-text terminals or markdown `pre` blocks (wrap is a terminal
concern, not CSS).
- Layout overflow caused by fixed widths or `white-space: nowrap`,
not by wrapping behavior.
---
## Inputs Required
Do not run this skill without:
- [ ] Screenshot of the offending render at the actual breakpoint.
- [ ] Locale code and the raw translation string (file path + key).
- [ ] Path to the component file containing the element.
- [ ] Live URL or deterministic repro steps.
- [ ] CSS / component framework in use (Tailwind, Ant Design, MUI,
Mantine, Chakra, plain CSS, …).
- [ ] Browser and zoom level where the bug reproduces.
Optional but recommended:
- [ ] List of all CJK locales the project ships.
- [ ] Light/dark mode information if the affected text changes color.
- [ ] Prior commit(s) that touched the same string or component.
Without asking the user (unless unavailable), gather these inputs
directly from the repository and the running app.
---
## Output Format
Produce a structured 7-section report. Do not produce code changes
until Section 7 is approved.
1. **Symptom Report** — locale, breakpoint, browser, zoom, screenshot
reference, exact observed break (quote the broken line).
2. **Diagnosis** — codepoint dump of the raw string, computed values
of `word-break` / `overflow-wrap` / `text-wrap` / `hyphens` /
`line-break`, and the cascade trace identifying the *winning*
declaration with its specificity.
3. **Root Cause** — name the failing layer: global CSS, component
library injector, headline balancing algorithm, or translation
string itself.
4. **Fix Strategy** — which of the four layers to touch, in what
order, and *why* additional layers are or are not needed.
5. **Proposed Patch** — minimal concrete diffs (file path + before /
after). No speculative refactors.
6. **Verification Plan** — explicit matrix of zoom levels ×
breakpoints × locales × theme modes to be re-checked.
7. **Approval Gate** — explicit statement: "Awaiting approval before
applying patch." Do not edit code until the user confirms.
---
## Procedure
### Gate 0 – Symptom Capture
Collect screenshot, URL, locale code, breakpoint width, browser, and
zoom level. If any item is missing, request it before proceeding.
Quote the offending broken line verbatim from the screenshot so later
gates have an unambiguous target.
Halt if no screenshot or repro is available — visual bugs cannot be
audited from descriptions alone.
---
### Gate 1 – Raw String Forensics
Read the translation file containing the offending key. Dump the
target string as Unicode codepoints (e.g., `U+C774 U+B3D9 U+D558
U+C138 U+C694`) and annotate:
- Existing NBSP (`U+00A0`), zero-width joiner (`U+200D`), zero-width
space (`U+200B`), or soft hyphen (`U+00AD`) — any of these may
already be influencing the wrap.
- Logical phrase boundaries — mark which adjacent words form a
semantic unit that should not split (e.g., Korean 어절 / Japanese
文節 / Chinese 词组).
- Latin-script substrings — these still wrap by Latin rules even
inside a CJK string.
Output: a labeled codepoint table and a phrase-boundary map.
---
### Gate 2 – Computed Style Inspection
Read the affected component file. Then trace the computed CSS for the
broken element. Document the *winning* declaration (with selector and
specificity) for each of:
- `word-break`
- `overflow-wrap` (and legacy `word-wrap`)
- `text-wrap` (`wrap` / `nowrap` / `balance` / `pretty`)
- `hyphens`
- `line-break`
Note any `white-space` value other than `normal` — it changes wrap
semantics entirely.
If the component is wrapped by a UI library (Ant Design `Typography`,
MUI `Typography`, Mantine `Text`, Chakra `Text`, …), record the
library's injected class and where its rule comes from.
---
### Gate 3 – Override Tracing
For each library identified in Gate 2, locate the injected stylesheet
(usually via `cssinjs` / `emotion` / `stitches`) and capture the rule
that is in effect. Calculate its specificity.
Common offenders:
- Ant Design `Typography`: injects `.ant-typography { word-break:
break-word }` (specificity 0,1,0).
- MUI `Typography`: injects `.MuiTypography-root { … }` similarly.
- Tailwind preflight: `body` defaults can be overridden by any
component class.
Identify a *safe* override location and selector. Prefer:
- The doubled-class trick: `.ant-typography.ant-typography { … }`
(specificity 0,2,0) — beats the library's single-class rule
*without* `!important`.
- A scoped wrapper class on the page or layout level.
Avoid:
- `:where(.ant-typography)` — `:where()` collapses specificity to
`0,0,0`. The override will *lose* every cascade contest.
- `!important` — wins, but pollutes future overrides and is hard to
audit.
---
### Gate 4 – Layered Fix Proposal
Propose the minimum number of layers needed. Stop at the first layer
that fully resolves the symptom; only escalate if the verification
matrix in Gate 5 still has failing cells.
**Layer 1 — Global cascade.** Add a base rule on `body` (or the
nearest app-shell element):
```css
body {
word-break: keep-all;
overflow-wrap: break-word;
}
/* Always exempt monospace contexts. */
code, pre, kbd, samp {
word-break: normal;
overflow-wrap: normal;
}
```
`word-break: keep-all` tells the browser not to break *between* CJK
syllables (which is the default behavior in most engines for
historical reasons). `overflow-wrap: break-word` is the safety net
for very long Latin tokens that would otherwise overflow.
**Layer 2 — Component-library override.** If a UI library injects a
stronger rule, neutralize it at the same scope:
```css
.ant-typography.ant-typography {
word-break: keep-all;
overflow-wrap: break-word;
}
```
(Adjust the selector for whichever library is in use. Use the
doubled-class trick rather than `!important`.)
**Layer 3 — Headline-specific balancing.** For hero / headline text
where line-length aesthetics matter, add a balanced wrap on the
specific element:
```html