{"slug": "i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times", "title": "I set the font to the largest size and found the same bug eleven times", "summary": "A developer found the same layout bug eleven times in an iOS app by setting Dynamic Type to XXXL and screenshotting every screen. The root cause was a shared row component that pinned a value to the right, leaving too little space for labels, which broke worse in Japanese due to character wrapping. The fix involved moving values to a second line while keeping affordances on the right, and the developer emphasized verifying convergence by actually viewing screens rather than relying on tests.", "body_md": "I was cleaning up the UI on a side-project iOS app and did one thing: **set Dynamic Type to XXXL and screenshot every screen.**\n\n**Reading the code had turned up nothing. The screenshots showed problems immediately.**\n\nEleven of them, in the end. **All the same cause.**\n\nHere's the conclusion first. **\"A parent that pins things side by side\" × \"text that grows\" is not a bug, it's a pattern.** And in Japanese it breaks reliably worse than in English.\n\nSame layout, different failure depending on language.\n\n**English** wraps at word boundaries and, failing that, ends in `…`\n\n. You can't read it, but you can tell something was cut.\n\n**Japanese doesn't do that.** It can break between almost any two characters, so once a column is squeezed to one character wide, you get **one character per line, stacked downward.**\n\nActual output:\n\n| Rendered | Intended |\n|---|---|\n`Paire / d / Macs` |\nPaired Macs (broken mid-word) |\n`Claud / e / Code` |\nClaude Code |\n| One character per line | \"実行中のセッション\" (Running sessions) |\n| Three step labels stacked vertically | A three-step horizontal stepper |\n`De / mo` , turning the capsule into a circle |\nA \"Demo\" badge |\n\n`Paire / d / Macs`\n\nis English breaking mid-word. Once the column has only a few characters left, even English gets there. **Japanese gets there much earlier.**\n\nNearly all eleven were this:\n\n```\nHStack {\n  Image(systemName: icon).frame(width: 44)   // fixed\n  Text(label)\n  Spacer()\n  Text(value)                                 // pinned right\n}\n```\n\n**The 44pt icon and the trailing value claim their width first, leaving the label column a few characters.**\n\nThe fix: rows that carry a value drop the value to the line below — but **affordances like chevrons and toggles stay on the right.**\n\nThat row component was shared across the whole settings tree, so **fixing one place fixed the entire settings screen.** Which also means one decision inside a shared component was breaking eleven screens.\n\n`ViewThatFits`\n\nis not a general answer\nI used `ViewThatFits`\n\nto switch to a stacked layout. **It broke the standard size.**\n\n`ViewThatFits`\n\ndecides using each child's **ideal width**. A title that wraps reports **its unwrapped full length** as its ideal width. So the side-by-side candidate was judged \"doesn't fit\" and the badge dropped below the title *at the standard size.*\n\n**I could not have reasoned my way to this. I found it by screenshotting the standard size.**\n\nWhere each one belongs:\n\n`ViewThatFits`\n\n`isAccessibilitySize`\n\nbranchI reversed one of my own calls partway through.\n\nI'd classified a `.menu`\n\nPicker with Toggle, as something that belongs pinned right. **A screenshot of the audit-log screen disproved that** — the filter label came out as `絞り… / 込み`\n\n.\n\nThe rule I ended up with:\n\n`.menu`\n\nPicker, chevron)I had a `UITEST_FORCE_ACCESSIBILITY_XXXL`\n\nenv var for UI tests. It applies `.dynamicTypeSize`\n\nto the root view.\n\n**That does not propagate into a .sheet's environment.**\n\nSo the paywall and the setup guide had **never once been rendered at the largest text size.** What was passing was \"the tests are green,\" not \"the modals were checked.\"\n\nNow it's applied from the system side:\n\n```\nxcrun simctl ui <udid> content_size accessibility-extra-extra-extra-large\n```\n\nAfter fixing six, I wrote that the pattern had converged. **It hadn't.**\n\nThere were simply screens I hadn't looked at. When I did, four more appeared — numbers seven through ten. Then I went through the QR scanner, host-add and pairing flows in Japanese at XXXL, confirmed **zero new findings**, and only then called it converged at eleven.\n\n**The lesson is: don't declare convergence until you've actually looked at the screens.** Judge by how many screens you looked at, not by how many you fixed.\n\nWhile reading the audit log, `1 items need attention`\n\ncaught my eye. I swept the strings: of **38 English strings with a quantity placeholder, zero had plural variants.**\n\n`1 items need attention`\n\n`1 saved Macs`\n\n`1 days free`\n\n(on the paywall)`1 sessions, 1 active…`\n\n— Thirteen keys got variants. Japanese has no plural agreement, so nothing changed there. Strings like `%d of %d Macs`\n\n, where the number doesn't agree with the noun, were deliberately left alone.\n\nThe verification order mattered. Add one key, build, confirm `1 item needs attention`\n\non a real screen, *then* roll out the rest. Finally, **read en.lproj/Localizable.stringsdict out of the build product** and confirm\n\n`NSStringPluralRuleType`\n\nwas generated correctly.Recorded honestly, on the principle of not manufacturing work:\n\n`ScrollView`\n\nbehaviour)`.large`\n\nnavigation title layout — the same as Apple's own apps`ViewThatFits`\n\njudges on ideal width.Reading the code found zero of the eleven.\n\nSeparately from this, I build a desktop AI agent called [Wisp](https://rengaworks.gumroad.com/l/wisp?utm_source=devto&utm_medium=article&utm_campaign=xxxl-broke-japanese-worse).\n\nIt stands on your desktop, answers when you talk to it, and runs commands when you ask — always showing you what it's about to do first.", "url": "https://wpnews.pro/news/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times", "canonical_source": "https://dev.to/renga154/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times-496m", "published_at": "2026-08-16 00:01:23+00:00", "updated_at": "2026-08-16 00:11:21.364539+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["iOS", "Dynamic Type", "SwiftUI", "ViewThatFits", "Claude Code", "Macs"], "alternates": {"html": "https://wpnews.pro/news/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times", "markdown": "https://wpnews.pro/news/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times.md", "text": "https://wpnews.pro/news/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times.txt", "jsonld": "https://wpnews.pro/news/i-set-the-font-to-the-largest-size-and-found-the-same-bug-eleven-times.jsonld"}}