refactor: build CSS and HTML by interpolation instead of String(format:) - #86
Merged
Conversation
NuPlay
changed the base branch from
chore/remove-dead-css-and-noop-api
to
main
August 29, 2026 09:26
The templates were printf format strings rendered with `String(format:)`, which treats `%` as the start of a conversion. CSS is full of literal percent signs, so every one of them had to be written `%%`, and forgetting one was silent. That single choice produced three separate defects: - `imageCSS` was missing its escapes, so `max-width: 100%` shipped as `max-width: 100` - an invalid length the browser drops - and images were not constrained to the web view width. Reported in December 2025, fixed in #76. - `iframeCSS` used `%d` for a Swift `Int`, reading 32 bits of a 64-bit value. - `htmlTemplate` took seven positional arguments whose order nothing enforced, which needed a dedicated test to pin down. Interpolation removes all three classes at once: percent signs are literal, the compiler checks the types, and arguments are named. The container id and the two script message handler names are constants inside `htmlDocument(css:body:)`, so five of those seven positional arguments no longer exist at all. The seven format strings stay as deprecated public API and still work for anyone using them directly. Output is unchanged: each builder was diffed against the format string it replaces and all seven render byte-identical results, and the generated document was re-checked in a browser for JS syntax and for the `<details>` height resync. The regression test that scanned the format strings for unescaped percent signs is removed with them; it existed only to police a footgun that no longer has a live call site. Assertions on the generated output are kept.
NuPlay
force-pushed
the
refactor/interpolated-css-builders
branch
from
August 29, 2026 09:27
b42e345 to
3227a69
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The CSS and HTML templates are printf format strings rendered with
String(format:), which treats%as the start of a conversion. CSS is full of literal percent signs, so every one of them has to be written%%- and forgetting one is silent.That single choice has now produced three separate defects:
imageCSSmissing its escapes, somax-width: 100%shipped asmax-width: 100- an invalid length the browser drops. Reported December 2025iframeCSSreading 32 bits of a 64-bitInt%dinstead of%ldhtmlTemplatetook seven positional arguments and nothing enforced their orderThe regression test added in #81 exists only to police a footgun the code creates for itself.
Changes
Interpolated builders replace the format strings:
All three classes disappear at once: percent signs are literal, the compiler checks the types, and arguments are named. The container id and the two script message handler names are constants inside
htmlDocument(css:body:), so five of those seven positional arguments no longer exist.The seven format strings stay as deprecated public API and still work for anyone using them directly.
Verification
Each builder was rendered and diffed against the format string it replaces:
imageCSStextCSSiframeCSSlinkCSScssTemplatemediaCSSTemplatehtmlTemplateAll seven produce byte-identical output. The generated document was additionally re-checked in a browser for JS syntax, and the
<details>height resync from #78 was re-run against it: 91px collapsed, 165px open, matching the measured element height.The format-string scanner test is removed along with its subject - it policed a footgun that no longer has a live call site. Assertions on the generated output are kept.