Repository navigation
feat(swift): theme a live view with an app's own colors - #135
Merged
Merged
Conversation
tmm
force-pushed
the
swift-theme-live-view
branch
2 times, most recently
from
November 14, 2026 16:29
48cb366 to
e4a8d01
Compare
TextKitColor(uiColor:) and TextKitColor(nsColor:) make a theme color from a UIColor or NSColor, resolved in the light and dark appearances into the color and its dark variant, so a dynamic color keeps following the appearance. The labels follow SwiftUI's Color(uiColor:) and Color(nsColor:), which replaced its unlabeled initializers. LexicalUITextView and LexicalNSTextView get a theme property that draws the document again when it changes, and LexicalEditor.theme(_:) now sets it instead of its private applyTheme. There is no Dynamic Type opt-out flag: SwiftUI's .dynamicTypeSize(.large) and UIKit's traitOverrides.preferredContentSizeCategory reach the view, as the new tests check on the iOS Simulator. DESIGN.md and the view docs say so. Amp-Thread-ID: https://ampcode.com/threads/T-01a1d884-217a-7785-8897-c425a5f1a37d
tmm
force-pushed
the
swift-theme-live-view
branch
from
November 14, 2026 16:39
e4a8d01 to
f82b028
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.
Closes #111, as approved in the API proposals doc: parts 1 and 2, and no Dynamic Type flag for part 3.
What changes
TextKitColor(uiColor:)/TextKitColor(nsColor:)(inLexicalUIKit/LexicalAppKit, soLexicalTextKitstill builds on Linux). The color is resolved in the light and dark appearances into the color and itswithDarkvariant, so a dynamic color (.label, an asset catalog color, a SwiftUIColorthroughUIColor(_:)) keeps following the appearance. A color that is the same in both gets no dark variant. High-contrast variants are lost, and the doc comment says so.TextKitColor(_ color: UIColor). SwiftUI deprecatedColor(_: UIColor)/Color(_: NSColor)in favor ofColor(uiColor:)/Color(nsColor:), so the labels match current Apple API, andTextKitColor(.label)would read like a CSS color anyway.themeproperty onLexicalUITextViewandLexicalNSTextView. Setting a different theme draws the document again; setting an equal one edits nothing. It works likeplaceholder.LexicalEditor.theme(_:)now sets it, and the privateapplyThemeis gone.controller.themestays for setting a theme before the first render, and its doc comment now points to the view's property.scalesWithDynamicTypeflag. On the iOS 27.2 Simulator, in a window pinned to the largest accessibility size (AX5), body text draws at 45 pt. WithLexicalEditor(editor).dynamicTypeSize(.large)it draws at 16 pt, and withtextView.traitOverrides.preferredContentSizeCategory = .largeon the UIKit view it also draws at 16 pt, whether the override is set before or after the view joins the window. So the platform's own opt-out reaches the editor's text. DESIGN.md's Dynamic Type entry and the view and modifier docs now name it, and thenative/dynamic-type-theme-scaledeviation stays as it is.Videos (iOS 27.2 Simulator, iPhone 17 Pro)
A UIKit
LexicalUITextViewgets a brand theme on a live view (19 pt navy body, pink heading, teal quote, all from dynamicUIColors). Then paragraph 2 is edited, and the system appearance switches to dark and back to light.controller.theme = brandwith each color resolved once. Nothing changes when the theme is set. Only paragraph 2 takes the theme once it is edited, and in dark it stays navy on black.textView.theme = brandwithTextKitColor(uiColor:)draws the whole document at once. In dark every color switches to its readable dark variant.Checks
swift format lint --strict, thenswift build --build-tests && swift test --skip-buildon Linux and macOS (Xcode 27.3): all green. The two known issues on macOS were already on main.pnpm --filter lexical-conformance run testandcheck-ledger: green.-only-testing:LexicalUIKitTests: 37 tests, all pass, including the newThemeTests.UIColortoTextKitColorin light and dark, plus.label; a live theme switch redraws (size and both appearances' colors), an equal theme edits no storage, and switching back works;LexicalEditor.themechanges the live view;.dynamicTypeSize(.large)andtraitOverrideskeep 16 pt under AX5.NSColortoTextKitColor, plus.textColor; a live theme switch and the equal-theme no-op.Performance (Mac satellite, main against branch, built the same way)
LexicalBenchmarks(release build, median of 3 runs each, alternating, 5,000 blocks): no change past noise. The views aren't in this benchmark, and the change moves no reconciler code.Theme switch and redraw through
LexicalEditor.theme(_:)(iOS 27.2 Simulator, Debug, 1,000 paragraphs, 30 switches, 2 runs each, alternating):The switch costs the same on both, because it is the same full render.
Not run locally: the iOS 26.5 Simulator (CI runs it on main after merge).
Changeset
minor(new public API), so it goes out in 0.2.0. It must not merge before Swift 0.1.10 is released.