Repository navigation
fix: make in-app survey text follow Dynamic Type [ENG-3427] - #62
Conversation
WKWebView never applies Dynamic Type to web content, and the survey renderer sizes its text in CSS pixels, so respondents with a larger text size saw default-sized surveys. The template now sets -webkit-text-size-adjust on the root element from the user's content size category: WebKit multiplies it into every computed font size, as Android's WebView already does with the system font scale. The default size emits `auto` (the initial value), so the page renders as before. Accessibility sizes are capped at 200%, Android's largest font scale, which the survey layout is known to fit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WebKit resolves em lengths against the adjusted font size and the renderer compiles its spacing to em, so padding grows with the text on iOS, unlike Android's text-only zoom. The 2x cap still fits every question type on a phone-width card. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Generated by Claude Code |
UIFontMetrics scales a custom 17 pt size to 131% at xxxLarge, not the 135% the system body-size table gives, so the exact-value assertion failed on CI. The test now checks that the largest standard size grows the text without reaching the cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependabot's group bump (#61) moved it to v8.2.2, which verifies the scanner's GPG signature against keyservers this harden-runner macOS runner cannot reach, so the SonarCloud step fails on every run. The comment above the pin already explains why it is held at v6.0.0. This restores that pin; it is a no-op once main carries the same revert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Not this PR's: #61 (dependabot, merged as Ported the fix, the revert of that one line, in Generated by Claude Code |
…rd0x # Conflicts: # Sources/FormbricksSDK/WebView/FormbricksViewModel.swift
UIApplication.preferredContentSizeCategory ignores trait overrides, so a host app that caps Dynamic Type on its window or view controller still got a 200% survey. The view model now reads the category from the trait source it already receives for the appearance (the key window, or the presenting view controller), and without one the page keeps the default size, so HTML-building tests no longer depend on the simulator setting. The tests check the value alone instead of the whole CSS rule, and set the SDK up only in the test that builds a page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full-size iPads load WKWebView content in desktop-class mode, where WebKit ignores -webkit-text-size-adjust, so the survey text stayed at the default size there. The survey WebView now asks for mobile content. At the default text size the iPad render is unchanged (pixel-identical on an iPad Pro 13" simulator); the user agent now reports an iPad instead of a Mac. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|



Ref ENG-3427
What & why
Was: in-app surveys on iOS rendered at the default text size whatever the respondent chose under Larger Text. WKWebView never applies Dynamic Type to web content.
Now: the survey's text follows Dynamic Type both ways, up to 200% at the accessibility sizes, on iPhone and iPad. The default size is unchanged.
Android needs no change: its WebView already scales page text by the system font scale unless the app calls
setTextZoom, which the Android SDK never does (AwSettings.updateFontScaleLocked,aw_settings.cc→SetTextZoomFactor).Where to look
WebView/SurveyTextScale.swift— Dynamic Type →-webkit-text-size-adjust, and the 200% capWebView/FormbricksViewModel.swift— where the value enters the page, read from the presenter's traitsWebView/SurveyWebView.swift— mobile content mode, so full-size iPads honour the valueDesign points worth checking
Why
-webkit-text-size-adjust. WebKit multiplies it into every computed font size (updateFontForTextSizeAdjust), andemlengths resolve against the adjusted size (resolveEm). The renderer compiles its spacing toem, so on iOS padding and gaps grow with the text while pixel-sized parts keep theirs. Android's text zoom grows only the text.WKWebView.pageZoomwas the alternative: it scales everything, shrinks the layout viewport to ~190 CSS px at 2×, and would need the card rect for touch passthrough rescaled.The 200% cap. It is Android's largest font scale and WCAG 1.4.4's resize target. Every question type checked fits a 402 pt iPhone card at 2× (screenshots); the matrix scrolls sideways. iOS goes to ~3× (AX5), so the largest accessibility sizes get 200%. It's a product call — one constant,
maximumFactor.Scale source.
UIFontMetrics(forTextStyle: .body)— Apple's API for scaling custom font sizes — so xxxLarge gives 131%, slightly under the system body text's 135%.Whose setting. The category comes from the trait source
PresentSurveyManageralready passes for the appearance: the host's key window (no overlay) or the presenting view controller (overlay). A host that overrides Dynamic Type there withtraitOverridescaps the survey too. A SwiftUI-only.dynamicTypeSize(...)cap never reaches UIKit traits, so the SDK can't see it; such apps need to set the same cap intraitOverrides. Without a trait source the page keeps the default size.Mobile content mode. Full-size iPads load WKWebView content in desktop-class mode, where WebKit ignores the percentage. The survey WebView asks for
.mobile. At the default size the iPad render is pixel-identical tomain; the user agent now says iPad instead of Mac.Default size is untouched. At "Large" the template emits
auto, the property's initial value, not100%. Pixel-identical tomainon iPhone and iPad.Read once, at presentation. A Dynamic Type change while a survey is open applies to the next survey.
The renderer must not set it. It doesn't today: its only rule is
#fbjs html { -webkit-text-size-adjust: 100% }, which matches nothing. If that selector were ever "fixed" tohtml, it would override this.Coverage
auto)testTheDefaultSizeLeavesThePageUntouched, return"\(percent)%"unconditionally atSurveyTextScale.swift:43testTheTextFollowsTheSettingInBothDirections, return1atSurveyTextScale.swift:36testTheAccessibilitySizesStopAtTheCap, drop theminatSurveyTextScale.swift:36autotestTheSurveyPageCarriesThePresentersSetting, readUIApplication.preferredContentSizeCategoryinstead atFormbricksViewModel.swift:26(red)testTheSurveyWebViewAsksForMobileContent, drop the line atSurveyWebView.swift:33(red)packages/surveys; computed styles read in the page, 0 pixels differ at Large (screenshots)traitOverridescaps the surveyauto(screenshot)Rerun:
xcodebuild test -scheme FormbricksSDK -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' -only-testing:FormbricksSDKTests/SurveyTextScaleTestsScreenshots — iOS 26.5 simulators, the SDK in a host app, the survey renderer built from
packages/surveysmainvs this PR, same survey and host, only the SDK differs.mainautomain(desktop-class mode)traitOverridesat Large → survey at default size.dynamicTypeSize(...DynamicTypeSize.large)only → not visible to UIKit, survey at 200%Open gaps
emline-heights twice under a percentage text-size-adjust: once through the adjusted font, once by the factor. The renderer'sleading-*compile tocalc(var(--spacing) * N)with--spacing: .25em, so at 200% headlines get a 96 px line-height for 32 px text instead of 48 px, and buttons 64 px. Unitless and pixel line-heights scale correctly, and the iPad in mobile mode is unaffected. The fix is unitlessleading-*inpackages/surveys; follow-up, not in this PR.PassthroughPresentationTests' 3 s wait times out intermittently onmain(comments below).Breaking changes
No public API change. Surveys on full-size iPads now load in mobile content mode, so their user agent says iPad instead of Mac; the layout at the default size is unchanged.
Note
AI model used —
claude-opus-5-5.